SHARE
TWEET

Removing memory leak from ClosableThreadLocal

a guest Dec 18th, 2010 124 Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1.     /// <summary>
  2.     /// Added SlotsDictionary for prettier code
  3.     /// </summary>
  4.     public class SlotsDictionary : Dictionary<Guid, object> { }
  5.  
  6.  
  7.     public class CloseableThreadLocal
  8.     {
  9.         /// <summary>
  10.         /// Keep a thread global reference to each local thread store
  11.         /// </summary>
  12.         private static readonly HashSet<SlotsDictionary> AllSlots = new HashSet<SlotsDictionary>();
  13.  
  14.         [ThreadStatic]
  15.         private static SlotsDictionary slots;
  16.  
  17.         /// <summary>
  18.         /// Changed the key to something which does not keep a strong reference,
  19.         /// hence prohibit the finalizer from running
  20.         /// </summary>
  21.         private readonly Guid idenfitier = Guid.NewGuid();
  22.  
  23.         /// <summary>
  24.         /// Changed the code to always track local thread slot.
  25.         /// This will avoid a potential memory leak when Set is called on the
  26.         /// same thread after Close have removed the slots from AllSlots.
  27.         /// </summary>
  28.         public static Dictionary<Guid, object> Slots
  29.         {
  30.             get
  31.             {
  32.                 if (slots == null)
  33.                 {
  34.                     slots = new SlotsDictionary();
  35.                 }
  36.  
  37.                 AllSlots.Add(slots);
  38.                 return slots;
  39.             }
  40.         }
  41.  
  42.         public static int SlotsCount
  43.         {
  44.             get { return slots.Count; }
  45.         }
  46.  
  47.         public static int AllSlotsCount
  48.         {
  49.             get { return AllSlots.Count; }
  50.         }
  51.  
  52.         public /*protected internal*/ virtual Object InitialValue()
  53.         {
  54.             return null;
  55.         }
  56.  
  57.         public virtual Object Get()
  58.         {
  59.             object val;
  60.  
  61.             if (Slots.TryGetValue(idenfitier, out val))
  62.             {
  63.                 return val;
  64.             }
  65.             val = InitialValue();
  66.             Set(val);
  67.             return val;
  68.         }
  69.  
  70.         public virtual void Set(object val)
  71.         {
  72.             Slots[idenfitier] = val;
  73.         }
  74.  
  75.         /// <summary>
  76.         /// Changed the code to iterate all slots and remove from each.
  77.         /// </summary>
  78.         public virtual void Close()
  79.         {
  80.             foreach (var localSlots in AllSlots)
  81.             {
  82.                 localSlots.Remove(idenfitier);
  83.             }
  84.  
  85.             AllSlots.RemoveWhere(slots => slots.Count == 0);
  86.         }
  87.  
  88.         ~CloseableThreadLocal()
  89.         {
  90.             Close();
  91.         }
  92.     }
  93.  
  94.     [TestClass]
  95.     public class Remove_memory_leak_from_closable_thread_local
  96.     {
  97.         [TestMethod]
  98.         public void Clean_all_references()
  99.         {
  100.             UseThreadLocalAndGarbageCollect();
  101.             CloseableThreadLocal.SlotsCount.ShouldEqual(0);
  102.             CloseableThreadLocal.AllSlotsCount.ShouldEqual(0);
  103.         }
  104.  
  105.         [TestMethod]
  106.         public void Clean_all_references_when_reused_after_a_previous_clean()
  107.         {
  108.             UseThreadLocalAndGarbageCollect();
  109.             UseThreadLocalAndGarbageCollect();
  110.             CloseableThreadLocal.SlotsCount.ShouldEqual(0);
  111.             CloseableThreadLocal.AllSlotsCount.ShouldEqual(0);
  112.         }
  113.  
  114.         private static void UseThreadLocalAndGarbageCollect()
  115.         {
  116.             UseThreadLocal();
  117.             GC.Collect(2);
  118.             GC.WaitForPendingFinalizers();
  119.         }
  120.  
  121.         private static void UseThreadLocal()
  122.         {
  123.             var tl = new CloseableThreadLocal();
  124.             tl.Set("hello there");
  125.             tl.Get().ShouldEqual("hello there");
  126.         }
  127.     }
RAW Paste Data
We use cookies for various purposes including analytics. By continuing to use Pastebin, you agree to our use of cookies as described in the Cookies Policy. OK, I Understand
Not a member of Pastebin yet?
Sign Up, it unlocks many cool features!
 
Top