Initial commit
[mono.git] / mcs / class / referencesource / mscorlib / system / threading / ThreadLocal.cs
1 #pragma warning disable 0420
2 // ==++==
3 //
4 //   Copyright (c) Microsoft Corporation.  All rights reserved.
5 // 
6 // ==--==
7 // =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
8 //
9 // ThreadLocal.cs
10 //
11 // <OWNER>[....]</OWNER>
12 //
13 // A class that provides a simple, lightweight implementation of thread-local lazy-initialization, where a value is initialized once per accessing 
14 // thread; this provides an alternative to using a ThreadStatic static variable and having 
15 // to check the variable prior to every access to see if it's been initialized.
16 //
17 // 
18 //
19 // =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
20
21 using System.Diagnostics;
22 using System.Collections.Generic;
23 using System.Security.Permissions;
24 using System.Diagnostics.Contracts;
25
26 namespace System.Threading
27 {
28     /// <summary>
29     /// Provides thread-local storage of data.
30     /// </summary>
31     /// <typeparam name="T">Specifies the type of data stored per-thread.</typeparam>
32     /// <remarks>
33     /// <para>
34     /// With the exception of <see cref="Dispose()"/>, all public and protected members of 
35     /// <see cref="ThreadLocal{T}"/> are thread-safe and may be used
36     /// concurrently from multiple threads.
37     /// </para>
38     /// </remarks>
39     [DebuggerTypeProxy(typeof(SystemThreading_ThreadLocalDebugView<>))]
40     [DebuggerDisplay("IsValueCreated={IsValueCreated}, Value={ValueForDebugDisplay}, Count={ValuesCountForDebugDisplay}")]
41     [HostProtection(Synchronization = true, ExternalThreading = true)]
42     public class ThreadLocal<T> : IDisposable
43     {
44
45         // a delegate that returns the created value, if null the created value will be default(T)
46         private Func<T> m_valueFactory;
47
48         //
49         // ts_slotArray is a table of thread-local values for all ThreadLocal<T> instances
50         //
51         // So, when a thread reads ts_slotArray, it gets back an array of *all* ThreadLocal<T> values for this thread and this T.
52         // The slot relevant to this particular ThreadLocal<T> instance is determined by the m_idComplement instance field stored in
53         // the ThreadLocal<T> instance.
54         //
55         [ThreadStatic]
56         static LinkedSlotVolatile[] ts_slotArray;
57
58         [ThreadStatic]
59         static FinalizationHelper ts_finalizationHelper;
60
61         // Slot ID of this ThreadLocal<> instance. We store a bitwise complement of the ID (that is ~ID), which allows us to distinguish
62         // between the case when ID is 0 and an incompletely initialized object, either due to a thread abort in the constructor, or
63         // possibly due to a memory model issue in user code.
64         private int m_idComplement;
65
66         // This field is set to true when the constructor completes. That is helpful for recognizing whether a constructor
67         // threw an exception - either due to invalid argument or due to a thread abort. Finally, the field is set to false
68         // when the instance is disposed.
69         private volatile bool m_initialized;
70
71         // IdManager assigns and reuses slot IDs. Additionally, the object is also used as a global lock.
72         private static IdManager s_idManager = new IdManager();
73
74         // A linked list of all values associated with this ThreadLocal<T> instance.
75         // We create a dummy head node. That allows us to remove any (non-dummy)  node without having to locate the m_linkedSlot field. 
76         private LinkedSlot m_linkedSlot = new LinkedSlot(null);
77
78         // Whether the Values property is supported
79         private bool m_trackAllValues;
80
81         /// <summary>
82         /// Initializes the <see cref="System.Threading.ThreadLocal{T}"/> instance.
83         /// </summary>
84         public ThreadLocal()
85         {
86             Initialize(null, false);
87         }
88
89         /// <summary>
90         /// Initializes the <see cref="System.Threading.ThreadLocal{T}"/> instance.
91         /// </summary>
92         /// <param name="trackAllValues">Whether to track all values set on the instance and expose them through the Values property.</param>
93         public ThreadLocal(bool trackAllValues)
94         {
95             Initialize(null, trackAllValues);
96         }
97
98
99         /// <summary>
100         /// Initializes the <see cref="System.Threading.ThreadLocal{T}"/> instance with the
101         /// specified <paramref name="valueFactory"/> function.
102         /// </summary>
103         /// <param name="valueFactory">
104         /// The <see cref="T:System.Func{T}"/> invoked to produce a lazily-initialized value when 
105         /// an attempt is made to retrieve <see cref="Value"/> without it having been previously initialized.
106         /// </param>
107         /// <exception cref="T:System.ArgumentNullException">
108         /// <paramref name="valueFactory"/> is a null reference (Nothing in Visual Basic).
109         /// </exception>
110         public ThreadLocal(Func<T> valueFactory)
111         {
112             if (valueFactory == null)
113                 throw new ArgumentNullException("valueFactory");
114
115             Initialize(valueFactory, false);
116         }
117
118         /// <summary>
119         /// Initializes the <see cref="System.Threading.ThreadLocal{T}"/> instance with the
120         /// specified <paramref name="valueFactory"/> function.
121         /// </summary>
122         /// <param name="valueFactory">
123         /// The <see cref="T:System.Func{T}"/> invoked to produce a lazily-initialized value when 
124         /// an attempt is made to retrieve <see cref="Value"/> without it having been previously initialized.
125         /// </param>
126         /// <param name="trackAllValues">Whether to track all values set on the instance and expose them via the Values property.</param>
127         /// <exception cref="T:System.ArgumentNullException">
128         /// <paramref name="valueFactory"/> is a null reference (Nothing in Visual Basic).
129         /// </exception>
130         public ThreadLocal(Func<T> valueFactory, bool trackAllValues)
131         {
132             if (valueFactory == null)
133                 throw new ArgumentNullException("valueFactory");
134
135             Initialize(valueFactory, trackAllValues);
136         }
137
138         private void Initialize(Func<T> valueFactory, bool trackAllValues)
139         {
140             m_valueFactory = valueFactory;
141             m_trackAllValues = trackAllValues;
142
143             // Assign the ID and mark the instance as initialized. To avoid leaking IDs, we assign the ID and set m_initialized
144             // in a finally block, to avoid a thread abort in between the two statements.
145             try { }
146             finally
147             {
148                 m_idComplement = ~s_idManager.GetId();
149
150                 // As the last step, mark the instance as fully initialized. (Otherwise, if m_initialized=false, we know that an exception
151                 // occurred in the constructor.)
152                 m_initialized = true;
153             }
154         }
155
156         /// <summary>
157         /// Releases the resources used by this <see cref="T:System.Threading.ThreadLocal{T}" /> instance.
158         /// </summary>
159         ~ThreadLocal()
160         {
161             // finalizer to return the type combination index to the pool
162             Dispose(false);
163         }
164
165         #region IDisposable Members
166
167         /// <summary>
168         /// Releases the resources used by this <see cref="T:System.Threading.ThreadLocal{T}" /> instance.
169         /// </summary>
170         /// <remarks>
171         /// Unlike most of the members of <see cref="T:System.Threading.ThreadLocal{T}"/>, this method is not thread-safe.
172         /// </remarks>
173         public void Dispose()
174         {
175             Dispose(true);
176             GC.SuppressFinalize(this);
177         }
178
179         /// <summary>
180         /// Releases the resources used by this <see cref="T:System.Threading.ThreadLocal{T}" /> instance.
181         /// </summary>
182         /// <param name="disposing">
183         /// A Boolean value that indicates whether this method is being called due to a call to <see cref="Dispose()"/>.
184         /// </param>
185         /// <remarks>
186         /// Unlike most of the members of <see cref="T:System.Threading.ThreadLocal{T}"/>, this method is not thread-safe.
187         /// </remarks>
188         protected virtual void Dispose(bool disposing)
189         {
190             int id;
191
192             lock (s_idManager)
193             {
194                 id = ~m_idComplement;
195                 m_idComplement = 0;
196
197                 if (id < 0 || !m_initialized)
198                 {
199                     Contract.Assert(id >= 0 || !m_initialized, "expected id >= 0 if initialized");
200
201                     // Handle double Dispose calls or disposal of an instance whose constructor threw an exception.
202                     return;
203                 }
204                 m_initialized = false;
205
206                 for (LinkedSlot linkedSlot = m_linkedSlot.Next; linkedSlot != null; linkedSlot = linkedSlot.Next)
207                 {
208                     LinkedSlotVolatile[] slotArray = linkedSlot.SlotArray;
209
210                     if (slotArray == null)
211                     {
212                         // The thread that owns this slotArray has already finished.
213                         continue;
214                     }
215
216                     // Remove the reference from the LinkedSlot to the slot table.
217                     linkedSlot.SlotArray = null;
218
219                     // And clear the references from the slot table to the linked slot and the value so that
220                     // both can get garbage collected.
221                     slotArray[id].Value.Value = default(T);
222                     slotArray[id].Value = null;
223                 }
224             }
225             m_linkedSlot = null;
226             s_idManager.ReturnId(id);
227         }
228
229         #endregion
230
231         /// <summary>Creates and returns a string representation of this instance for the current thread.</summary>
232         /// <returns>The result of calling <see cref="System.Object.ToString"/> on the <see cref="Value"/>.</returns>
233         /// <exception cref="T:System.NullReferenceException">
234         /// The <see cref="Value"/> for the current thread is a null reference (Nothing in Visual Basic).
235         /// </exception>
236         /// <exception cref="T:System.InvalidOperationException">
237         /// The initialization function referenced <see cref="Value"/> in an improper manner.
238         /// </exception>
239         /// <exception cref="T:System.ObjectDisposedException">
240         /// The <see cref="ThreadLocal{T}"/> instance has been disposed.
241         /// </exception>
242         /// <remarks>
243         /// Calling this method forces initialization for the current thread, as is the
244         /// case with accessing <see cref="Value"/> directly.
245         /// </remarks>
246         public override string ToString()
247         {
248             return Value.ToString();
249         }
250
251         /// <summary>
252         /// Gets or sets the value of this instance for the current thread.
253         /// </summary>
254         /// <exception cref="T:System.InvalidOperationException">
255         /// The initialization function referenced <see cref="Value"/> in an improper manner.
256         /// </exception>
257         /// <exception cref="T:System.ObjectDisposedException">
258         /// The <see cref="ThreadLocal{T}"/> instance has been disposed.
259         /// </exception>
260         /// <remarks>
261         /// If this instance was not previously initialized for the current thread,
262         /// accessing <see cref="Value"/> will attempt to initialize it. If an initialization function was 
263         /// supplied during the construction, that initialization will happen by invoking the function 
264         /// to retrieve the initial value for <see cref="Value"/>.  Otherwise, the default value of 
265         /// <typeparamref name="T"/> will be used.
266         /// </remarks>
267         [DebuggerBrowsable(DebuggerBrowsableState.Never)]
268         public T Value
269         {
270             get
271             {
272                 LinkedSlotVolatile[] slotArray = ts_slotArray;
273                 LinkedSlot slot;
274                 int id = ~m_idComplement;
275
276                 //
277                 // Attempt to get the value using the fast path
278                 //
279                 if (slotArray != null   // Has the slot array been initialized?
280                     && id >= 0   // Is the ID non-negative (i.e., instance is not disposed)?
281                     && id < slotArray.Length   // Is the table large enough?
282                     && (slot = slotArray[id].Value) != null   // Has a LinkedSlot object has been allocated for this ID?
283                     && m_initialized // Has the instance *still* not been disposed (important for ----s with Dispose)?
284                 ) 
285                 {
286                     // We verified that the instance has not been disposed *after* we got a reference to the slot.
287                     // This guarantees that we have a reference to the right slot.
288                     // 
289                     // Volatile read of the LinkedSlotVolatile.Value property ensures that the m_initialized read
290                     // will not be reordered before the read of slotArray[id].
291                     return slot.Value;
292                 }
293
294                 return GetValueSlow();
295             }
296             set
297             {
298                 LinkedSlotVolatile[] slotArray = ts_slotArray;
299                 LinkedSlot slot;
300                 int id = ~m_idComplement;
301
302                 //
303                 // Attempt to set the value using the fast path
304                 //
305                 if (slotArray != null   // Has the slot array been initialized?
306                     && id >= 0   // Is the ID non-negative (i.e., instance is not disposed)?
307                     && id < slotArray.Length   // Is the table large enough?
308                     && (slot = slotArray[id].Value) != null   // Has a LinkedSlot object has been allocated for this ID?
309                     && m_initialized // Has the instance *still* not been disposed (important for ----s with Dispose)?
310                     )
311                 {
312                     // We verified that the instance has not been disposed *after* we got a reference to the slot.
313                     // This guarantees that we have a reference to the right slot.
314                     // 
315                     // Volatile read of the LinkedSlotVolatile.Value property ensures that the m_initialized read
316                     // will not be reordered before the read of slotArray[id].
317                     slot.Value = value;
318                 }
319                 else
320                 {
321                     SetValueSlow(value, slotArray);
322                 }
323             }
324         }
325
326         private T GetValueSlow()
327         {
328             // If the object has been disposed, the id will be -1.
329             int id = ~m_idComplement;
330             if (id < 0)
331             {
332                 throw new ObjectDisposedException(Environment.GetResourceString("ThreadLocal_Disposed"));
333             }
334
335             Debugger.NotifyOfCrossThreadDependency();
336
337             // Determine the initial value
338             T value;
339             if (m_valueFactory == null)
340             {
341                 value = default(T);
342             }
343             else
344             {
345                 value = m_valueFactory();
346
347                 if (IsValueCreated)
348                 {
349                     throw new InvalidOperationException(Environment.GetResourceString("ThreadLocal_Value_RecursiveCallsToValue"));
350                 }
351             }
352
353             // Since the value has been previously uninitialized, we also need to set it (according to the ThreadLocal semantics).
354             Value = value;
355             return value;
356         }
357
358         private void SetValueSlow(T value, LinkedSlotVolatile[] slotArray)
359         {
360             int id = ~m_idComplement;
361
362             // If the object has been disposed, id will be -1.
363             if (id < 0)
364             {
365                 throw new ObjectDisposedException(Environment.GetResourceString("ThreadLocal_Disposed"));
366             }
367
368             // If a slot array has not been created on this thread yet, create it.
369             if (slotArray == null)
370             {
371                 slotArray = new LinkedSlotVolatile[GetNewTableSize(id + 1)];
372                 ts_finalizationHelper = new FinalizationHelper(slotArray, m_trackAllValues);
373                 ts_slotArray = slotArray;
374             }
375
376             // If the slot array is not big enough to hold this ID, increase the table size.
377             if (id >= slotArray.Length)
378             {
379                 GrowTable(ref slotArray, id + 1);
380                 ts_finalizationHelper.SlotArray = slotArray;
381                 ts_slotArray = slotArray;
382             }
383
384             // If we are using the slot in this table for the first time, create a new LinkedSlot and add it into
385             // the linked list for this ThreadLocal instance.
386             if (slotArray[id].Value == null)
387             {
388                 CreateLinkedSlot(slotArray, id, value);
389             }
390             else
391             {
392                 // Volatile read of the LinkedSlotVolatile.Value property ensures that the m_initialized read
393                 // that follows will not be reordered before the read of slotArray[id].
394                 LinkedSlot slot = slotArray[id].Value;
395
396                 // It is important to verify that the ThreadLocal instance has not been disposed. The check must come
397                 // after capturing slotArray[id], but before assigning the value into the slot. This ensures that
398                 // if this ThreadLocal instance was disposed on another thread and another ThreadLocal instance was
399                 // created, we definitely won't assign the value into the wrong instance.
400
401                 if (!m_initialized)
402                 {
403                     throw new ObjectDisposedException(Environment.GetResourceString("ThreadLocal_Disposed"));
404                 }
405
406                 slot.Value = value;
407             }
408         }
409
410         /// <summary>
411         /// Creates a LinkedSlot and inserts it into the linked list for this ThreadLocal instance.
412         /// </summary>
413         private void CreateLinkedSlot(LinkedSlotVolatile[] slotArray, int id, T value)
414         {
415             // Create a LinkedSlot
416             var linkedSlot = new LinkedSlot(slotArray);
417
418             // Insert the LinkedSlot into the linked list maintained by this ThreadLocal<> instance and into the slot array
419             lock (s_idManager)
420             {
421                 // Check that the instance has not been disposed. It is important to check this under a lock, since
422                 // Dispose also executes under a lock.
423                 if (!m_initialized)
424                 {
425                     throw new ObjectDisposedException(Environment.GetResourceString("ThreadLocal_Disposed"));
426                 }
427
428                 LinkedSlot firstRealNode = m_linkedSlot.Next;
429
430                 //
431                 // Insert linkedSlot between nodes m_linkedSlot and firstRealNode. 
432                 // (m_linkedSlot is the dummy head node that should always be in the front.)
433                 //
434                 linkedSlot.Next = firstRealNode;
435                 linkedSlot.Previous = m_linkedSlot;
436                 linkedSlot.Value = value;
437
438                 if (firstRealNode != null)
439                 {
440                     firstRealNode.Previous = linkedSlot;
441                 }
442                 m_linkedSlot.Next = linkedSlot;
443
444                 // Assigning the slot under a lock prevents a ---- with Dispose (dispose also acquires the lock).
445                 // Otherwise, it would be possible that the ThreadLocal instance is disposed, another one gets created
446                 // with the same ID, and the write would go to the wrong instance.
447                 slotArray[id].Value = linkedSlot;
448             }
449         }
450
451         /// <summary>
452         /// Gets a list for all of the values currently stored by all of the threads that have accessed this instance.
453         /// </summary>
454         /// <exception cref="T:System.ObjectDisposedException">
455         /// The <see cref="ThreadLocal{T}"/> instance has been disposed.
456         /// </exception>
457         public IList<T> Values
458         {
459             get
460             {
461                 if (!m_trackAllValues)
462                 {
463                     throw new InvalidOperationException(Environment.GetResourceString("ThreadLocal_ValuesNotAvailable"));
464                 }
465
466                 var list = GetValuesAsList(); // returns null if disposed
467                 if (list == null) throw new ObjectDisposedException(Environment.GetResourceString("ThreadLocal_Disposed"));
468                 return list;
469             }
470         }
471
472         /// <summary>Gets all of the threads' values in a list.</summary>
473         private List<T> GetValuesAsList()
474         {
475             List<T> valueList = new List<T>();
476             int id = ~m_idComplement;
477             if (id == -1)
478             {
479                 return null;
480             }
481
482             // Walk over the linked list of slots and gather the values associated with this ThreadLocal instance.
483             for (LinkedSlot linkedSlot = m_linkedSlot.Next; linkedSlot != null; linkedSlot = linkedSlot.Next)
484             {
485                 // We can safely read linkedSlot.Value. Even if this ThreadLocal has been disposed in the meantime, the LinkedSlot
486                 // objects will never be assigned to another ThreadLocal instance.
487                 valueList.Add(linkedSlot.Value);
488             }
489
490             return valueList;
491         }
492
493         /// <summary>Gets the number of threads that have data in this instance.</summary>
494         private int ValuesCountForDebugDisplay
495         {
496             get
497             {
498                 int count = 0;
499                 for (LinkedSlot linkedSlot = m_linkedSlot.Next; linkedSlot != null; linkedSlot = linkedSlot.Next)
500                 {
501                     count++;
502                 }
503                 return count;
504             }
505         }
506
507         /// <summary>
508         /// Gets whether <see cref="Value"/> is initialized on the current thread.
509         /// </summary>
510         /// <exception cref="T:System.ObjectDisposedException">
511         /// The <see cref="ThreadLocal{T}"/> instance has been disposed.
512         /// </exception>
513         public bool IsValueCreated
514         {
515             get
516             {
517                 int id = ~m_idComplement;
518                 if (id < 0)
519                 {
520                     throw new ObjectDisposedException(Environment.GetResourceString("ThreadLocal_Disposed"));
521                 }
522
523                 LinkedSlotVolatile[] slotArray = ts_slotArray;
524                 return slotArray != null && id < slotArray.Length && slotArray[id].Value != null;
525             }
526         }
527
528
529         /// <summary>Gets the value of the ThreadLocal&lt;T&gt; for debugging display purposes. It takes care of getting
530         /// the value for the current thread in the ThreadLocal mode.</summary>
531         internal T ValueForDebugDisplay
532         {
533             get
534             {
535                 LinkedSlotVolatile[] slotArray = ts_slotArray;
536                 int id = ~m_idComplement;
537
538                 LinkedSlot slot;
539                 if (slotArray == null || id >= slotArray.Length || (slot = slotArray[id].Value) == null || !m_initialized)
540                     return default(T);
541                 return slot.Value;
542             }
543         }
544
545         /// <summary>Gets the values of all threads that accessed the ThreadLocal&lt;T&gt;.</summary>
546         internal List<T> ValuesForDebugDisplay // same as Values property, but doesn't throw if disposed
547         {
548             get { return GetValuesAsList(); }
549         }
550
551         /// <summary>
552         /// Resizes a table to a certain length (or larger).
553         /// </summary>
554         private void GrowTable(ref LinkedSlotVolatile[] table, int minLength)
555         {
556             Contract.Assert(table.Length < minLength);
557
558             // Determine the size of the new table and allocate it.
559             int newLen = GetNewTableSize(minLength);
560             LinkedSlotVolatile[] newTable = new LinkedSlotVolatile[newLen];
561
562             //
563             // The lock is necessary to avoid a race with ThreadLocal.Dispose. GrowTable has to point all 
564             // LinkedSlot instances referenced in the old table to reference the new table. Without locking, 
565             // Dispose could use a stale SlotArray reference and clear out a slot in the old array only, while 
566             // the value continues to be referenced from the new (larger) array.
567             //
568             lock (s_idManager)
569             {
570                 for (int i = 0; i < table.Length; i++)
571                 {
572                     LinkedSlot linkedSlot = table[i].Value;
573                     if (linkedSlot != null && linkedSlot.SlotArray != null)
574                     {
575                         linkedSlot.SlotArray = newTable;
576                         newTable[i] = table[i];
577                     }
578                 }
579             }
580
581             table = newTable;
582         }
583
584         /// <summary>
585         /// Chooses the next larger table size
586         /// </summary>
587         private static int GetNewTableSize(int minSize)
588         {
589             if ((uint)minSize > Array.MaxArrayLength)
590             {
591                 // Intentionally return a value that will result in an OutOfMemoryException
592                 return int.MaxValue;
593             }
594             Contract.Assert(minSize > 0);
595
596             //
597             // Round up the size to the next power of 2
598             //
599             // The algorithm takes three steps: 
600             //   input -> subtract one -> propagate 1-bits to the right -> add one
601             //
602             // Let's take a look at the 3 steps in both interesting cases: where the input 
603             // is (Example 1) and isn't (Example 2) a power of 2.
604             //
605             //   Example 1: 100000 -> 011111 -> 011111 -> 100000
606             //   Example 2: 011010 -> 011001 -> 011111 -> 100000
607             // 
608             int newSize = minSize;
609
610             // Step 1: Decrement
611             newSize--;
612
613             // Step 2: Propagate 1-bits to the right.
614             newSize |= newSize >> 1;
615             newSize |= newSize >> 2;
616             newSize |= newSize >> 4;
617             newSize |= newSize >> 8;
618             newSize |= newSize >> 16;
619
620             // Step 3: Increment
621             newSize++;
622
623             // Don't set newSize to more than Array.MaxArrayLength
624             if ((uint)newSize > Array.MaxArrayLength)
625             {
626                 newSize = Array.MaxArrayLength;
627             }
628
629             return newSize;
630         }
631
632         /// <summary>
633         /// A wrapper struct used as LinkedSlotVolatile[] - an array of LinkedSlot instances, but with volatile semantics
634         /// on array accesses.
635         /// </summary>
636         private struct LinkedSlotVolatile
637         {
638             internal volatile LinkedSlot Value;
639         }
640
641         /// <summary>
642         /// A node in the doubly-linked list stored in the ThreadLocal instance.
643         /// 
644         /// The value is stored in one of two places:
645         /// 
646         ///     1. If SlotArray is not null, the value is in SlotArray.Table[id]
647         ///     2. If SlotArray is null, the value is in FinalValue.
648         /// </summary>
649         private sealed class LinkedSlot
650         {
651             internal LinkedSlot(LinkedSlotVolatile[] slotArray)
652             {
653                 SlotArray = slotArray;
654             }
655
656             // The next LinkedSlot for this ThreadLocal<> instance
657             internal volatile LinkedSlot Next;
658
659             // The previous LinkedSlot for this ThreadLocal<> instance
660             internal volatile LinkedSlot Previous;
661
662             // The SlotArray that stores this LinkedSlot at SlotArray.Table[id].
663             internal volatile LinkedSlotVolatile[] SlotArray;
664
665             // The value for this slot.
666             internal T Value;
667         }
668
669         /// <summary>
670         /// A manager class that assigns IDs to ThreadLocal instances
671         /// </summary>
672         private class IdManager
673         {
674             // The next ID to try
675             private int m_nextIdToTry = 0;
676
677             // Stores whether each ID is free or not. Additionally, the object is also used as a lock for the IdManager.
678             private List<bool> m_freeIds = new List<bool>();
679
680             internal int GetId()
681             {
682                 lock (m_freeIds)
683                 {
684                     int availableId = m_nextIdToTry;
685                     while (availableId < m_freeIds.Count)
686                     {
687                         if (m_freeIds[availableId]) { break; }
688                         availableId++;
689                     }
690
691                     if (availableId == m_freeIds.Count)
692                     {
693                         m_freeIds.Add(false);
694                     }
695                     else
696                     {
697                         m_freeIds[availableId] = false;
698                     }
699
700                     m_nextIdToTry = availableId + 1;
701
702                     return availableId;
703                 }
704             }
705
706             // Return an ID to the pool
707             internal void ReturnId(int id)
708             {
709                 lock (m_freeIds)
710                 {
711                     m_freeIds[id] = true;
712                     if (id < m_nextIdToTry) m_nextIdToTry = id;
713                 }
714             }
715         }
716
717         /// <summary>
718         /// A class that facilitates ThreadLocal cleanup after a thread exits.
719         /// 
720         /// After a thread with an associated thread-local table has exited, the FinalizationHelper 
721         /// is reponsible for removing back-references to the table. Since an instance of FinalizationHelper 
722         /// is only referenced from a single thread-local slot, the FinalizationHelper will be GC'd once
723         /// the thread has exited.
724         /// 
725         /// The FinalizationHelper then locates all LinkedSlot instances with back-references to the table
726         /// (all those LinkedSlot instances can be found by following references from the table slots) and
727         /// releases the table so that it can get GC'd.
728         /// </summary>
729         private class FinalizationHelper
730         {
731             internal LinkedSlotVolatile[] SlotArray;
732             private bool m_trackAllValues;
733
734             internal FinalizationHelper(LinkedSlotVolatile[] slotArray, bool trackAllValues)
735             {
736                 SlotArray = slotArray;
737                 m_trackAllValues = trackAllValues;
738             }
739
740             ~FinalizationHelper()
741             {
742                 LinkedSlotVolatile[] slotArray = SlotArray;
743                 Contract.Assert(slotArray != null);
744
745                 for (int i = 0; i < slotArray.Length; i++)
746                 {
747                     LinkedSlot linkedSlot = slotArray[i].Value;
748                     if (linkedSlot == null)
749                     {
750                         // This slot in the table is empty
751                         continue;
752                     }
753
754                     if (m_trackAllValues)
755                     {
756                         // Set the SlotArray field to null to release the slot array.
757                         linkedSlot.SlotArray = null;
758                     }
759                     else
760                     {
761                         // Remove the LinkedSlot from the linked list. Once the FinalizationHelper is done, all back-references to
762                         // the table will be have been removed, and so the table can get GC'd.
763                         lock (s_idManager)
764                         {
765                             if (linkedSlot.Next != null)
766                             {
767                                 linkedSlot.Next.Previous = linkedSlot.Previous;
768                             }
769
770                             // Since the list uses a dummy head node, the Previous reference should never be null.
771                             Contract.Assert(linkedSlot.Previous != null);
772                             linkedSlot.Previous.Next = linkedSlot.Next;
773                         }
774                     }
775                 }
776             }
777         }
778     }
779
780     /// <summary>A debugger view of the ThreadLocal&lt;T&gt; to surface additional debugging properties and 
781     /// to ensure that the ThreadLocal&lt;T&gt; does not become initialized if it was not already.</summary>
782     internal sealed class SystemThreading_ThreadLocalDebugView<T>
783     {
784         //The ThreadLocal object being viewed.
785         private readonly ThreadLocal<T> m_tlocal;
786
787         /// <summary>Constructs a new debugger view object for the provided ThreadLocal object.</summary>
788         /// <param name="tlocal">A ThreadLocal object to browse in the debugger.</param>
789         public SystemThreading_ThreadLocalDebugView(ThreadLocal<T> tlocal)
790         {
791             m_tlocal = tlocal;
792         }
793
794         /// <summary>Returns whether the ThreadLocal object is initialized or not.</summary>
795         public bool IsValueCreated
796         {
797             get { return m_tlocal.IsValueCreated; }
798         }
799
800         /// <summary>Returns the value of the ThreadLocal object.</summary>
801         public T Value
802         {
803             get
804             {
805                 return m_tlocal.ValueForDebugDisplay;
806             }
807         }
808
809         /// <summary>Return all values for all threads that have accessed this instance.</summary>
810         public List<T> Values
811         {
812             get
813             {
814                 return m_tlocal.ValuesForDebugDisplay;
815             }
816         }
817     }
818 }