diff options
author | Anipik <anirudhagnihotry098@gmail.com> | 2018-08-11 02:32:09 +0300 |
---|---|---|
committer | Jan Kotas <jkotas@microsoft.com> | 2018-08-11 03:48:06 +0300 |
commit | 106c32100761ef21d67b74e59fa509fd81312d65 (patch) | |
tree | 5f085b259be6de13f859a63207068713721cc371 /src | |
parent | 40af11f5d52b5097304f6f143e219035b5796ee1 (diff) |
Moved to shared
Diffstat (limited to 'src')
-rw-r--r-- | src/System.Private.CoreLib/src/System.Private.CoreLib.csproj | 1 | ||||
-rw-r--r-- | src/System.Private.CoreLib/src/System/Threading/ThreadLocal.cs | 816 |
2 files changed, 0 insertions, 817 deletions
diff --git a/src/System.Private.CoreLib/src/System.Private.CoreLib.csproj b/src/System.Private.CoreLib/src/System.Private.CoreLib.csproj index d978b8ddd..a6b3b2c5b 100644 --- a/src/System.Private.CoreLib/src/System.Private.CoreLib.csproj +++ b/src/System.Private.CoreLib/src/System.Private.CoreLib.csproj @@ -319,7 +319,6 @@ <Compile Include="System\Threading\Tasks\TaskFactory.cs" /> <Compile Include="System\Threading\Tasks\TaskScheduler.cs" /> <Compile Include="System\Threading\Tasks\ThreadPoolTaskScheduler.cs" /> - <Compile Include="System\Threading\ThreadLocal.cs" /> <Compile Include="System\Threading\ThreadPool.cs" /> <Compile Include="System\Threading\ThreadPoolCallbackWrapper.cs" /> <Compile Include="System\Threading\Timer.cs" /> diff --git a/src/System.Private.CoreLib/src/System/Threading/ThreadLocal.cs b/src/System.Private.CoreLib/src/System/Threading/ThreadLocal.cs deleted file mode 100644 index ad6da89a9..000000000 --- a/src/System.Private.CoreLib/src/System/Threading/ThreadLocal.cs +++ /dev/null @@ -1,816 +0,0 @@ -// 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; - -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}")] - 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] - private static LinkedSlotVolatile[] ts_slotArray; - - [ThreadStatic] - private 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(nameof(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(nameof(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; - - using (LockHolder.Hold(s_idManager.m_lock)) - { - id = ~m_idComplement; - m_idComplement = 0; - - if (id < 0 || !m_initialized) - { - Debug.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 races 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 races 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(SR.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(SR.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(SR.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(SR.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 - using (LockHolder.Hold(s_idManager.m_lock)) - { - // 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(SR.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 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(SR.ThreadLocal_ValuesNotAvailable); - } - - var list = GetValuesAsList(); // returns null if disposed - if (list == null) throw new ObjectDisposedException(SR.ThreadLocal_Disposed); - return list; - } - } - - /// <summary>Gets all of the threads' values in a list.</summary> - private LowLevelListWithIList<T> GetValuesAsList() - { - LowLevelListWithIList<T> valueList = new LowLevelListWithIList<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(SR.ThreadLocal_Disposed); - } - - LinkedSlotVolatile[] slotArray = ts_slotArray; - return slotArray != null && id < slotArray.Length && slotArray[id].Value != null; - } - } - - - /// <summary>Gets the value of the ThreadLocal<T> 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<T>.</summary> - internal IList<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) - { - Debug.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. - // - using (LockHolder.Hold(s_idManager.m_lock)) - { - 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; - } - - private const int MaxArrayLength = int.MaxValue; - - /// <summary> - /// Chooses the next larger table size - /// </summary> - private static int GetNewTableSize(int minSize) - { - if ((uint)minSize > MaxArrayLength) - { - // Intentionally return a value that will result in an OutOfMemoryException - return int.MaxValue; - } - Debug.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 > MaxArrayLength) - { - newSize = MaxArrayLength; - } - - return newSize; - } - - /// <summary> - /// A wrapper struct used as LinkedSlotVolatile[] - an array of LinkedSlot instances, but with volatile semantics - /// on array accesses. - /// </summary> - internal 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> - internal 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 LowLevelList<bool> m_freeIds = new LowLevelList<bool>(); - - internal Lock m_lock = new Lock(); - - internal int GetId() - { - using (LockHolder.Hold(m_lock)) - { - 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) - { - using (LockHolder.Hold(m_lock)) - { - 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> - internal class FinalizationHelper - { - internal LinkedSlotVolatile[] SlotArray; - private bool m_trackAllValues; - - internal FinalizationHelper(LinkedSlotVolatile[] slotArray, bool trackAllValues) - { - SlotArray = slotArray; - m_trackAllValues = trackAllValues; - } - - ~FinalizationHelper() - { - LinkedSlotVolatile[] slotArray = SlotArray; - Debug.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. - using (LockHolder.Hold(s_idManager.m_lock)) - { - if (linkedSlot.Next != null) - { - linkedSlot.Next.Previous = linkedSlot.Previous; - } - - // Since the list uses a dummy head node, the Previous reference should never be null. - Debug.Assert(linkedSlot.Previous != null); - linkedSlot.Previous.Next = linkedSlot.Next; - } - } - } - } - } - } - - /// <summary>A debugger view of the ThreadLocal<T> to surface additional debugging properties and - /// to ensure that the ThreadLocal<T> 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 IList<T> Values - { - get - { - return m_tlocal.ValuesForDebugDisplay; - } - } - } -} |