summaryrefslogtreecommitdiff
path: root/src/mscorlib/src/System/Threading/ThreadLocal.cs
blob: 64b8a601966ace0fdbe56a749842befb0d63a4b9 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
// 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.Collections.Generic;
using System.Diagnostics;
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}")]
    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;

            lock (s_idManager)
            {
                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 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(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
            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(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 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(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 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(SR.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)
        {
            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.
            //
            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;
            }
            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 > 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;
                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.
                        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.
                            Debug.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;
            }
        }
    }
}