1b59163482ce8390ecd5f6f74122f6ef1cf19193
[platform/upstream/coreclr.git] / src / mscorlib / src / System / Threading / ThreadPool.cs
1 // Licensed to the .NET Foundation under one or more agreements.
2 // The .NET Foundation licenses this file to you under the MIT license.
3 // See the LICENSE file in the project root for more information.
4
5 /*=============================================================================
6 **
7 **
8 **
9 ** Purpose: Class for creating and managing a threadpool
10 **
11 **
12 =============================================================================*/
13
14 using System.Collections.Concurrent;
15 using System.Collections.Generic;
16 using System.Diagnostics;
17 using System.Diagnostics.CodeAnalysis;
18 using System.Diagnostics.Tracing;
19 using System.Runtime.CompilerServices;
20 using System.Runtime.ConstrainedExecution;
21 using System.Runtime.InteropServices;
22 using System.Security;
23 using Microsoft.Win32;
24
25 namespace System.Threading
26 {
27     internal static class ThreadPoolGlobals
28     {
29         //Per-appDomain quantum (in ms) for which the thread keeps processing
30         //requests in the current domain.
31         public const uint TP_QUANTUM = 30U;
32
33         public static readonly int processorCount = Environment.ProcessorCount;
34
35         public static volatile bool vmTpInitialized;
36         public static bool enableWorkerTracking;
37
38         public static readonly ThreadPoolWorkQueue workQueue = new ThreadPoolWorkQueue();
39     }
40
41     [StructLayout(LayoutKind.Sequential)] // enforce layout so that padding reduces false sharing
42     internal sealed class ThreadPoolWorkQueue
43     {
44         internal static class WorkStealingQueueList
45         {
46             private static volatile WorkStealingQueue[] _queues = new WorkStealingQueue[0];
47
48             public static WorkStealingQueue[] Queues => _queues;
49
50             public static void Add(WorkStealingQueue queue)
51             {
52                 Debug.Assert(queue != null);
53                 while (true)
54                 {
55                     WorkStealingQueue[] oldQueues = _queues;
56                     Debug.Assert(Array.IndexOf(oldQueues, queue) == -1);
57
58                     var newQueues = new WorkStealingQueue[oldQueues.Length + 1];
59                     Array.Copy(oldQueues, 0, newQueues, 0, oldQueues.Length);
60                     newQueues[newQueues.Length - 1] = queue;
61                     if (Interlocked.CompareExchange(ref _queues, newQueues, oldQueues) == oldQueues)
62                     {
63                         break;
64                     }
65                 }
66             }
67
68             public static void Remove(WorkStealingQueue queue)
69             {
70                 Debug.Assert(queue != null);
71                 while (true)
72                 {
73                     WorkStealingQueue[] oldQueues = _queues;
74                     if (oldQueues.Length == 0)
75                     {
76                         return;
77                     }
78
79                     int pos = Array.IndexOf(oldQueues, queue);
80                     if (pos == -1)
81                     {
82                         Debug.Fail("Should have found the queue");
83                         return;
84                     }
85
86                     var newQueues = new WorkStealingQueue[oldQueues.Length - 1];
87                     if (pos == 0)
88                     {
89                         Array.Copy(oldQueues, 1, newQueues, 0, newQueues.Length);
90                     }
91                     else if (pos == oldQueues.Length - 1)
92                     {
93                         Array.Copy(oldQueues, 0, newQueues, 0, newQueues.Length);
94                     }
95                     else
96                     {
97                         Array.Copy(oldQueues, 0, newQueues, 0, pos);
98                         Array.Copy(oldQueues, pos + 1, newQueues, pos, newQueues.Length - pos);
99                     }
100
101                     if (Interlocked.CompareExchange(ref _queues, newQueues, oldQueues) == oldQueues)
102                     {
103                         break;
104                     }
105                 }
106             }
107         }
108
109         internal sealed class WorkStealingQueue
110         {
111             private const int INITIAL_SIZE = 32;
112             internal volatile IThreadPoolWorkItem[] m_array = new IThreadPoolWorkItem[INITIAL_SIZE];
113             private volatile int m_mask = INITIAL_SIZE - 1;
114
115 #if DEBUG
116             // in debug builds, start at the end so we exercise the index reset logic.
117             private const int START_INDEX = int.MaxValue;
118 #else
119             private const int START_INDEX = 0;
120 #endif
121
122             private volatile int m_headIndex = START_INDEX;
123             private volatile int m_tailIndex = START_INDEX;
124
125             private SpinLock m_foreignLock = new SpinLock(enableThreadOwnerTracking: false);
126
127             public void LocalPush(IThreadPoolWorkItem obj)
128             {
129                 int tail = m_tailIndex;
130
131                 // We're going to increment the tail; if we'll overflow, then we need to reset our counts
132                 if (tail == int.MaxValue)
133                 {
134                     bool lockTaken = false;
135                     try
136                     {
137                         m_foreignLock.Enter(ref lockTaken);
138
139                         if (m_tailIndex == int.MaxValue)
140                         {
141                             //
142                             // Rather than resetting to zero, we'll just mask off the bits we don't care about.
143                             // This way we don't need to rearrange the items already in the queue; they'll be found
144                             // correctly exactly where they are.  One subtlety here is that we need to make sure that
145                             // if head is currently < tail, it remains that way.  This happens to just fall out from
146                             // the bit-masking, because we only do this if tail == int.MaxValue, meaning that all
147                             // bits are set, so all of the bits we're keeping will also be set.  Thus it's impossible
148                             // for the head to end up > than the tail, since you can't set any more bits than all of 
149                             // them.
150                             //
151                             m_headIndex = m_headIndex & m_mask;
152                             m_tailIndex = tail = m_tailIndex & m_mask;
153                             Debug.Assert(m_headIndex <= m_tailIndex);
154                         }
155                     }
156                     finally
157                     {
158                         if (lockTaken)
159                             m_foreignLock.Exit(useMemoryBarrier: true);
160                     }
161                 }
162
163                 // When there are at least 2 elements' worth of space, we can take the fast path.
164                 if (tail < m_headIndex + m_mask)
165                 {
166                     Volatile.Write(ref m_array[tail & m_mask], obj);
167                     m_tailIndex = tail + 1;
168                 }
169                 else
170                 {
171                     // We need to contend with foreign pops, so we lock.
172                     bool lockTaken = false;
173                     try
174                     {
175                         m_foreignLock.Enter(ref lockTaken);
176
177                         int head = m_headIndex;
178                         int count = m_tailIndex - m_headIndex;
179
180                         // If there is still space (one left), just add the element.
181                         if (count >= m_mask)
182                         {
183                             // We're full; expand the queue by doubling its size.
184                             var newArray = new IThreadPoolWorkItem[m_array.Length << 1];
185                             for (int i = 0; i < m_array.Length; i++)
186                                 newArray[i] = m_array[(i + head) & m_mask];
187
188                             // Reset the field values, incl. the mask.
189                             m_array = newArray;
190                             m_headIndex = 0;
191                             m_tailIndex = tail = count;
192                             m_mask = (m_mask << 1) | 1;
193                         }
194
195                         Volatile.Write(ref m_array[tail & m_mask], obj);
196                         m_tailIndex = tail + 1;
197                     }
198                     finally
199                     {
200                         if (lockTaken)
201                             m_foreignLock.Exit(useMemoryBarrier: false);
202                     }
203                 }
204             }
205
206             [SuppressMessage("Microsoft.Concurrency", "CA8001", Justification = "Reviewed for thread safety")]
207             public bool LocalFindAndPop(IThreadPoolWorkItem obj)
208             {
209                 // Fast path: check the tail. If equal, we can skip the lock.
210                 if (m_array[(m_tailIndex - 1) & m_mask] == obj)
211                 {
212                     IThreadPoolWorkItem unused = LocalPop();
213                     Debug.Assert(unused == null || unused == obj);
214                     return unused != null;
215                 }
216
217                 // Else, do an O(N) search for the work item. The theory of work stealing and our
218                 // inlining logic is that most waits will happen on recently queued work.  And
219                 // since recently queued work will be close to the tail end (which is where we
220                 // begin our search), we will likely find it quickly.  In the worst case, we
221                 // will traverse the whole local queue; this is typically not going to be a
222                 // problem (although degenerate cases are clearly an issue) because local work
223                 // queues tend to be somewhat shallow in length, and because if we fail to find
224                 // the work item, we are about to block anyway (which is very expensive).
225                 for (int i = m_tailIndex - 2; i >= m_headIndex; i--)
226                 {
227                     if (m_array[i & m_mask] == obj)
228                     {
229                         // If we found the element, block out steals to avoid interference.
230                         bool lockTaken = false;
231                         try
232                         {
233                             m_foreignLock.Enter(ref lockTaken);
234
235                             // If we encountered a race condition, bail.
236                             if (m_array[i & m_mask] == null)
237                                 return false;
238
239                             // Otherwise, null out the element.
240                             Volatile.Write(ref m_array[i & m_mask], null);
241
242                             // And then check to see if we can fix up the indexes (if we're at
243                             // the edge).  If we can't, we just leave nulls in the array and they'll
244                             // get filtered out eventually (but may lead to superflous resizing).
245                             if (i == m_tailIndex)
246                                 m_tailIndex -= 1;
247                             else if (i == m_headIndex)
248                                 m_headIndex += 1;
249
250                             return true;
251                         }
252                         finally
253                         {
254                             if (lockTaken)
255                                 m_foreignLock.Exit(useMemoryBarrier: false);
256                         }
257                     }
258                 }
259
260                 return false;
261             }
262
263             public IThreadPoolWorkItem LocalPop() => m_headIndex < m_tailIndex ? LocalPopCore() : null;
264
265             [SuppressMessage("Microsoft.Concurrency", "CA8001", Justification = "Reviewed for thread safety")]
266             private IThreadPoolWorkItem LocalPopCore()
267             {
268                 while (true)
269                 {
270                     int tail = m_tailIndex;
271                     if (m_headIndex >= tail)
272                     {
273                         return null;
274                     }
275
276                     // Decrement the tail using a fence to ensure subsequent read doesn't come before.
277                     tail -= 1;
278                     Interlocked.Exchange(ref m_tailIndex, tail);
279
280                     // If there is no interaction with a take, we can head down the fast path.
281                     if (m_headIndex <= tail)
282                     {
283                         int idx = tail & m_mask;
284                         IThreadPoolWorkItem obj = Volatile.Read(ref m_array[idx]);
285
286                         // Check for nulls in the array.
287                         if (obj == null) continue;
288
289                         m_array[idx] = null;
290                         return obj;
291                     }
292                     else
293                     {
294                         // Interaction with takes: 0 or 1 elements left.
295                         bool lockTaken = false;
296                         try
297                         {
298                             m_foreignLock.Enter(ref lockTaken);
299
300                             if (m_headIndex <= tail)
301                             {
302                                 // Element still available. Take it.
303                                 int idx = tail & m_mask;
304                                 IThreadPoolWorkItem obj = Volatile.Read(ref m_array[idx]);
305
306                                 // Check for nulls in the array.
307                                 if (obj == null) continue;
308
309                                 m_array[idx] = null;
310                                 return obj;
311                             }
312                             else
313                             {
314                                 // If we encountered a race condition and element was stolen, restore the tail.
315                                 m_tailIndex = tail + 1;
316                                 return null;
317                             }
318                         }
319                         finally
320                         {
321                             if (lockTaken)
322                                 m_foreignLock.Exit(useMemoryBarrier: false);
323                         }
324                     }
325                 }
326             }
327
328             public bool CanSteal => m_headIndex < m_tailIndex;
329
330             public IThreadPoolWorkItem TrySteal(ref bool missedSteal)
331             {
332                 while (true)
333                 {
334                     if (CanSteal)
335                     {
336                         bool taken = false;
337                         try
338                         {
339                             m_foreignLock.TryEnter(ref taken);
340                             if (taken)
341                             {
342                                 // Increment head, and ensure read of tail doesn't move before it (fence).
343                                 int head = m_headIndex;
344                                 Interlocked.Exchange(ref m_headIndex, head + 1);
345
346                                 if (head < m_tailIndex)
347                                 {
348                                     int idx = head & m_mask;
349                                     IThreadPoolWorkItem obj = Volatile.Read(ref m_array[idx]);
350
351                                     // Check for nulls in the array.
352                                     if (obj == null) continue;
353
354                                     m_array[idx] = null;
355                                     return obj;
356                                 }
357                                 else
358                                 {
359                                     // Failed, restore head.
360                                     m_headIndex = head;
361                                 }
362                             }
363                         }
364                         finally
365                         {
366                             if (taken)
367                                 m_foreignLock.Exit(useMemoryBarrier: false);
368                         }
369
370                         missedSteal = true;
371                     }
372
373                     return null;
374                 }
375             }
376         }
377
378         internal bool loggingEnabled;
379         internal readonly ConcurrentQueue<IThreadPoolWorkItem> workItems = new ConcurrentQueue<IThreadPoolWorkItem>();
380
381         private Internal.PaddingFor32 pad1;
382
383         private volatile int numOutstandingThreadRequests = 0;
384
385         private Internal.PaddingFor32 pad2;
386
387         public ThreadPoolWorkQueue()
388         {
389             loggingEnabled = FrameworkEventSource.Log.IsEnabled(EventLevel.Verbose, FrameworkEventSource.Keywords.ThreadPool | FrameworkEventSource.Keywords.ThreadTransfer);
390         }
391
392         public ThreadPoolWorkQueueThreadLocals EnsureCurrentThreadHasQueue() =>
393             ThreadPoolWorkQueueThreadLocals.threadLocals ??
394             (ThreadPoolWorkQueueThreadLocals.threadLocals = new ThreadPoolWorkQueueThreadLocals(this));
395
396         internal void EnsureThreadRequested()
397         {
398             //
399             // If we have not yet requested #procs threads from the VM, then request a new thread
400             // as needed
401             //
402             // Note that there is a separate count in the VM which will also be incremented in this case, 
403             // which is handled by RequestWorkerThread.
404             //
405             int count = numOutstandingThreadRequests;
406             while (count < ThreadPoolGlobals.processorCount)
407             {
408                 int prev = Interlocked.CompareExchange(ref numOutstandingThreadRequests, count + 1, count);
409                 if (prev == count)
410                 {
411                     ThreadPool.RequestWorkerThread();
412                     break;
413                 }
414                 count = prev;
415             }
416         }
417
418         internal void MarkThreadRequestSatisfied()
419         {
420             //
421             // The VM has called us, so one of our outstanding thread requests has been satisfied.
422             // Decrement the count so that future calls to EnsureThreadRequested will succeed.
423             // Note that there is a separate count in the VM which has already been decremented by the VM
424             // by the time we reach this point.
425             //
426             int count = numOutstandingThreadRequests;
427             while (count > 0)
428             {
429                 int prev = Interlocked.CompareExchange(ref numOutstandingThreadRequests, count - 1, count);
430                 if (prev == count)
431                 {
432                     break;
433                 }
434                 count = prev;
435             }
436         }
437
438         public void Enqueue(IThreadPoolWorkItem callback, bool forceGlobal)
439         {
440             if (loggingEnabled)
441                 System.Diagnostics.Tracing.FrameworkEventSource.Log.ThreadPoolEnqueueWorkObject(callback);
442
443             ThreadPoolWorkQueueThreadLocals tl = null;
444             if (!forceGlobal)
445                 tl = ThreadPoolWorkQueueThreadLocals.threadLocals;
446
447             if (null != tl)
448             {
449                 tl.workStealingQueue.LocalPush(callback);
450             }
451             else
452             {
453                 workItems.Enqueue(callback);
454             }
455
456             EnsureThreadRequested();
457         }
458
459         internal bool LocalFindAndPop(IThreadPoolWorkItem callback)
460         {
461             ThreadPoolWorkQueueThreadLocals tl = ThreadPoolWorkQueueThreadLocals.threadLocals;
462             return tl != null && tl.workStealingQueue.LocalFindAndPop(callback);
463         }
464
465         public IThreadPoolWorkItem Dequeue(ThreadPoolWorkQueueThreadLocals tl, ref bool missedSteal)
466         {
467             WorkStealingQueue localWsq = tl.workStealingQueue;
468             IThreadPoolWorkItem callback;
469
470             if ((callback = localWsq.LocalPop()) == null && // first try the local queue
471                 !workItems.TryDequeue(out callback)) // then try the global queue
472             {
473                 // finally try to steal from another thread's local queue
474                 WorkStealingQueue[] queues = WorkStealingQueueList.Queues;
475                 int c = queues.Length;
476                 Debug.Assert(c > 0, "There must at least be a queue for this thread.");
477                 int maxIndex = c - 1;
478                 int i = tl.random.Next(c);
479                 while (c > 0)
480                 {
481                     i = (i < maxIndex) ? i + 1 : 0;
482                     WorkStealingQueue otherQueue = queues[i];
483                     if (otherQueue != localWsq && otherQueue.CanSteal)
484                     {
485                         callback = otherQueue.TrySteal(ref missedSteal);
486                         if (callback != null)
487                         {
488                             break;
489                         }
490                     }
491                     c--;
492                 }
493             }
494
495             return callback;
496         }
497
498         internal static bool Dispatch()
499         {
500             var workQueue = ThreadPoolGlobals.workQueue;
501             //
502             // The clock is ticking!  We have ThreadPoolGlobals.TP_QUANTUM milliseconds to get some work done, and then
503             // we need to return to the VM.
504             //
505             int quantumStartTime = Environment.TickCount;
506
507             //
508             // Update our records to indicate that an outstanding request for a thread has now been fulfilled.
509             // From this point on, we are responsible for requesting another thread if we stop working for any
510             // reason, and we believe there might still be work in the queue.
511             //
512             // Note that if this thread is aborted before we get a chance to request another one, the VM will
513             // record a thread request on our behalf.  So we don't need to worry about getting aborted right here.
514             //
515             workQueue.MarkThreadRequestSatisfied();
516
517             // Has the desire for logging changed since the last time we entered?
518             workQueue.loggingEnabled = FrameworkEventSource.Log.IsEnabled(EventLevel.Verbose, FrameworkEventSource.Keywords.ThreadPool | FrameworkEventSource.Keywords.ThreadTransfer);
519
520             //
521             // Assume that we're going to need another thread if this one returns to the VM.  We'll set this to 
522             // false later, but only if we're absolutely certain that the queue is empty.
523             //
524             bool needAnotherThread = true;
525             IThreadPoolWorkItem workItem = null;
526             try
527             {
528                 //
529                 // Set up our thread-local data
530                 //
531                 ThreadPoolWorkQueueThreadLocals tl = workQueue.EnsureCurrentThreadHasQueue();
532
533                 //
534                 // Loop until our quantum expires.
535                 //
536                 while ((Environment.TickCount - quantumStartTime) < ThreadPoolGlobals.TP_QUANTUM)
537                 {
538                     bool missedSteal = false;
539                     workItem = workQueue.Dequeue(tl, ref missedSteal);
540
541                     if (workItem == null)
542                     {
543                         //
544                         // No work.  We're going to return to the VM once we leave this protected region.
545                         // If we missed a steal, though, there may be more work in the queue.
546                         // Instead of looping around and trying again, we'll just request another thread.  This way
547                         // we won't starve other AppDomains while we spin trying to get locks, and hopefully the thread
548                         // that owns the contended work-stealing queue will pick up its own workitems in the meantime, 
549                         // which will be more efficient than this thread doing it anyway.
550                         //
551                         needAnotherThread = missedSteal;
552
553                         // Tell the VM we're returning normally, not because Hill Climbing asked us to return.
554                         return true;
555                     }
556
557                     if (workQueue.loggingEnabled)
558                         System.Diagnostics.Tracing.FrameworkEventSource.Log.ThreadPoolDequeueWorkObject(workItem);
559
560                     //
561                     // If we found work, there may be more work.  Ask for another thread so that the other work can be processed
562                     // in parallel.  Note that this will only ask for a max of #procs threads, so it's safe to call it for every dequeue.
563                     //
564                     workQueue.EnsureThreadRequested();
565
566                     //
567                     // Execute the workitem outside of any finally blocks, so that it can be aborted if needed.
568                     //
569                     if (ThreadPoolGlobals.enableWorkerTracking)
570                     {
571                         bool reportedStatus = false;
572                         try
573                         {
574                             ThreadPool.ReportThreadStatus(isWorking: true);
575                             reportedStatus = true;
576                             workItem.ExecuteWorkItem();
577                         }
578                         finally
579                         {
580                             if (reportedStatus)
581                                 ThreadPool.ReportThreadStatus(isWorking: false);
582                         }
583                     }
584                     else
585                     {
586                         workItem.ExecuteWorkItem();
587                     }
588                     workItem = null;
589
590                     // 
591                     // Notify the VM that we executed this workitem.  This is also our opportunity to ask whether Hill Climbing wants
592                     // us to return the thread to the pool or not.
593                     //
594                     if (!ThreadPool.NotifyWorkItemComplete())
595                         return false;
596                 }
597
598                 // If we get here, it's because our quantum expired.  Tell the VM we're returning normally.
599                 return true;
600             }
601             catch (ThreadAbortException tae)
602             {
603                 //
604                 // This is here to catch the case where this thread is aborted between the time we exit the finally block in the dispatch
605                 // loop, and the time we execute the work item.  QueueUserWorkItemCallback uses this to update its accounting of whether
606                 // it was executed or not (in debug builds only).  Task uses this to communicate the ThreadAbortException to anyone
607                 // who waits for the task to complete.
608                 //
609                 workItem?.MarkAborted(tae);
610
611                 //
612                 // In this case, the VM is going to request another thread on our behalf.  No need to do it twice.
613                 //
614                 needAnotherThread = false;
615                 // throw;  //no need to explicitly rethrow a ThreadAbortException, and doing so causes allocations on amd64.
616             }
617             finally
618             {
619                 //
620                 // If we are exiting for any reason other than that the queue is definitely empty, ask for another
621                 // thread to pick up where we left off.
622                 //
623                 if (needAnotherThread)
624                     workQueue.EnsureThreadRequested();
625             }
626
627             // we can never reach this point, but the C# compiler doesn't know that, because it doesn't know the ThreadAbortException will be reraised above.
628             Debug.Fail("Should never reach this point");
629             return true;
630         }
631     }
632
633     // Simple random number generator. We don't need great randomness, we just need a little and for it to be fast.
634     internal struct FastRandom // xorshift prng
635     {
636         private uint _w, _x, _y, _z;
637
638         public FastRandom(int seed)
639         {
640             _x = (uint)seed;
641             _w = 88675123;
642             _y = 362436069;
643             _z = 521288629;
644         }
645
646         public int Next(int maxValue)
647         {
648             Debug.Assert(maxValue > 0);
649
650             uint t = _x ^ (_x << 11);
651             _x = _y; _y = _z; _z = _w;
652             _w = _w ^ (_w >> 19) ^ (t ^ (t >> 8));
653
654             return (int)(_w % (uint)maxValue);
655         }
656     }
657
658     // Holds a WorkStealingQueue, and remmoves it from the list when this object is no longer referened.
659     internal sealed class ThreadPoolWorkQueueThreadLocals
660     {
661         [ThreadStatic]
662         public static ThreadPoolWorkQueueThreadLocals threadLocals;
663
664         public readonly ThreadPoolWorkQueue workQueue;
665         public readonly ThreadPoolWorkQueue.WorkStealingQueue workStealingQueue;
666         public FastRandom random = new FastRandom(Thread.CurrentThread.ManagedThreadId); // mutable struct, do not copy or make readonly
667
668         public ThreadPoolWorkQueueThreadLocals(ThreadPoolWorkQueue tpq)
669         {
670             workQueue = tpq;
671             workStealingQueue = new ThreadPoolWorkQueue.WorkStealingQueue();
672             ThreadPoolWorkQueue.WorkStealingQueueList.Add(workStealingQueue);
673         }
674
675         private void CleanUp()
676         {
677             if (null != workStealingQueue)
678             {
679                 if (null != workQueue)
680                 {
681                     IThreadPoolWorkItem cb;
682                     while ((cb = workStealingQueue.LocalPop()) != null)
683                     {
684                         Debug.Assert(null != cb);
685                         workQueue.Enqueue(cb, forceGlobal: true);
686                     }
687                 }
688
689                 ThreadPoolWorkQueue.WorkStealingQueueList.Remove(workStealingQueue);
690             }
691         }
692
693         ~ThreadPoolWorkQueueThreadLocals()
694         {
695             // Since the purpose of calling CleanUp is to transfer any pending workitems into the global
696             // queue so that they will be executed by another thread, there's no point in doing this cleanup
697             // if we're in the process of shutting down or unloading the AD.  In those cases, the work won't
698             // execute anyway.  And there are subtle race conditions involved there that would lead us to do the wrong
699             // thing anyway.  So we'll only clean up if this is a "normal" finalization.
700             if (!(Environment.HasShutdownStarted || AppDomain.CurrentDomain.IsFinalizingForUnload()))
701                 CleanUp();
702         }
703     }
704
705     internal sealed class RegisteredWaitHandleSafe : CriticalFinalizerObject
706     {
707         private static IntPtr InvalidHandle => Win32Native.INVALID_HANDLE_VALUE;
708         private IntPtr registeredWaitHandle = InvalidHandle;
709         private WaitHandle m_internalWaitObject;
710         private bool bReleaseNeeded = false;
711         private volatile int m_lock = 0;
712
713         internal IntPtr GetHandle() => registeredWaitHandle;
714
715         internal void SetHandle(IntPtr handle)
716         {
717             registeredWaitHandle = handle;
718         }
719
720         internal void SetWaitObject(WaitHandle waitObject)
721         {
722             // needed for DangerousAddRef
723             RuntimeHelpers.PrepareConstrainedRegions();
724
725             m_internalWaitObject = waitObject;
726             if (waitObject != null)
727             {
728                 m_internalWaitObject.SafeWaitHandle.DangerousAddRef(ref bReleaseNeeded);
729             }
730         }
731
732         internal bool Unregister(
733              WaitHandle waitObject          // object to be notified when all callbacks to delegates have completed
734              )
735         {
736             bool result = false;
737             // needed for DangerousRelease
738             RuntimeHelpers.PrepareConstrainedRegions();
739
740             // lock(this) cannot be used reliably in Cer since thin lock could be
741             // promoted to syncblock and that is not a guaranteed operation
742             bool bLockTaken = false;
743             do
744             {
745                 if (Interlocked.CompareExchange(ref m_lock, 1, 0) == 0)
746                 {
747                     bLockTaken = true;
748                     try
749                     {
750                         if (ValidHandle())
751                         {
752                             result = UnregisterWaitNative(GetHandle(), waitObject == null ? null : waitObject.SafeWaitHandle);
753                             if (result == true)
754                             {
755                                 if (bReleaseNeeded)
756                                 {
757                                     m_internalWaitObject.SafeWaitHandle.DangerousRelease();
758                                     bReleaseNeeded = false;
759                                 }
760                                 // if result not true don't release/suppress here so finalizer can make another attempt
761                                 SetHandle(InvalidHandle);
762                                 m_internalWaitObject = null;
763                                 GC.SuppressFinalize(this);
764                             }
765                         }
766                     }
767                     finally
768                     {
769                         m_lock = 0;
770                     }
771                 }
772                 Thread.SpinWait(1);     // yield to processor
773             }
774             while (!bLockTaken);
775
776             return result;
777         }
778
779         private bool ValidHandle() =>
780             registeredWaitHandle != InvalidHandle && registeredWaitHandle != IntPtr.Zero;
781
782         ~RegisteredWaitHandleSafe()
783         {
784             // if the app has already unregistered the wait, there is nothing to cleanup
785             // we can detect this by checking the handle. Normally, there is no race condition here
786             // so no need to protect reading of handle. However, if this object gets 
787             // resurrected and then someone does an unregister, it would introduce a race condition
788             //
789             // PrepareConstrainedRegions call not needed since finalizer already in Cer
790             //
791             // lock(this) cannot be used reliably even in Cer since thin lock could be
792             // promoted to syncblock and that is not a guaranteed operation
793             //
794             // Note that we will not "spin" to get this lock.  We make only a single attempt;
795             // if we can't get the lock, it means some other thread is in the middle of a call
796             // to Unregister, which will do the work of the finalizer anyway.
797             //
798             // Further, it's actually critical that we *not* wait for the lock here, because
799             // the other thread that's in the middle of Unregister may be suspended for shutdown.
800             // Then, during the live-object finalization phase of shutdown, this thread would
801             // end up spinning forever, as the other thread would never release the lock.
802             // This will result in a "leak" of sorts (since the handle will not be cleaned up)
803             // but the process is exiting anyway.
804             //
805             // During AD-unload, we don�t finalize live objects until all threads have been 
806             // aborted out of the AD.  Since these locked regions are CERs, we won�t abort them 
807             // while the lock is held.  So there should be no leak on AD-unload.
808             //
809             if (Interlocked.CompareExchange(ref m_lock, 1, 0) == 0)
810             {
811                 try
812                 {
813                     if (ValidHandle())
814                     {
815                         WaitHandleCleanupNative(registeredWaitHandle);
816                         if (bReleaseNeeded)
817                         {
818                             m_internalWaitObject.SafeWaitHandle.DangerousRelease();
819                             bReleaseNeeded = false;
820                         }
821                         SetHandle(InvalidHandle);
822                         m_internalWaitObject = null;
823                     }
824                 }
825                 finally
826                 {
827                     m_lock = 0;
828                 }
829             }
830         }
831
832         [MethodImplAttribute(MethodImplOptions.InternalCall)]
833         private static extern void WaitHandleCleanupNative(IntPtr handle);
834
835         [MethodImplAttribute(MethodImplOptions.InternalCall)]
836         private static extern bool UnregisterWaitNative(IntPtr handle, SafeHandle waitObject);
837     }
838
839     public sealed class RegisteredWaitHandle : MarshalByRefObject
840     {
841         private readonly RegisteredWaitHandleSafe internalRegisteredWait;
842
843         internal RegisteredWaitHandle()
844         {
845             internalRegisteredWait = new RegisteredWaitHandleSafe();
846         }
847
848         internal void SetHandle(IntPtr handle)
849         {
850             internalRegisteredWait.SetHandle(handle);
851         }
852
853         internal void SetWaitObject(WaitHandle waitObject)
854         {
855             internalRegisteredWait.SetWaitObject(waitObject);
856         }
857
858         // This is the only public method on this class
859         public bool Unregister(
860              WaitHandle waitObject          // object to be notified when all callbacks to delegates have completed
861              )
862         {
863             return internalRegisteredWait.Unregister(waitObject);
864         }
865     }
866
867     public delegate void WaitCallback(Object state);
868
869     public delegate void WaitOrTimerCallback(Object state, bool timedOut);  // signalled or timed out
870
871     //
872     // This type is necessary because VS 2010's debugger looks for a method named _ThreadPoolWaitCallbacck.PerformWaitCallback
873     // on the stack to determine if a thread is a ThreadPool thread or not.  We have a better way to do this for .NET 4.5, but
874     // still need to maintain compatibility with VS 2010.  When compat with VS 2010 is no longer an issue, this type may be
875     // removed.
876     //
877     internal static class _ThreadPoolWaitCallback
878     {
879         internal static bool PerformWaitCallback() => ThreadPoolWorkQueue.Dispatch();
880     }
881
882     //
883     // Interface to something that can be queued to the TP.  This is implemented by 
884     // QueueUserWorkItemCallback, Task, and potentially other internal types.
885     // For example, SemaphoreSlim represents callbacks using its own type that
886     // implements IThreadPoolWorkItem.
887     //
888     // If we decide to expose some of the workstealing
889     // stuff, this is NOT the thing we want to expose to the public.
890     //
891     internal interface IThreadPoolWorkItem
892     {
893         void ExecuteWorkItem();
894         void MarkAborted(ThreadAbortException tae);
895     }
896
897     internal sealed class QueueUserWorkItemCallback : IThreadPoolWorkItem
898     {
899         private WaitCallback callback;
900         private readonly ExecutionContext context;
901         private readonly Object state;
902
903 #if DEBUG
904         private volatile int executed;
905
906         ~QueueUserWorkItemCallback()
907         {
908             Debug.Assert(
909                 executed != 0 || Environment.HasShutdownStarted || AppDomain.CurrentDomain.IsFinalizingForUnload(),
910                 "A QueueUserWorkItemCallback was never called!");
911         }
912
913         private void MarkExecuted(bool aborted)
914         {
915             GC.SuppressFinalize(this);
916             Debug.Assert(
917                 0 == Interlocked.Exchange(ref executed, 1) || aborted,
918                 "A QueueUserWorkItemCallback was called twice!");
919         }
920 #endif
921
922         internal QueueUserWorkItemCallback(WaitCallback waitCallback, Object stateObj, ExecutionContext ec)
923         {
924             callback = waitCallback;
925             state = stateObj;
926             context = ec;
927         }
928
929         void IThreadPoolWorkItem.ExecuteWorkItem()
930         {
931 #if DEBUG
932             MarkExecuted(aborted: false);
933 #endif
934             // call directly if it is an unsafe call OR EC flow is suppressed
935             if (context == null)
936             {
937                 WaitCallback cb = callback;
938                 callback = null;
939                 cb(state);
940             }
941             else
942             {
943                 ExecutionContext.Run(context, ccb, this);
944             }
945         }
946
947         void IThreadPoolWorkItem.MarkAborted(ThreadAbortException tae)
948         {
949 #if DEBUG
950             // this workitem didn't execute because we got a ThreadAbortException prior to the call to ExecuteWorkItem.  
951             // This counts as being executed for our purposes.
952             MarkExecuted(aborted: true);
953 #endif
954         }
955
956         internal static readonly ContextCallback ccb = new ContextCallback(WaitCallback_Context);
957
958         private static void WaitCallback_Context(Object state)
959         {
960             QueueUserWorkItemCallback obj = (QueueUserWorkItemCallback)state;
961             WaitCallback wc = obj.callback;
962             Debug.Assert(null != wc);
963             wc(obj.state);
964         }
965     }
966
967     internal sealed class QueueUserWorkItemCallbackDefaultContext : IThreadPoolWorkItem
968     {
969         private WaitCallback callback;
970         private readonly Object state;
971
972 #if DEBUG
973         private volatile int executed;
974
975         ~QueueUserWorkItemCallbackDefaultContext()
976         {
977             Debug.Assert(
978                 executed != 0 || Environment.HasShutdownStarted || AppDomain.CurrentDomain.IsFinalizingForUnload(),
979                 "A QueueUserWorkItemCallbackDefaultContext was never called!");
980         }
981
982         private void MarkExecuted(bool aborted)
983         {
984             GC.SuppressFinalize(this);
985             Debug.Assert(
986                 0 == Interlocked.Exchange(ref executed, 1) || aborted,
987                 "A QueueUserWorkItemCallbackDefaultContext was called twice!");
988         }
989 #endif
990
991         internal QueueUserWorkItemCallbackDefaultContext(WaitCallback waitCallback, Object stateObj)
992         {
993             callback = waitCallback;
994             state = stateObj;
995         }
996
997         void IThreadPoolWorkItem.ExecuteWorkItem()
998         {
999 #if DEBUG
1000             MarkExecuted(aborted: false);
1001 #endif
1002             ExecutionContext.Run(ExecutionContext.Default, ccb, this);
1003         }
1004
1005         void IThreadPoolWorkItem.MarkAborted(ThreadAbortException tae)
1006         {
1007 #if DEBUG
1008             // this workitem didn't execute because we got a ThreadAbortException prior to the call to ExecuteWorkItem.  
1009             // This counts as being executed for our purposes.
1010             MarkExecuted(aborted: true);
1011 #endif
1012         }
1013
1014         internal static readonly ContextCallback ccb = new ContextCallback(WaitCallback_Context);
1015
1016         private static void WaitCallback_Context(Object state)
1017         {
1018             QueueUserWorkItemCallbackDefaultContext obj = (QueueUserWorkItemCallbackDefaultContext)state;
1019             WaitCallback wc = obj.callback;
1020             Debug.Assert(null != wc);
1021             obj.callback = null;
1022             wc(obj.state);
1023         }
1024     }
1025
1026     internal class _ThreadPoolWaitOrTimerCallback
1027     {
1028         private WaitOrTimerCallback _waitOrTimerCallback;
1029         private ExecutionContext _executionContext;
1030         private Object _state;
1031         private static readonly ContextCallback _ccbt = new ContextCallback(WaitOrTimerCallback_Context_t);
1032         private static readonly ContextCallback _ccbf = new ContextCallback(WaitOrTimerCallback_Context_f);
1033
1034         internal _ThreadPoolWaitOrTimerCallback(WaitOrTimerCallback waitOrTimerCallback, Object state, bool compressStack)
1035         {
1036             _waitOrTimerCallback = waitOrTimerCallback;
1037             _state = state;
1038
1039             if (compressStack)
1040             {
1041                 // capture the exection context
1042                 _executionContext = ExecutionContext.Capture();
1043             }
1044         }
1045
1046         private static void WaitOrTimerCallback_Context_t(Object state) =>
1047             WaitOrTimerCallback_Context(state, timedOut: true);
1048
1049         private static void WaitOrTimerCallback_Context_f(Object state) =>
1050             WaitOrTimerCallback_Context(state, timedOut: false);
1051
1052         private static void WaitOrTimerCallback_Context(Object state, bool timedOut)
1053         {
1054             _ThreadPoolWaitOrTimerCallback helper = (_ThreadPoolWaitOrTimerCallback)state;
1055             helper._waitOrTimerCallback(helper._state, timedOut);
1056         }
1057
1058         // call back helper
1059         internal static void PerformWaitOrTimerCallback(Object state, bool timedOut)
1060         {
1061             _ThreadPoolWaitOrTimerCallback helper = (_ThreadPoolWaitOrTimerCallback)state;
1062             Debug.Assert(helper != null, "Null state passed to PerformWaitOrTimerCallback!");
1063             // call directly if it is an unsafe call OR EC flow is suppressed
1064             if (helper._executionContext == null)
1065             {
1066                 WaitOrTimerCallback callback = helper._waitOrTimerCallback;
1067                 callback(helper._state, timedOut);
1068             }
1069             else
1070             {
1071                 ExecutionContext.Run(helper._executionContext, timedOut ? _ccbt : _ccbf, helper);
1072             }
1073         }
1074     }
1075
1076     [CLSCompliant(false)]
1077     unsafe public delegate void IOCompletionCallback(uint errorCode, // Error code
1078                                        uint numBytes, // No. of bytes transferred 
1079                                        NativeOverlapped* pOVERLAP // ptr to OVERLAP structure
1080                                        );
1081
1082     public static class ThreadPool
1083     {
1084         public static bool SetMaxThreads(int workerThreads, int completionPortThreads)
1085         {
1086             return SetMaxThreadsNative(workerThreads, completionPortThreads);
1087         }
1088
1089         public static void GetMaxThreads(out int workerThreads, out int completionPortThreads)
1090         {
1091             GetMaxThreadsNative(out workerThreads, out completionPortThreads);
1092         }
1093
1094         public static bool SetMinThreads(int workerThreads, int completionPortThreads)
1095         {
1096             return SetMinThreadsNative(workerThreads, completionPortThreads);
1097         }
1098
1099         public static void GetMinThreads(out int workerThreads, out int completionPortThreads)
1100         {
1101             GetMinThreadsNative(out workerThreads, out completionPortThreads);
1102         }
1103
1104         public static void GetAvailableThreads(out int workerThreads, out int completionPortThreads)
1105         {
1106             GetAvailableThreadsNative(out workerThreads, out completionPortThreads);
1107         }
1108
1109         [CLSCompliant(false)]
1110         [System.Security.DynamicSecurityMethod] // Methods containing StackCrawlMark local var has to be marked DynamicSecurityMethod            
1111         public static RegisteredWaitHandle RegisterWaitForSingleObject(  // throws RegisterWaitException
1112              WaitHandle waitObject,
1113              WaitOrTimerCallback callBack,
1114              Object state,
1115              uint millisecondsTimeOutInterval,
1116              bool executeOnlyOnce    // NOTE: we do not allow other options that allow the callback to be queued as an APC
1117              )
1118         {
1119             StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller;
1120             return RegisterWaitForSingleObject(waitObject, callBack, state, millisecondsTimeOutInterval, executeOnlyOnce, ref stackMark, true);
1121         }
1122
1123         [CLSCompliant(false)]
1124         [System.Security.DynamicSecurityMethod] // Methods containing StackCrawlMark local var has to be marked DynamicSecurityMethod
1125         public static RegisteredWaitHandle UnsafeRegisterWaitForSingleObject(  // throws RegisterWaitException
1126              WaitHandle waitObject,
1127              WaitOrTimerCallback callBack,
1128              Object state,
1129              uint millisecondsTimeOutInterval,
1130              bool executeOnlyOnce    // NOTE: we do not allow other options that allow the callback to be queued as an APC
1131              )
1132         {
1133             StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller;
1134             return RegisterWaitForSingleObject(waitObject, callBack, state, millisecondsTimeOutInterval, executeOnlyOnce, ref stackMark, false);
1135         }
1136
1137
1138         private static RegisteredWaitHandle RegisterWaitForSingleObject(  // throws RegisterWaitException
1139              WaitHandle waitObject,
1140              WaitOrTimerCallback callBack,
1141              Object state,
1142              uint millisecondsTimeOutInterval,
1143              bool executeOnlyOnce,   // NOTE: we do not allow other options that allow the callback to be queued as an APC
1144              ref StackCrawlMark stackMark,
1145              bool compressStack
1146              )
1147         {
1148             RegisteredWaitHandle registeredWaitHandle = new RegisteredWaitHandle();
1149
1150             if (callBack != null)
1151             {
1152                 _ThreadPoolWaitOrTimerCallback callBackHelper = new _ThreadPoolWaitOrTimerCallback(callBack, state, compressStack);
1153                 state = (Object)callBackHelper;
1154                 // call SetWaitObject before native call so that waitObject won't be closed before threadpoolmgr registration
1155                 // this could occur if callback were to fire before SetWaitObject does its addref
1156                 registeredWaitHandle.SetWaitObject(waitObject);
1157                 IntPtr nativeRegisteredWaitHandle = RegisterWaitForSingleObjectNative(waitObject,
1158                                                                                state,
1159                                                                                millisecondsTimeOutInterval,
1160                                                                                executeOnlyOnce,
1161                                                                                registeredWaitHandle,
1162                                                                                ref stackMark,
1163                                                                                compressStack);
1164                 registeredWaitHandle.SetHandle(nativeRegisteredWaitHandle);
1165             }
1166             else
1167             {
1168                 throw new ArgumentNullException(nameof(WaitOrTimerCallback));
1169             }
1170             return registeredWaitHandle;
1171         }
1172
1173
1174         [System.Security.DynamicSecurityMethod] // Methods containing StackCrawlMark local var has to be marked DynamicSecurityMethod
1175         public static RegisteredWaitHandle RegisterWaitForSingleObject(  // throws RegisterWaitException
1176              WaitHandle waitObject,
1177              WaitOrTimerCallback callBack,
1178              Object state,
1179              int millisecondsTimeOutInterval,
1180              bool executeOnlyOnce    // NOTE: we do not allow other options that allow the callback to be queued as an APC
1181              )
1182         {
1183             if (millisecondsTimeOutInterval < -1)
1184                 throw new ArgumentOutOfRangeException(nameof(millisecondsTimeOutInterval), SR.ArgumentOutOfRange_NeedNonNegOrNegative1);
1185             StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller;
1186             return RegisterWaitForSingleObject(waitObject, callBack, state, (UInt32)millisecondsTimeOutInterval, executeOnlyOnce, ref stackMark, true);
1187         }
1188
1189         [System.Security.DynamicSecurityMethod] // Methods containing StackCrawlMark local var has to be marked DynamicSecurityMethod            
1190         public static RegisteredWaitHandle UnsafeRegisterWaitForSingleObject(  // throws RegisterWaitException
1191              WaitHandle waitObject,
1192              WaitOrTimerCallback callBack,
1193              Object state,
1194              int millisecondsTimeOutInterval,
1195              bool executeOnlyOnce    // NOTE: we do not allow other options that allow the callback to be queued as an APC
1196              )
1197         {
1198             if (millisecondsTimeOutInterval < -1)
1199                 throw new ArgumentOutOfRangeException(nameof(millisecondsTimeOutInterval), SR.ArgumentOutOfRange_NeedNonNegOrNegative1);
1200             StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller;
1201             return RegisterWaitForSingleObject(waitObject, callBack, state, (UInt32)millisecondsTimeOutInterval, executeOnlyOnce, ref stackMark, false);
1202         }
1203
1204         [System.Security.DynamicSecurityMethod] // Methods containing StackCrawlMark local var has to be marked DynamicSecurityMethod
1205         public static RegisteredWaitHandle RegisterWaitForSingleObject(  // throws RegisterWaitException
1206             WaitHandle waitObject,
1207             WaitOrTimerCallback callBack,
1208             Object state,
1209             long millisecondsTimeOutInterval,
1210             bool executeOnlyOnce    // NOTE: we do not allow other options that allow the callback to be queued as an APC
1211         )
1212         {
1213             if (millisecondsTimeOutInterval < -1)
1214                 throw new ArgumentOutOfRangeException(nameof(millisecondsTimeOutInterval), SR.ArgumentOutOfRange_NeedNonNegOrNegative1);
1215             StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller;
1216             return RegisterWaitForSingleObject(waitObject, callBack, state, (UInt32)millisecondsTimeOutInterval, executeOnlyOnce, ref stackMark, true);
1217         }
1218
1219         [System.Security.DynamicSecurityMethod] // Methods containing StackCrawlMark local var has to be marked DynamicSecurityMethod
1220         public static RegisteredWaitHandle UnsafeRegisterWaitForSingleObject(  // throws RegisterWaitException
1221             WaitHandle waitObject,
1222             WaitOrTimerCallback callBack,
1223             Object state,
1224             long millisecondsTimeOutInterval,
1225             bool executeOnlyOnce    // NOTE: we do not allow other options that allow the callback to be queued as an APC
1226         )
1227         {
1228             if (millisecondsTimeOutInterval < -1)
1229                 throw new ArgumentOutOfRangeException(nameof(millisecondsTimeOutInterval), SR.ArgumentOutOfRange_NeedNonNegOrNegative1);
1230             StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller;
1231             return RegisterWaitForSingleObject(waitObject, callBack, state, (UInt32)millisecondsTimeOutInterval, executeOnlyOnce, ref stackMark, false);
1232         }
1233
1234         [System.Security.DynamicSecurityMethod] // Methods containing StackCrawlMark local var has to be marked DynamicSecurityMethod
1235         public static RegisteredWaitHandle RegisterWaitForSingleObject(
1236                           WaitHandle waitObject,
1237                           WaitOrTimerCallback callBack,
1238                           Object state,
1239                           TimeSpan timeout,
1240                           bool executeOnlyOnce
1241                           )
1242         {
1243             long tm = (long)timeout.TotalMilliseconds;
1244             if (tm < -1)
1245                 throw new ArgumentOutOfRangeException(nameof(timeout), SR.ArgumentOutOfRange_NeedNonNegOrNegative1);
1246             if (tm > (long)Int32.MaxValue)
1247                 throw new ArgumentOutOfRangeException(nameof(timeout), SR.ArgumentOutOfRange_LessEqualToIntegerMaxVal);
1248             StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller;
1249             return RegisterWaitForSingleObject(waitObject, callBack, state, (UInt32)tm, executeOnlyOnce, ref stackMark, true);
1250         }
1251
1252         [System.Security.DynamicSecurityMethod] // Methods containing StackCrawlMark local var has to be marked DynamicSecurityMethod
1253         public static RegisteredWaitHandle UnsafeRegisterWaitForSingleObject(
1254                           WaitHandle waitObject,
1255                           WaitOrTimerCallback callBack,
1256                           Object state,
1257                           TimeSpan timeout,
1258                           bool executeOnlyOnce
1259                           )
1260         {
1261             long tm = (long)timeout.TotalMilliseconds;
1262             if (tm < -1)
1263                 throw new ArgumentOutOfRangeException(nameof(timeout), SR.ArgumentOutOfRange_NeedNonNegOrNegative1);
1264             if (tm > (long)Int32.MaxValue)
1265                 throw new ArgumentOutOfRangeException(nameof(timeout), SR.ArgumentOutOfRange_LessEqualToIntegerMaxVal);
1266             StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller;
1267             return RegisterWaitForSingleObject(waitObject, callBack, state, (UInt32)tm, executeOnlyOnce, ref stackMark, false);
1268         }
1269
1270         public static bool QueueUserWorkItem(WaitCallback callBack) =>
1271             QueueUserWorkItem(callBack, null, preferLocal: false);
1272
1273         public static bool QueueUserWorkItem(WaitCallback callBack, object state) =>
1274             QueueUserWorkItem(callBack, state, preferLocal: false);
1275
1276         public static bool QueueUserWorkItem(WaitCallback callBack, object state, bool preferLocal)
1277         {
1278             if (callBack == null)
1279             {
1280                 ThrowHelper.ThrowArgumentNullException(ExceptionArgument.callBack);
1281             }
1282
1283             EnsureVMInitialized();
1284
1285             ExecutionContext context = ExecutionContext.Capture();
1286
1287             IThreadPoolWorkItem tpcallBack = context == ExecutionContext.Default ?
1288                 new QueueUserWorkItemCallbackDefaultContext(callBack, state) :
1289                 (IThreadPoolWorkItem)new QueueUserWorkItemCallback(callBack, state, context);
1290
1291             ThreadPoolGlobals.workQueue.Enqueue(tpcallBack, forceGlobal: !preferLocal);
1292
1293             return true;
1294         }
1295
1296         public static bool UnsafeQueueUserWorkItem(WaitCallback callBack, Object state)
1297         {
1298             if (callBack == null)
1299             {
1300                 ThrowHelper.ThrowArgumentNullException(ExceptionArgument.callBack);
1301             }
1302
1303             EnsureVMInitialized();
1304
1305             IThreadPoolWorkItem tpcallBack = new QueueUserWorkItemCallback(callBack, state, null);
1306
1307             ThreadPoolGlobals.workQueue.Enqueue(tpcallBack, forceGlobal: true);
1308
1309             return true;
1310         }
1311
1312         internal static void UnsafeQueueCustomWorkItem(IThreadPoolWorkItem workItem, bool forceGlobal)
1313         {
1314             Debug.Assert(null != workItem);
1315             EnsureVMInitialized();
1316             ThreadPoolGlobals.workQueue.Enqueue(workItem, forceGlobal);
1317         }
1318
1319         // This method tries to take the target callback out of the current thread's queue.
1320         internal static bool TryPopCustomWorkItem(IThreadPoolWorkItem workItem)
1321         {
1322             Debug.Assert(null != workItem);
1323             return
1324                 ThreadPoolGlobals.vmTpInitialized && // if not initialized, so there's no way this workitem was ever queued.
1325                 ThreadPoolGlobals.workQueue.LocalFindAndPop(workItem);
1326         }
1327
1328         // Get all workitems.  Called by TaskScheduler in its debugger hooks.
1329         internal static IEnumerable<IThreadPoolWorkItem> GetQueuedWorkItems()
1330         {
1331             // Enumerate global queue
1332             foreach (IThreadPoolWorkItem workItem in ThreadPoolGlobals.workQueue.workItems)
1333             {
1334                 yield return workItem;
1335             }
1336
1337             // Enumerate each local queue
1338             foreach (ThreadPoolWorkQueue.WorkStealingQueue wsq in ThreadPoolWorkQueue.WorkStealingQueueList.Queues)
1339             {
1340                 if (wsq != null && wsq.m_array != null)
1341                 {
1342                     IThreadPoolWorkItem[] items = wsq.m_array;
1343                     for (int i = 0; i < items.Length; i++)
1344                     {
1345                         IThreadPoolWorkItem item = items[i];
1346                         if (item != null)
1347                         {
1348                             yield return item;
1349                         }
1350                     }
1351                 }
1352             }
1353         }
1354
1355         internal static IEnumerable<IThreadPoolWorkItem> GetLocallyQueuedWorkItems()
1356         {
1357             ThreadPoolWorkQueue.WorkStealingQueue wsq = ThreadPoolWorkQueueThreadLocals.threadLocals.workStealingQueue;
1358             if (wsq != null && wsq.m_array != null)
1359             {
1360                 IThreadPoolWorkItem[] items = wsq.m_array;
1361                 for (int i = 0; i < items.Length; i++)
1362                 {
1363                     IThreadPoolWorkItem item = items[i];
1364                     if (item != null)
1365                         yield return item;
1366                 }
1367             }
1368         }
1369
1370         internal static IEnumerable<IThreadPoolWorkItem> GetGloballyQueuedWorkItems() => ThreadPoolGlobals.workQueue.workItems;
1371
1372         private static object[] ToObjectArray(IEnumerable<IThreadPoolWorkItem> workitems)
1373         {
1374             int i = 0;
1375             foreach (IThreadPoolWorkItem item in workitems)
1376             {
1377                 i++;
1378             }
1379
1380             object[] result = new object[i];
1381             i = 0;
1382             foreach (IThreadPoolWorkItem item in workitems)
1383             {
1384                 if (i < result.Length) //just in case someone calls us while the queues are in motion
1385                     result[i] = item;
1386                 i++;
1387             }
1388
1389             return result;
1390         }
1391
1392         // This is the method the debugger will actually call, if it ends up calling
1393         // into ThreadPool directly.  Tests can use this to simulate a debugger, as well.
1394         internal static object[] GetQueuedWorkItemsForDebugger() =>
1395             ToObjectArray(GetQueuedWorkItems());
1396
1397         internal static object[] GetGloballyQueuedWorkItemsForDebugger() =>
1398             ToObjectArray(GetGloballyQueuedWorkItems());
1399
1400         internal static object[] GetLocallyQueuedWorkItemsForDebugger() =>
1401             ToObjectArray(GetLocallyQueuedWorkItems());
1402
1403         [DllImport(JitHelpers.QCall, CharSet = CharSet.Unicode)]
1404         internal static extern bool RequestWorkerThread();
1405
1406         [MethodImplAttribute(MethodImplOptions.InternalCall)]
1407         unsafe private static extern bool PostQueuedCompletionStatus(NativeOverlapped* overlapped);
1408
1409         [CLSCompliant(false)]
1410         unsafe public static bool UnsafeQueueNativeOverlapped(NativeOverlapped* overlapped) =>
1411             PostQueuedCompletionStatus(overlapped);
1412
1413         // The thread pool maintains a per-appdomain managed work queue.
1414         // New thread pool entries are added in the managed queue.
1415         // The VM is responsible for the actual growing/shrinking of 
1416         // threads. 
1417         private static void EnsureVMInitialized()
1418         {
1419             if (!ThreadPoolGlobals.vmTpInitialized)
1420             {
1421                 EnsureVMInitializedCore(); // separate out to help with inlining
1422             }
1423         }
1424
1425         private static void EnsureVMInitializedCore()
1426         {
1427             ThreadPool.InitializeVMTp(ref ThreadPoolGlobals.enableWorkerTracking);
1428             ThreadPoolGlobals.vmTpInitialized = true;
1429         }
1430
1431         // Native methods: 
1432
1433         [MethodImplAttribute(MethodImplOptions.InternalCall)]
1434         private static extern bool SetMinThreadsNative(int workerThreads, int completionPortThreads);
1435
1436         [MethodImplAttribute(MethodImplOptions.InternalCall)]
1437         private static extern bool SetMaxThreadsNative(int workerThreads, int completionPortThreads);
1438
1439         [MethodImplAttribute(MethodImplOptions.InternalCall)]
1440         private static extern void GetMinThreadsNative(out int workerThreads, out int completionPortThreads);
1441
1442         [MethodImplAttribute(MethodImplOptions.InternalCall)]
1443         private static extern void GetMaxThreadsNative(out int workerThreads, out int completionPortThreads);
1444
1445         [MethodImplAttribute(MethodImplOptions.InternalCall)]
1446         private static extern void GetAvailableThreadsNative(out int workerThreads, out int completionPortThreads);
1447
1448         [MethodImplAttribute(MethodImplOptions.InternalCall)]
1449         internal static extern bool NotifyWorkItemComplete();
1450
1451         [MethodImplAttribute(MethodImplOptions.InternalCall)]
1452         internal static extern void ReportThreadStatus(bool isWorking);
1453
1454         internal static void NotifyWorkItemProgress()
1455         {
1456             if (!ThreadPoolGlobals.vmTpInitialized)
1457                 ThreadPool.InitializeVMTp(ref ThreadPoolGlobals.enableWorkerTracking);
1458             NotifyWorkItemProgressNative();
1459         }
1460
1461         [MethodImplAttribute(MethodImplOptions.InternalCall)]
1462         internal static extern void NotifyWorkItemProgressNative();
1463
1464         [DllImport(JitHelpers.QCall, CharSet = CharSet.Unicode)]
1465         private static extern void InitializeVMTp(ref bool enableWorkerTracking);
1466
1467         [MethodImplAttribute(MethodImplOptions.InternalCall)]
1468         private static extern IntPtr RegisterWaitForSingleObjectNative(
1469              WaitHandle waitHandle,
1470              Object state,
1471              uint timeOutInterval,
1472              bool executeOnlyOnce,
1473              RegisteredWaitHandle registeredWaitHandle,
1474              ref StackCrawlMark stackMark,
1475              bool compressStack
1476              );
1477
1478
1479         [Obsolete("ThreadPool.BindHandle(IntPtr) has been deprecated.  Please use ThreadPool.BindHandle(SafeHandle) instead.", false)]
1480         public static bool BindHandle(IntPtr osHandle)
1481         {
1482             return BindIOCompletionCallbackNative(osHandle);
1483         }
1484
1485         public static bool BindHandle(SafeHandle osHandle)
1486         {
1487             if (osHandle == null)
1488                 throw new ArgumentNullException(nameof(osHandle));
1489
1490             bool ret = false;
1491             bool mustReleaseSafeHandle = false;
1492             RuntimeHelpers.PrepareConstrainedRegions();
1493             try
1494             {
1495                 osHandle.DangerousAddRef(ref mustReleaseSafeHandle);
1496                 ret = BindIOCompletionCallbackNative(osHandle.DangerousGetHandle());
1497             }
1498             finally
1499             {
1500                 if (mustReleaseSafeHandle)
1501                     osHandle.DangerousRelease();
1502             }
1503             return ret;
1504         }
1505
1506         [MethodImplAttribute(MethodImplOptions.InternalCall)]
1507         private static extern bool BindIOCompletionCallbackNative(IntPtr fileHandle);
1508     }
1509 }