Lucene.Net  3.0.3
Lucene.Net is a port of the Lucene search engine library, written in C# and targeted at .NET runtime users.
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Properties Pages
CloseableThreadLocal.cs
Go to the documentation of this file.
1 /*
2  * Licensed to the Apache Software Foundation (ASF) under one or more
3  * contributor license agreements. See the NOTICE file distributed with
4  * this work for additional information regarding copyright ownership.
5  * The ASF licenses this file to You under the Apache License, Version 2.0
6  * (the "License"); you may not use this file except in compliance with
7  * the License. You may obtain a copy of the License at
8  *
9  * http://www.apache.org/licenses/LICENSE-2.0
10  *
11  * Unless required by applicable law or agreed to in writing, software
12  * distributed under the License is distributed on an "AS IS" BASIS,
13  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  * See the License for the specific language governing permissions and
15  * limitations under the License.
16  */
17 
18 using System;
19 using System.Collections.Generic;
20 using System.Linq;
21 using System.Threading;
22 using Lucene.Net.Support;
23 
24 #if NET35
25 using Lucene.Net.Support.Compatibility;
26 #endif
27 
28 namespace Lucene.Net.Util
29 {
30 
31  /// <summary>Java's builtin ThreadLocal has a serious flaw:
32  /// it can take an arbitrarily long amount of time to
33  /// dereference the things you had stored in it, even once the
34  /// ThreadLocal instance itself is no longer referenced.
35  /// This is because there is single, master map stored for
36  /// each thread, which all ThreadLocals share, and that
37  /// master map only periodically purges "stale" entries.
38  ///
39  /// While not technically a memory leak, because eventually
40  /// the memory will be reclaimed, it can take a long time
41  /// and you can easily hit OutOfMemoryError because from the
42  /// GC's standpoint the stale entries are not reclaimaible.
43  ///
44  /// This class works around that, by only enrolling
45  /// WeakReference values into the ThreadLocal, and
46  /// separately holding a hard reference to each stored
47  /// value. When you call <see cref="Close" />, these hard
48  /// references are cleared and then GC is freely able to
49  /// reclaim space by objects stored in it.
50  /// </summary>
51  ///
52 
53  public class CloseableThreadLocal<T> : IDisposable where T : class
54  {
55  // NOTE: Java has WeakReference<T>. This isn't available for .Net until 4.5 (according to msdn docs)
56  private ThreadLocal<WeakReference> t = new ThreadLocal<WeakReference>();
57 
58  private IDictionary<Thread, T> hardRefs = new HashMap<Thread, T>();
59 
60  private bool isDisposed;
61 
62  public virtual T InitialValue()
63  {
64  return null;
65  }
66 
67  public virtual T Get()
68  {
69  WeakReference weakRef = t.Get();
70  if (weakRef == null)
71  {
72  T iv = InitialValue();
73  if (iv != null)
74  {
75  Set(iv);
76  return iv;
77  }
78  else
79  return null;
80  }
81  else
82  {
83  return (T)weakRef.Get();
84  }
85  }
86 
87  public virtual void Set(T @object)
88  {
89  //+-- For Debuging
91  {
93  {
94  CloseableThreadLocalProfiler.Instances.Add(new WeakReference(@object));
95  }
96  }
97  //+--
98 
99  t.Set(new WeakReference(@object));
100 
101  lock (hardRefs)
102  {
103  //hardRefs[Thread.CurrentThread] = @object;
104  hardRefs.Add(Thread.CurrentThread, @object);
105 
106  // Java's iterator can remove, .NET's cannot
107  var threadsToRemove = hardRefs.Keys.Where(thread => !thread.IsAlive).ToList();
108  // Purge dead threads
109  foreach (var thread in threadsToRemove)
110  {
111  hardRefs.Remove(thread);
112  }
113  }
114  }
115 
116  [Obsolete("Use Dispose() instead")]
117  public virtual void Close()
118  {
119  Dispose();
120  }
121 
122  public void Dispose()
123  {
124  Dispose(true);
125  }
126 
127  protected virtual void Dispose(bool disposing)
128  {
129  if (isDisposed) return;
130 
131  if (disposing)
132  {
133  // Clear the hard refs; then, the only remaining refs to
134  // all values we were storing are weak (unless somewhere
135  // else is still using them) and so GC may reclaim them:
136  hardRefs = null;
137  // Take care of the current thread right now; others will be
138  // taken care of via the WeakReferences.
139  if (t != null)
140  {
141  t.Remove();
142  }
143  t = null;
144  }
145 
146  isDisposed = true;
147  }
148  }
149 
150  internal static class CloseableThreadLocalExtensions
151  {
152  public static void Set<T>(this ThreadLocal<T> t, T val)
153  {
154  t.Value = val;
155  }
156 
157  public static T Get<T>(this ThreadLocal<T> t)
158  {
159  return t.Value;
160  }
161 
162  public static void Remove<T>(this ThreadLocal<T> t)
163  {
164  t.Dispose();
165  }
166 
167  public static object Get(this WeakReference w)
168  {
169  return w.Target;
170  }
171  }
172 
173  //// {{DIGY}}
174  //// To compile against Framework 2.0
175  //// Uncomment below class
176  //public class ThreadLocal<T> : IDisposable
177  //{
178  // [ThreadStatic]
179  // static SupportClass.WeakHashTable slots;
180 
181  // void Init()
182  // {
183  // if (slots == null) slots = new SupportClass.WeakHashTable();
184  // }
185 
186  // public T Value
187  // {
188  // set
189  // {
190  // Init();
191  // slots.Add(this, value);
192  // }
193  // get
194  // {
195  // Init();
196  // return (T)slots[this];
197  // }
198  // }
199 
200  // public void Dispose()
201  // {
202  // if (slots != null) slots.Remove(this);
203  // }
204  //}
205 }