summaryrefslogtreecommitdiff
path: root/src/mscorlib/src/System/Threading/ThreadLocal.cs
diff options
context:
space:
mode:
Diffstat (limited to 'src/mscorlib/src/System/Threading/ThreadLocal.cs')
-rw-r--r--src/mscorlib/src/System/Threading/ThreadLocal.cs815
1 files changed, 815 insertions, 0 deletions
diff --git a/src/mscorlib/src/System/Threading/ThreadLocal.cs b/src/mscorlib/src/System/Threading/ThreadLocal.cs
new file mode 100644
index 0000000000..b4cf12ab7c
--- /dev/null
+++ b/src/mscorlib/src/System/Threading/ThreadLocal.cs
@@ -0,0 +1,815 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+#pragma warning disable 0420
+
+// =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
+//
+//
+//
+// A class that provides a simple, lightweight implementation of thread-local lazy-initialization, where a value is initialized once per accessing
+// thread; this provides an alternative to using a ThreadStatic static variable and having
+// to check the variable prior to every access to see if it's been initialized.
+//
+//
+//
+// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
+
+using System.Diagnostics;
+using System.Collections.Generic;
+using System.Security.Permissions;
+using System.Diagnostics.Contracts;
+
+namespace System.Threading
+{
+ /// <summary>
+ /// Provides thread-local storage of data.
+ /// </summary>
+ /// <typeparam name="T">Specifies the type of data stored per-thread.</typeparam>
+ /// <remarks>
+ /// <para>
+ /// With the exception of <see cref="Dispose()"/>, all public and protected members of
+ /// <see cref="ThreadLocal{T}"/> are thread-safe and may be used
+ /// concurrently from multiple threads.
+ /// </para>
+ /// </remarks>
+ [DebuggerTypeProxy(typeof(SystemThreading_ThreadLocalDebugView<>))]
+ [DebuggerDisplay("IsValueCreated={IsValueCreated}, Value={ValueForDebugDisplay}, Count={ValuesCountForDebugDisplay}")]
+ [HostProtection(Synchronization = true, ExternalThreading = true)]
+ public class ThreadLocal<T> : IDisposable
+ {
+
+ // a delegate that returns the created value, if null the created value will be default(T)
+ private Func<T> m_valueFactory;
+
+ //
+ // ts_slotArray is a table of thread-local values for all ThreadLocal<T> instances
+ //
+ // So, when a thread reads ts_slotArray, it gets back an array of *all* ThreadLocal<T> values for this thread and this T.
+ // The slot relevant to this particular ThreadLocal<T> instance is determined by the m_idComplement instance field stored in
+ // the ThreadLocal<T> instance.
+ //
+ [ThreadStatic]
+ static LinkedSlotVolatile[] ts_slotArray;
+
+ [ThreadStatic]
+ static FinalizationHelper ts_finalizationHelper;
+
+ // Slot ID of this ThreadLocal<> instance. We store a bitwise complement of the ID (that is ~ID), which allows us to distinguish
+ // between the case when ID is 0 and an incompletely initialized object, either due to a thread abort in the constructor, or
+ // possibly due to a memory model issue in user code.
+ private int m_idComplement;
+
+ // This field is set to true when the constructor completes. That is helpful for recognizing whether a constructor
+ // threw an exception - either due to invalid argument or due to a thread abort. Finally, the field is set to false
+ // when the instance is disposed.
+ private volatile bool m_initialized;
+
+ // IdManager assigns and reuses slot IDs. Additionally, the object is also used as a global lock.
+ private static IdManager s_idManager = new IdManager();
+
+ // A linked list of all values associated with this ThreadLocal<T> instance.
+ // We create a dummy head node. That allows us to remove any (non-dummy) node without having to locate the m_linkedSlot field.
+ private LinkedSlot m_linkedSlot = new LinkedSlot(null);
+
+ // Whether the Values property is supported
+ private bool m_trackAllValues;
+
+ /// <summary>
+ /// Initializes the <see cref="System.Threading.ThreadLocal{T}"/> instance.
+ /// </summary>
+ public ThreadLocal()
+ {
+ Initialize(null, false);
+ }
+
+ /// <summary>
+ /// Initializes the <see cref="System.Threading.ThreadLocal{T}"/> instance.
+ /// </summary>
+ /// <param name="trackAllValues">Whether to track all values set on the instance and expose them through the Values property.</param>
+ public ThreadLocal(bool trackAllValues)
+ {
+ Initialize(null, trackAllValues);
+ }
+
+
+ /// <summary>
+ /// Initializes the <see cref="System.Threading.ThreadLocal{T}"/> instance with the
+ /// specified <paramref name="valueFactory"/> function.
+ /// </summary>
+ /// <param name="valueFactory">
+ /// The <see cref="T:System.Func{T}"/> invoked to produce a lazily-initialized value when
+ /// an attempt is made to retrieve <see cref="Value"/> without it having been previously initialized.
+ /// </param>
+ /// <exception cref="T:System.ArgumentNullException">
+ /// <paramref name="valueFactory"/> is a null reference (Nothing in Visual Basic).
+ /// </exception>
+ public ThreadLocal(Func<T> valueFactory)
+ {
+ if (valueFactory == null)
+ throw new ArgumentNullException("valueFactory");
+
+ Initialize(valueFactory, false);
+ }
+
+ /// <summary>
+ /// Initializes the <see cref="System.Threading.ThreadLocal{T}"/> instance with the
+ /// specified <paramref name="valueFactory"/> function.
+ /// </summary>
+ /// <param name="valueFactory">
+ /// The <see cref="T:System.Func{T}"/> invoked to produce a lazily-initialized value when
+ /// an attempt is made to retrieve <see cref="Value"/> without it having been previously initialized.
+ /// </param>
+ /// <param name="trackAllValues">Whether to track all values set on the instance and expose them via the Values property.</param>
+ /// <exception cref="T:System.ArgumentNullException">
+ /// <paramref name="valueFactory"/> is a null reference (Nothing in Visual Basic).
+ /// </exception>
+ public ThreadLocal(Func<T> valueFactory, bool trackAllValues)
+ {
+ if (valueFactory == null)
+ throw new ArgumentNullException("valueFactory");
+
+ Initialize(valueFactory, trackAllValues);
+ }
+
+ private void Initialize(Func<T> valueFactory, bool trackAllValues)
+ {
+ m_valueFactory = valueFactory;
+ m_trackAllValues = trackAllValues;
+
+ // Assign the ID and mark the instance as initialized. To avoid leaking IDs, we assign the ID and set m_initialized
+ // in a finally block, to avoid a thread abort in between the two statements.
+ try { }
+ finally
+ {
+ m_idComplement = ~s_idManager.GetId();
+
+ // As the last step, mark the instance as fully initialized. (Otherwise, if m_initialized=false, we know that an exception
+ // occurred in the constructor.)
+ m_initialized = true;
+ }
+ }
+
+ /// <summary>
+ /// Releases the resources used by this <see cref="T:System.Threading.ThreadLocal{T}" /> instance.
+ /// </summary>
+ ~ThreadLocal()
+ {
+ // finalizer to return the type combination index to the pool
+ Dispose(false);
+ }
+
+ #region IDisposable Members
+
+ /// <summary>
+ /// Releases the resources used by this <see cref="T:System.Threading.ThreadLocal{T}" /> instance.
+ /// </summary>
+ /// <remarks>
+ /// Unlike most of the members of <see cref="T:System.Threading.ThreadLocal{T}"/>, this method is not thread-safe.
+ /// </remarks>
+ public void Dispose()
+ {
+ Dispose(true);
+ GC.SuppressFinalize(this);
+ }
+
+ /// <summary>
+ /// Releases the resources used by this <see cref="T:System.Threading.ThreadLocal{T}" /> instance.
+ /// </summary>
+ /// <param name="disposing">
+ /// A Boolean value that indicates whether this method is being called due to a call to <see cref="Dispose()"/>.
+ /// </param>
+ /// <remarks>
+ /// Unlike most of the members of <see cref="T:System.Threading.ThreadLocal{T}"/>, this method is not thread-safe.
+ /// </remarks>
+ protected virtual void Dispose(bool disposing)
+ {
+ int id;
+
+ lock (s_idManager)
+ {
+ id = ~m_idComplement;
+ m_idComplement = 0;
+
+ if (id < 0 || !m_initialized)
+ {
+ Contract.Assert(id >= 0 || !m_initialized, "expected id >= 0 if initialized");
+
+ // Handle double Dispose calls or disposal of an instance whose constructor threw an exception.
+ return;
+ }
+ m_initialized = false;
+
+ for (LinkedSlot linkedSlot = m_linkedSlot.Next; linkedSlot != null; linkedSlot = linkedSlot.Next)
+ {
+ LinkedSlotVolatile[] slotArray = linkedSlot.SlotArray;
+
+ if (slotArray == null)
+ {
+ // The thread that owns this slotArray has already finished.
+ continue;
+ }
+
+ // Remove the reference from the LinkedSlot to the slot table.
+ linkedSlot.SlotArray = null;
+
+ // And clear the references from the slot table to the linked slot and the value so that
+ // both can get garbage collected.
+ slotArray[id].Value.Value = default(T);
+ slotArray[id].Value = null;
+ }
+ }
+ m_linkedSlot = null;
+ s_idManager.ReturnId(id);
+ }
+
+ #endregion
+
+ /// <summary>Creates and returns a string representation of this instance for the current thread.</summary>
+ /// <returns>The result of calling <see cref="System.Object.ToString"/> on the <see cref="Value"/>.</returns>
+ /// <exception cref="T:System.NullReferenceException">
+ /// The <see cref="Value"/> for the current thread is a null reference (Nothing in Visual Basic).
+ /// </exception>
+ /// <exception cref="T:System.InvalidOperationException">
+ /// The initialization function referenced <see cref="Value"/> in an improper manner.
+ /// </exception>
+ /// <exception cref="T:System.ObjectDisposedException">
+ /// The <see cref="ThreadLocal{T}"/> instance has been disposed.
+ /// </exception>
+ /// <remarks>
+ /// Calling this method forces initialization for the current thread, as is the
+ /// case with accessing <see cref="Value"/> directly.
+ /// </remarks>
+ public override string ToString()
+ {
+ return Value.ToString();
+ }
+
+ /// <summary>
+ /// Gets or sets the value of this instance for the current thread.
+ /// </summary>
+ /// <exception cref="T:System.InvalidOperationException">
+ /// The initialization function referenced <see cref="Value"/> in an improper manner.
+ /// </exception>
+ /// <exception cref="T:System.ObjectDisposedException">
+ /// The <see cref="ThreadLocal{T}"/> instance has been disposed.
+ /// </exception>
+ /// <remarks>
+ /// If this instance was not previously initialized for the current thread,
+ /// accessing <see cref="Value"/> will attempt to initialize it. If an initialization function was
+ /// supplied during the construction, that initialization will happen by invoking the function
+ /// to retrieve the initial value for <see cref="Value"/>. Otherwise, the default value of
+ /// <typeparamref name="T"/> will be used.
+ /// </remarks>
+ [DebuggerBrowsable(DebuggerBrowsableState.Never)]
+ public T Value
+ {
+ get
+ {
+ LinkedSlotVolatile[] slotArray = ts_slotArray;
+ LinkedSlot slot;
+ int id = ~m_idComplement;
+
+ //
+ // Attempt to get the value using the fast path
+ //
+ if (slotArray != null // Has the slot array been initialized?
+ && id >= 0 // Is the ID non-negative (i.e., instance is not disposed)?
+ && id < slotArray.Length // Is the table large enough?
+ && (slot = slotArray[id].Value) != null // Has a LinkedSlot object has been allocated for this ID?
+ && m_initialized // Has the instance *still* not been disposed (important for a race condition with Dispose)?
+ )
+ {
+ // We verified that the instance has not been disposed *after* we got a reference to the slot.
+ // This guarantees that we have a reference to the right slot.
+ //
+ // Volatile read of the LinkedSlotVolatile.Value property ensures that the m_initialized read
+ // will not be reordered before the read of slotArray[id].
+ return slot.Value;
+ }
+
+ return GetValueSlow();
+ }
+ set
+ {
+ LinkedSlotVolatile[] slotArray = ts_slotArray;
+ LinkedSlot slot;
+ int id = ~m_idComplement;
+
+ //
+ // Attempt to set the value using the fast path
+ //
+ if (slotArray != null // Has the slot array been initialized?
+ && id >= 0 // Is the ID non-negative (i.e., instance is not disposed)?
+ && id < slotArray.Length // Is the table large enough?
+ && (slot = slotArray[id].Value) != null // Has a LinkedSlot object has been allocated for this ID?
+ && m_initialized // Has the instance *still* not been disposed (important for a race condition with Dispose)?
+ )
+ {
+ // We verified that the instance has not been disposed *after* we got a reference to the slot.
+ // This guarantees that we have a reference to the right slot.
+ //
+ // Volatile read of the LinkedSlotVolatile.Value property ensures that the m_initialized read
+ // will not be reordered before the read of slotArray[id].
+ slot.Value = value;
+ }
+ else
+ {
+ SetValueSlow(value, slotArray);
+ }
+ }
+ }
+
+ private T GetValueSlow()
+ {
+ // If the object has been disposed, the id will be -1.
+ int id = ~m_idComplement;
+ if (id < 0)
+ {
+ throw new ObjectDisposedException(Environment.GetResourceString("ThreadLocal_Disposed"));
+ }
+
+ Debugger.NotifyOfCrossThreadDependency();
+
+ // Determine the initial value
+ T value;
+ if (m_valueFactory == null)
+ {
+ value = default(T);
+ }
+ else
+ {
+ value = m_valueFactory();
+
+ if (IsValueCreated)
+ {
+ throw new InvalidOperationException(Environment.GetResourceString("ThreadLocal_Value_RecursiveCallsToValue"));
+ }
+ }
+
+ // Since the value has been previously uninitialized, we also need to set it (according to the ThreadLocal semantics).
+ Value = value;
+ return value;
+ }
+
+ private void SetValueSlow(T value, LinkedSlotVolatile[] slotArray)
+ {
+ int id = ~m_idComplement;
+
+ // If the object has been disposed, id will be -1.
+ if (id < 0)
+ {
+ throw new ObjectDisposedException(Environment.GetResourceString("ThreadLocal_Disposed"));
+ }
+
+ // If a slot array has not been created on this thread yet, create it.
+ if (slotArray == null)
+ {
+ slotArray = new LinkedSlotVolatile[GetNewTableSize(id + 1)];
+ ts_finalizationHelper = new FinalizationHelper(slotArray, m_trackAllValues);
+ ts_slotArray = slotArray;
+ }
+
+ // If the slot array is not big enough to hold this ID, increase the table size.
+ if (id >= slotArray.Length)
+ {
+ GrowTable(ref slotArray, id + 1);
+ ts_finalizationHelper.SlotArray = slotArray;
+ ts_slotArray = slotArray;
+ }
+
+ // If we are using the slot in this table for the first time, create a new LinkedSlot and add it into
+ // the linked list for this ThreadLocal instance.
+ if (slotArray[id].Value == null)
+ {
+ CreateLinkedSlot(slotArray, id, value);
+ }
+ else
+ {
+ // Volatile read of the LinkedSlotVolatile.Value property ensures that the m_initialized read
+ // that follows will not be reordered before the read of slotArray[id].
+ LinkedSlot slot = slotArray[id].Value;
+
+ // It is important to verify that the ThreadLocal instance has not been disposed. The check must come
+ // after capturing slotArray[id], but before assigning the value into the slot. This ensures that
+ // if this ThreadLocal instance was disposed on another thread and another ThreadLocal instance was
+ // created, we definitely won't assign the value into the wrong instance.
+
+ if (!m_initialized)
+ {
+ throw new ObjectDisposedException(Environment.GetResourceString("ThreadLocal_Disposed"));
+ }
+
+ slot.Value = value;
+ }
+ }
+
+ /// <summary>
+ /// Creates a LinkedSlot and inserts it into the linked list for this ThreadLocal instance.
+ /// </summary>
+ private void CreateLinkedSlot(LinkedSlotVolatile[] slotArray, int id, T value)
+ {
+ // Create a LinkedSlot
+ var linkedSlot = new LinkedSlot(slotArray);
+
+ // Insert the LinkedSlot into the linked list maintained by this ThreadLocal<> instance and into the slot array
+ lock (s_idManager)
+ {
+ // Check that the instance has not been disposed. It is important to check this under a lock, since
+ // Dispose also executes under a lock.
+ if (!m_initialized)
+ {
+ throw new ObjectDisposedException(Environment.GetResourceString("ThreadLocal_Disposed"));
+ }
+
+ LinkedSlot firstRealNode = m_linkedSlot.Next;
+
+ //
+ // Insert linkedSlot between nodes m_linkedSlot and firstRealNode.
+ // (m_linkedSlot is the dummy head node that should always be in the front.)
+ //
+ linkedSlot.Next = firstRealNode;
+ linkedSlot.Previous = m_linkedSlot;
+ linkedSlot.Value = value;
+
+ if (firstRealNode != null)
+ {
+ firstRealNode.Previous = linkedSlot;
+ }
+ m_linkedSlot.Next = linkedSlot;
+
+ // Assigning the slot under a lock prevents a race condition with Dispose (dispose also acquires the lock).
+ // Otherwise, it would be possible that the ThreadLocal instance is disposed, another one gets created
+ // with the same ID, and the write would go to the wrong instance.
+ slotArray[id].Value = linkedSlot;
+ }
+ }
+
+ /// <summary>
+ /// Gets a list for all of the values currently stored by all of the threads that have accessed this instance.
+ /// </summary>
+ /// <exception cref="T:System.ObjectDisposedException">
+ /// The <see cref="ThreadLocal{T}"/> instance has been disposed.
+ /// </exception>
+ public IList<T> Values
+ {
+ get
+ {
+ if (!m_trackAllValues)
+ {
+ throw new InvalidOperationException(Environment.GetResourceString("ThreadLocal_ValuesNotAvailable"));
+ }
+
+ var list = GetValuesAsList(); // returns null if disposed
+ if (list == null) throw new ObjectDisposedException(Environment.GetResourceString("ThreadLocal_Disposed"));
+ return list;
+ }
+ }
+
+ /// <summary>Gets all of the threads' values in a list.</summary>
+ private List<T> GetValuesAsList()
+ {
+ List<T> valueList = new List<T>();
+ int id = ~m_idComplement;
+ if (id == -1)
+ {
+ return null;
+ }
+
+ // Walk over the linked list of slots and gather the values associated with this ThreadLocal instance.
+ for (LinkedSlot linkedSlot = m_linkedSlot.Next; linkedSlot != null; linkedSlot = linkedSlot.Next)
+ {
+ // We can safely read linkedSlot.Value. Even if this ThreadLocal has been disposed in the meantime, the LinkedSlot
+ // objects will never be assigned to another ThreadLocal instance.
+ valueList.Add(linkedSlot.Value);
+ }
+
+ return valueList;
+ }
+
+ /// <summary>Gets the number of threads that have data in this instance.</summary>
+ private int ValuesCountForDebugDisplay
+ {
+ get
+ {
+ int count = 0;
+ for (LinkedSlot linkedSlot = m_linkedSlot.Next; linkedSlot != null; linkedSlot = linkedSlot.Next)
+ {
+ count++;
+ }
+ return count;
+ }
+ }
+
+ /// <summary>
+ /// Gets whether <see cref="Value"/> is initialized on the current thread.
+ /// </summary>
+ /// <exception cref="T:System.ObjectDisposedException">
+ /// The <see cref="ThreadLocal{T}"/> instance has been disposed.
+ /// </exception>
+ public bool IsValueCreated
+ {
+ get
+ {
+ int id = ~m_idComplement;
+ if (id < 0)
+ {
+ throw new ObjectDisposedException(Environment.GetResourceString("ThreadLocal_Disposed"));
+ }
+
+ LinkedSlotVolatile[] slotArray = ts_slotArray;
+ return slotArray != null && id < slotArray.Length && slotArray[id].Value != null;
+ }
+ }
+
+
+ /// <summary>Gets the value of the ThreadLocal&lt;T&gt; for debugging display purposes. It takes care of getting
+ /// the value for the current thread in the ThreadLocal mode.</summary>
+ internal T ValueForDebugDisplay
+ {
+ get
+ {
+ LinkedSlotVolatile[] slotArray = ts_slotArray;
+ int id = ~m_idComplement;
+
+ LinkedSlot slot;
+ if (slotArray == null || id >= slotArray.Length || (slot = slotArray[id].Value) == null || !m_initialized)
+ return default(T);
+ return slot.Value;
+ }
+ }
+
+ /// <summary>Gets the values of all threads that accessed the ThreadLocal&lt;T&gt;.</summary>
+ internal List<T> ValuesForDebugDisplay // same as Values property, but doesn't throw if disposed
+ {
+ get { return GetValuesAsList(); }
+ }
+
+ /// <summary>
+ /// Resizes a table to a certain length (or larger).
+ /// </summary>
+ private void GrowTable(ref LinkedSlotVolatile[] table, int minLength)
+ {
+ Contract.Assert(table.Length < minLength);
+
+ // Determine the size of the new table and allocate it.
+ int newLen = GetNewTableSize(minLength);
+ LinkedSlotVolatile[] newTable = new LinkedSlotVolatile[newLen];
+
+ //
+ // The lock is necessary to avoid a race with ThreadLocal.Dispose. GrowTable has to point all
+ // LinkedSlot instances referenced in the old table to reference the new table. Without locking,
+ // Dispose could use a stale SlotArray reference and clear out a slot in the old array only, while
+ // the value continues to be referenced from the new (larger) array.
+ //
+ lock (s_idManager)
+ {
+ for (int i = 0; i < table.Length; i++)
+ {
+ LinkedSlot linkedSlot = table[i].Value;
+ if (linkedSlot != null && linkedSlot.SlotArray != null)
+ {
+ linkedSlot.SlotArray = newTable;
+ newTable[i] = table[i];
+ }
+ }
+ }
+
+ table = newTable;
+ }
+
+ /// <summary>
+ /// Chooses the next larger table size
+ /// </summary>
+ private static int GetNewTableSize(int minSize)
+ {
+ if ((uint)minSize > Array.MaxArrayLength)
+ {
+ // Intentionally return a value that will result in an OutOfMemoryException
+ return int.MaxValue;
+ }
+ Contract.Assert(minSize > 0);
+
+ //
+ // Round up the size to the next power of 2
+ //
+ // The algorithm takes three steps:
+ // input -> subtract one -> propagate 1-bits to the right -> add one
+ //
+ // Let's take a look at the 3 steps in both interesting cases: where the input
+ // is (Example 1) and isn't (Example 2) a power of 2.
+ //
+ // Example 1: 100000 -> 011111 -> 011111 -> 100000
+ // Example 2: 011010 -> 011001 -> 011111 -> 100000
+ //
+ int newSize = minSize;
+
+ // Step 1: Decrement
+ newSize--;
+
+ // Step 2: Propagate 1-bits to the right.
+ newSize |= newSize >> 1;
+ newSize |= newSize >> 2;
+ newSize |= newSize >> 4;
+ newSize |= newSize >> 8;
+ newSize |= newSize >> 16;
+
+ // Step 3: Increment
+ newSize++;
+
+ // Don't set newSize to more than Array.MaxArrayLength
+ if ((uint)newSize > Array.MaxArrayLength)
+ {
+ newSize = Array.MaxArrayLength;
+ }
+
+ return newSize;
+ }
+
+ /// <summary>
+ /// A wrapper struct used as LinkedSlotVolatile[] - an array of LinkedSlot instances, but with volatile semantics
+ /// on array accesses.
+ /// </summary>
+ private struct LinkedSlotVolatile
+ {
+ internal volatile LinkedSlot Value;
+ }
+
+ /// <summary>
+ /// A node in the doubly-linked list stored in the ThreadLocal instance.
+ ///
+ /// The value is stored in one of two places:
+ ///
+ /// 1. If SlotArray is not null, the value is in SlotArray.Table[id]
+ /// 2. If SlotArray is null, the value is in FinalValue.
+ /// </summary>
+ private sealed class LinkedSlot
+ {
+ internal LinkedSlot(LinkedSlotVolatile[] slotArray)
+ {
+ SlotArray = slotArray;
+ }
+
+ // The next LinkedSlot for this ThreadLocal<> instance
+ internal volatile LinkedSlot Next;
+
+ // The previous LinkedSlot for this ThreadLocal<> instance
+ internal volatile LinkedSlot Previous;
+
+ // The SlotArray that stores this LinkedSlot at SlotArray.Table[id].
+ internal volatile LinkedSlotVolatile[] SlotArray;
+
+ // The value for this slot.
+ internal T Value;
+ }
+
+ /// <summary>
+ /// A manager class that assigns IDs to ThreadLocal instances
+ /// </summary>
+ private class IdManager
+ {
+ // The next ID to try
+ private int m_nextIdToTry = 0;
+
+ // Stores whether each ID is free or not. Additionally, the object is also used as a lock for the IdManager.
+ private List<bool> m_freeIds = new List<bool>();
+
+ internal int GetId()
+ {
+ lock (m_freeIds)
+ {
+ int availableId = m_nextIdToTry;
+ while (availableId < m_freeIds.Count)
+ {
+ if (m_freeIds[availableId]) { break; }
+ availableId++;
+ }
+
+ if (availableId == m_freeIds.Count)
+ {
+ m_freeIds.Add(false);
+ }
+ else
+ {
+ m_freeIds[availableId] = false;
+ }
+
+ m_nextIdToTry = availableId + 1;
+
+ return availableId;
+ }
+ }
+
+ // Return an ID to the pool
+ internal void ReturnId(int id)
+ {
+ lock (m_freeIds)
+ {
+ m_freeIds[id] = true;
+ if (id < m_nextIdToTry) m_nextIdToTry = id;
+ }
+ }
+ }
+
+ /// <summary>
+ /// A class that facilitates ThreadLocal cleanup after a thread exits.
+ ///
+ /// After a thread with an associated thread-local table has exited, the FinalizationHelper
+ /// is reponsible for removing back-references to the table. Since an instance of FinalizationHelper
+ /// is only referenced from a single thread-local slot, the FinalizationHelper will be GC'd once
+ /// the thread has exited.
+ ///
+ /// The FinalizationHelper then locates all LinkedSlot instances with back-references to the table
+ /// (all those LinkedSlot instances can be found by following references from the table slots) and
+ /// releases the table so that it can get GC'd.
+ /// </summary>
+ private class FinalizationHelper
+ {
+ internal LinkedSlotVolatile[] SlotArray;
+ private bool m_trackAllValues;
+
+ internal FinalizationHelper(LinkedSlotVolatile[] slotArray, bool trackAllValues)
+ {
+ SlotArray = slotArray;
+ m_trackAllValues = trackAllValues;
+ }
+
+ ~FinalizationHelper()
+ {
+ LinkedSlotVolatile[] slotArray = SlotArray;
+ Contract.Assert(slotArray != null);
+
+ for (int i = 0; i < slotArray.Length; i++)
+ {
+ LinkedSlot linkedSlot = slotArray[i].Value;
+ if (linkedSlot == null)
+ {
+ // This slot in the table is empty
+ continue;
+ }
+
+ if (m_trackAllValues)
+ {
+ // Set the SlotArray field to null to release the slot array.
+ linkedSlot.SlotArray = null;
+ }
+ else
+ {
+ // Remove the LinkedSlot from the linked list. Once the FinalizationHelper is done, all back-references to
+ // the table will be have been removed, and so the table can get GC'd.
+ lock (s_idManager)
+ {
+ if (linkedSlot.Next != null)
+ {
+ linkedSlot.Next.Previous = linkedSlot.Previous;
+ }
+
+ // Since the list uses a dummy head node, the Previous reference should never be null.
+ Contract.Assert(linkedSlot.Previous != null);
+ linkedSlot.Previous.Next = linkedSlot.Next;
+ }
+ }
+ }
+ }
+ }
+ }
+
+ /// <summary>A debugger view of the ThreadLocal&lt;T&gt; to surface additional debugging properties and
+ /// to ensure that the ThreadLocal&lt;T&gt; does not become initialized if it was not already.</summary>
+ internal sealed class SystemThreading_ThreadLocalDebugView<T>
+ {
+ //The ThreadLocal object being viewed.
+ private readonly ThreadLocal<T> m_tlocal;
+
+ /// <summary>Constructs a new debugger view object for the provided ThreadLocal object.</summary>
+ /// <param name="tlocal">A ThreadLocal object to browse in the debugger.</param>
+ public SystemThreading_ThreadLocalDebugView(ThreadLocal<T> tlocal)
+ {
+ m_tlocal = tlocal;
+ }
+
+ /// <summary>Returns whether the ThreadLocal object is initialized or not.</summary>
+ public bool IsValueCreated
+ {
+ get { return m_tlocal.IsValueCreated; }
+ }
+
+ /// <summary>Returns the value of the ThreadLocal object.</summary>
+ public T Value
+ {
+ get
+ {
+ return m_tlocal.ValueForDebugDisplay;
+ }
+ }
+
+ /// <summary>Return all values for all threads that have accessed this instance.</summary>
+ public List<T> Values
+ {
+ get
+ {
+ return m_tlocal.ValuesForDebugDisplay;
+ }
+ }
+ }
+}