/* * 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.Threading; 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 {@link #close}, these hard /// references are cleared and then GC is freely able to /// reclaim space by objects stored in it. /// /// public class CloseableThreadLocal where T : class { private ThreadLocal t = new ThreadLocal(); private Dictionary hardRefs = new Dictionary(); 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 (Lucene.Net.Support.CloseableThreadLocalProfiler.EnableCloseableThreadLocalProfiler == true) { lock (Lucene.Net.Support.CloseableThreadLocalProfiler.Instances) { Lucene.Net.Support.CloseableThreadLocalProfiler.Instances.Add(new WeakReference(@object)); } } //+-- t.Set(new WeakReference(@object)); lock (hardRefs) { //hardRefs[Thread.CurrentThread] = @object; hardRefs.Add(Thread.CurrentThread,@object); // Purge dead threads foreach (var thread in new List(hardRefs.Keys)) { if (!thread.IsAlive) hardRefs.Remove(thread); } } } public virtual void Close() { // 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; } } 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}} // This causes security exception in Medium Trust // TestCase: Lucene.Net.Support.TestMediumTrust.TestIndexAndSearch //class WeakReference : WeakReference where T : class //{ // public WeakReference(T target) : base(target) // { // } // public WeakReference(T target, bool trackResurrection) : base(target, trackResurrection) // { // } // public new T Target // { // get { return (T)base.Target; } // } // public T Get() // { // return (T)base.Target; // } //} }