Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- /// <summary>
- /// Added SlotsDictionary for prettier code
- /// </summary>
- public class SlotsDictionary : Dictionary<Guid, object> { }
- public class CloseableThreadLocal
- {
- /// <summary>
- /// Keep a thread global reference to each local thread store
- /// </summary>
- private static readonly HashSet<SlotsDictionary> AllSlots = new HashSet<SlotsDictionary>();
- [ThreadStatic]
- private static SlotsDictionary slots;
- /// <summary>
- /// Changed the key to something which does not keep a strong reference,
- /// hence prohibit the finalizer from running
- /// </summary>
- private readonly Guid idenfitier = Guid.NewGuid();
- /// <summary>
- /// Changed the code to always track local thread slot.
- /// This will avoid a potential memory leak when Set is called on the
- /// same thread after Close have removed the slots from AllSlots.
- /// </summary>
- public static Dictionary<Guid, object> Slots
- {
- get
- {
- if (slots == null)
- {
- slots = new SlotsDictionary();
- }
- AllSlots.Add(slots);
- return slots;
- }
- }
- public static int SlotsCount
- {
- get { return slots.Count; }
- }
- public static int AllSlotsCount
- {
- get { return AllSlots.Count; }
- }
- public /*protected internal*/ virtual Object InitialValue()
- {
- return null;
- }
- public virtual Object Get()
- {
- object val;
- if (Slots.TryGetValue(idenfitier, out val))
- {
- return val;
- }
- val = InitialValue();
- Set(val);
- return val;
- }
- public virtual void Set(object val)
- {
- Slots[idenfitier] = val;
- }
- /// <summary>
- /// Changed the code to iterate all slots and remove from each.
- /// </summary>
- public virtual void Close()
- {
- foreach (var localSlots in AllSlots)
- {
- localSlots.Remove(idenfitier);
- }
- AllSlots.RemoveWhere(slots => slots.Count == 0);
- }
- ~CloseableThreadLocal()
- {
- Close();
- }
- }
- [TestClass]
- public class Remove_memory_leak_from_closable_thread_local
- {
- [TestMethod]
- public void Clean_all_references()
- {
- UseThreadLocalAndGarbageCollect();
- CloseableThreadLocal.SlotsCount.ShouldEqual(0);
- CloseableThreadLocal.AllSlotsCount.ShouldEqual(0);
- }
- [TestMethod]
- public void Clean_all_references_when_reused_after_a_previous_clean()
- {
- UseThreadLocalAndGarbageCollect();
- UseThreadLocalAndGarbageCollect();
- CloseableThreadLocal.SlotsCount.ShouldEqual(0);
- CloseableThreadLocal.AllSlotsCount.ShouldEqual(0);
- }
- private static void UseThreadLocalAndGarbageCollect()
- {
- UseThreadLocal();
- GC.Collect(2);
- GC.WaitForPendingFinalizers();
- }
- private static void UseThreadLocal()
- {
- var tl = new CloseableThreadLocal();
- tl.Set("hello there");
- tl.Get().ShouldEqual("hello there");
- }
- }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement