/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ using System; using System.Collections.Generic; using System.Linq; using System.Threading; using Lucene.Net.Support; #if NET35 using Lucene.Net.Support.Compatibility; #endif namespace Lucene.Net.Util { /// Java's builtin ThreadLocal has a serious flaw: /// it can take an arbitrarily long amount of time to /// dereference the things you had stored in it, even once the /// ThreadLocal instance itself is no longer referenced. /// This is because there is single, master map stored for /// each thread, which all ThreadLocals share, and that /// master map only periodically purges "stale" entries. /// /// While not technically a memory leak, because eventually /// the memory will be reclaimed, it can take a long time /// and you can easily hit OutOfMemoryError because from the /// GC's standpoint the stale entries are not reclaimaible. /// /// This class works around that, by only enrolling /// WeakReference values into the ThreadLocal, and /// separately holding a hard reference to each stored /// value. When you call , these hard /// references are cleared and then GC is freely able to /// reclaim space by objects stored in it. /// /// public class CloseableThreadLocal : IDisposable where T : class { // NOTE: Java has WeakReference. This isn't available for .Net until 4.5 (according to msdn docs) private ThreadLocal t = new ThreadLocal(); private IDictionary hardRefs = new HashMap(); private bool isDisposed; public virtual T InitialValue() { return null; } public virtual T Get() { WeakReference weakRef = t.Get(); if (weakRef == null) { T iv = InitialValue(); if (iv != null) { Set(iv); return iv; } else return null; } else { return (T)weakRef.Get(); } } public virtual void Set(T @object) { //+-- For Debuging if (CloseableThreadLocalProfiler.EnableCloseableThreadLocalProfiler == true) { lock (CloseableThreadLocalProfiler.Instances) { CloseableThreadLocalProfiler.Instances.Add(new WeakReference(@object)); } } //+-- t.Set(new WeakReference(@object)); lock (hardRefs) { //hardRefs[Thread.CurrentThread] = @object; hardRefs.Add(Thread.CurrentThread, @object); // Java's iterator can remove, .NET's cannot var threadsToRemove = hardRefs.Keys.Where(thread => !thread.IsAlive).ToList(); // Purge dead threads foreach (var thread in threadsToRemove) { hardRefs.Remove(thread); } } } [Obsolete("Use Dispose() instead")] public virtual void Close() { Dispose(); } public void Dispose() { Dispose(true); } protected virtual void Dispose(bool disposing) { if (isDisposed) return; if (disposing) { // Clear the hard refs; then, the only remaining refs to // all values we were storing are weak (unless somewhere // else is still using them) and so GC may reclaim them: hardRefs = null; // Take care of the current thread right now; others will be // taken care of via the WeakReferences. if (t != null) { t.Remove(); } t = null; } isDisposed = true; } } internal static class CloseableThreadLocalExtensions { public static void Set(this ThreadLocal t, T val) { t.Value = val; } public static T Get(this ThreadLocal t) { return t.Value; } public static void Remove(this ThreadLocal t) { t.Dispose(); } public static object Get(this WeakReference w) { return w.Target; } } //// {{DIGY}} //// To compile against Framework 2.0 //// Uncomment below class //public class ThreadLocal : IDisposable //{ // [ThreadStatic] // static SupportClass.WeakHashTable slots; // void Init() // { // if (slots == null) slots = new SupportClass.WeakHashTable(); // } // public T Value // { // set // { // Init(); // slots.Add(this, value); // } // get // { // Init(); // return (T)slots[this]; // } // } // public void Dispose() // { // if (slots != null) slots.Remove(this); // } //} }