Reduce Execution Context Save+Restore (#15629)
[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             ExecutionContext context = _context;
936             if (context == null)
937             {
938                 WaitCallback cb = _callback;
939                 _callback = null;
940                 cb(_state);
941             }
942             else
943             {
944                 ExecutionContext.RunInternal(context, ccb, this);
945             }
946         }
947
948         void IThreadPoolWorkItem.MarkAborted(ThreadAbortException tae)
949         {
950 #if DEBUG
951             // this workitem didn't execute because we got a ThreadAbortException prior to the call to ExecuteWorkItem.  
952             // This counts as being executed for our purposes.
953             MarkExecuted(aborted: true);
954 #endif
955         }
956
957         internal static readonly ContextCallback ccb = new ContextCallback(WaitCallback_Context);
958
959         private static void WaitCallback_Context(Object state)
960         {
961             QueueUserWorkItemCallback obj = (QueueUserWorkItemCallback)state;
962             WaitCallback wc = obj._callback;
963             Debug.Assert(null != wc);
964             wc(obj._state);
965         }
966     }
967
968     internal sealed class QueueUserWorkItemCallbackDefaultContext : IThreadPoolWorkItem
969     {
970         private WaitCallback callback;
971         private readonly Object state;
972
973 #if DEBUG
974         private volatile int executed;
975
976         ~QueueUserWorkItemCallbackDefaultContext()
977         {
978             Debug.Assert(
979                 executed != 0 || Environment.HasShutdownStarted || AppDomain.CurrentDomain.IsFinalizingForUnload(),
980                 "A QueueUserWorkItemCallbackDefaultContext was never called!");
981         }
982
983         private void MarkExecuted(bool aborted)
984         {
985             GC.SuppressFinalize(this);
986             Debug.Assert(
987                 0 == Interlocked.Exchange(ref executed, 1) || aborted,
988                 "A QueueUserWorkItemCallbackDefaultContext was called twice!");
989         }
990 #endif
991
992         internal QueueUserWorkItemCallbackDefaultContext(WaitCallback waitCallback, Object stateObj)
993         {
994             callback = waitCallback;
995             state = stateObj;
996         }
997
998         void IThreadPoolWorkItem.ExecuteWorkItem()
999         {
1000 #if DEBUG
1001             MarkExecuted(aborted: false);
1002 #endif
1003             // null executionContext on RunInternal is Default context
1004             ExecutionContext.RunInternal(executionContext: null, ccb, this);
1005         }
1006
1007         void IThreadPoolWorkItem.MarkAborted(ThreadAbortException tae)
1008         {
1009 #if DEBUG
1010             // this workitem didn't execute because we got a ThreadAbortException prior to the call to ExecuteWorkItem.  
1011             // This counts as being executed for our purposes.
1012             MarkExecuted(aborted: true);
1013 #endif
1014         }
1015
1016         internal static readonly ContextCallback ccb = new ContextCallback(WaitCallback_Context);
1017
1018         private static void WaitCallback_Context(Object state)
1019         {
1020             QueueUserWorkItemCallbackDefaultContext obj = (QueueUserWorkItemCallbackDefaultContext)state;
1021             WaitCallback wc = obj.callback;
1022             Debug.Assert(null != wc);
1023             obj.callback = null;
1024             wc(obj.state);
1025         }
1026     }
1027
1028     internal class _ThreadPoolWaitOrTimerCallback
1029     {
1030         private WaitOrTimerCallback _waitOrTimerCallback;
1031         private ExecutionContext _executionContext;
1032         private Object _state;
1033         private static readonly ContextCallback _ccbt = new ContextCallback(WaitOrTimerCallback_Context_t);
1034         private static readonly ContextCallback _ccbf = new ContextCallback(WaitOrTimerCallback_Context_f);
1035
1036         internal _ThreadPoolWaitOrTimerCallback(WaitOrTimerCallback waitOrTimerCallback, Object state, bool compressStack)
1037         {
1038             _waitOrTimerCallback = waitOrTimerCallback;
1039             _state = state;
1040
1041             if (compressStack)
1042             {
1043                 // capture the exection context
1044                 _executionContext = ExecutionContext.Capture();
1045             }
1046         }
1047
1048         private static void WaitOrTimerCallback_Context_t(Object state) =>
1049             WaitOrTimerCallback_Context(state, timedOut: true);
1050
1051         private static void WaitOrTimerCallback_Context_f(Object state) =>
1052             WaitOrTimerCallback_Context(state, timedOut: false);
1053
1054         private static void WaitOrTimerCallback_Context(Object state, bool timedOut)
1055         {
1056             _ThreadPoolWaitOrTimerCallback helper = (_ThreadPoolWaitOrTimerCallback)state;
1057             helper._waitOrTimerCallback(helper._state, timedOut);
1058         }
1059
1060         // call back helper
1061         internal static void PerformWaitOrTimerCallback(Object state, bool timedOut)
1062         {
1063             _ThreadPoolWaitOrTimerCallback helper = (_ThreadPoolWaitOrTimerCallback)state;
1064             Debug.Assert(helper != null, "Null state passed to PerformWaitOrTimerCallback!");
1065             // call directly if it is an unsafe call OR EC flow is suppressed
1066             ExecutionContext context = helper._executionContext;
1067             if (context == null)
1068             {
1069                 WaitOrTimerCallback callback = helper._waitOrTimerCallback;
1070                 callback(helper._state, timedOut);
1071             }
1072             else
1073             {
1074                 ExecutionContext.Run(context, timedOut ? _ccbt : _ccbf, helper);
1075             }
1076         }
1077     }
1078
1079     [CLSCompliant(false)]
1080     unsafe public delegate void IOCompletionCallback(uint errorCode, // Error code
1081                                        uint numBytes, // No. of bytes transferred 
1082                                        NativeOverlapped* pOVERLAP // ptr to OVERLAP structure
1083                                        );
1084
1085     public static class ThreadPool
1086     {
1087         public static bool SetMaxThreads(int workerThreads, int completionPortThreads)
1088         {
1089             return SetMaxThreadsNative(workerThreads, completionPortThreads);
1090         }
1091
1092         public static void GetMaxThreads(out int workerThreads, out int completionPortThreads)
1093         {
1094             GetMaxThreadsNative(out workerThreads, out completionPortThreads);
1095         }
1096
1097         public static bool SetMinThreads(int workerThreads, int completionPortThreads)
1098         {
1099             return SetMinThreadsNative(workerThreads, completionPortThreads);
1100         }
1101
1102         public static void GetMinThreads(out int workerThreads, out int completionPortThreads)
1103         {
1104             GetMinThreadsNative(out workerThreads, out completionPortThreads);
1105         }
1106
1107         public static void GetAvailableThreads(out int workerThreads, out int completionPortThreads)
1108         {
1109             GetAvailableThreadsNative(out workerThreads, out completionPortThreads);
1110         }
1111
1112         [CLSCompliant(false)]
1113         [System.Security.DynamicSecurityMethod] // Methods containing StackCrawlMark local var has to be marked DynamicSecurityMethod            
1114         public static RegisteredWaitHandle RegisterWaitForSingleObject(  // throws RegisterWaitException
1115              WaitHandle waitObject,
1116              WaitOrTimerCallback callBack,
1117              Object state,
1118              uint millisecondsTimeOutInterval,
1119              bool executeOnlyOnce    // NOTE: we do not allow other options that allow the callback to be queued as an APC
1120              )
1121         {
1122             StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller;
1123             return RegisterWaitForSingleObject(waitObject, callBack, state, millisecondsTimeOutInterval, executeOnlyOnce, ref stackMark, true);
1124         }
1125
1126         [CLSCompliant(false)]
1127         [System.Security.DynamicSecurityMethod] // Methods containing StackCrawlMark local var has to be marked DynamicSecurityMethod
1128         public static RegisteredWaitHandle UnsafeRegisterWaitForSingleObject(  // throws RegisterWaitException
1129              WaitHandle waitObject,
1130              WaitOrTimerCallback callBack,
1131              Object state,
1132              uint millisecondsTimeOutInterval,
1133              bool executeOnlyOnce    // NOTE: we do not allow other options that allow the callback to be queued as an APC
1134              )
1135         {
1136             StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller;
1137             return RegisterWaitForSingleObject(waitObject, callBack, state, millisecondsTimeOutInterval, executeOnlyOnce, ref stackMark, false);
1138         }
1139
1140
1141         private static RegisteredWaitHandle RegisterWaitForSingleObject(  // throws RegisterWaitException
1142              WaitHandle waitObject,
1143              WaitOrTimerCallback callBack,
1144              Object state,
1145              uint millisecondsTimeOutInterval,
1146              bool executeOnlyOnce,   // NOTE: we do not allow other options that allow the callback to be queued as an APC
1147              ref StackCrawlMark stackMark,
1148              bool compressStack
1149              )
1150         {
1151             RegisteredWaitHandle registeredWaitHandle = new RegisteredWaitHandle();
1152
1153             if (callBack != null)
1154             {
1155                 _ThreadPoolWaitOrTimerCallback callBackHelper = new _ThreadPoolWaitOrTimerCallback(callBack, state, compressStack);
1156                 state = (Object)callBackHelper;
1157                 // call SetWaitObject before native call so that waitObject won't be closed before threadpoolmgr registration
1158                 // this could occur if callback were to fire before SetWaitObject does its addref
1159                 registeredWaitHandle.SetWaitObject(waitObject);
1160                 IntPtr nativeRegisteredWaitHandle = RegisterWaitForSingleObjectNative(waitObject,
1161                                                                                state,
1162                                                                                millisecondsTimeOutInterval,
1163                                                                                executeOnlyOnce,
1164                                                                                registeredWaitHandle,
1165                                                                                ref stackMark,
1166                                                                                compressStack);
1167                 registeredWaitHandle.SetHandle(nativeRegisteredWaitHandle);
1168             }
1169             else
1170             {
1171                 throw new ArgumentNullException(nameof(WaitOrTimerCallback));
1172             }
1173             return registeredWaitHandle;
1174         }
1175
1176
1177         [System.Security.DynamicSecurityMethod] // Methods containing StackCrawlMark local var has to be marked DynamicSecurityMethod
1178         public static RegisteredWaitHandle RegisterWaitForSingleObject(  // throws RegisterWaitException
1179              WaitHandle waitObject,
1180              WaitOrTimerCallback callBack,
1181              Object state,
1182              int millisecondsTimeOutInterval,
1183              bool executeOnlyOnce    // NOTE: we do not allow other options that allow the callback to be queued as an APC
1184              )
1185         {
1186             if (millisecondsTimeOutInterval < -1)
1187                 throw new ArgumentOutOfRangeException(nameof(millisecondsTimeOutInterval), SR.ArgumentOutOfRange_NeedNonNegOrNegative1);
1188             StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller;
1189             return RegisterWaitForSingleObject(waitObject, callBack, state, (UInt32)millisecondsTimeOutInterval, executeOnlyOnce, ref stackMark, true);
1190         }
1191
1192         [System.Security.DynamicSecurityMethod] // Methods containing StackCrawlMark local var has to be marked DynamicSecurityMethod            
1193         public static RegisteredWaitHandle UnsafeRegisterWaitForSingleObject(  // throws RegisterWaitException
1194              WaitHandle waitObject,
1195              WaitOrTimerCallback callBack,
1196              Object state,
1197              int millisecondsTimeOutInterval,
1198              bool executeOnlyOnce    // NOTE: we do not allow other options that allow the callback to be queued as an APC
1199              )
1200         {
1201             if (millisecondsTimeOutInterval < -1)
1202                 throw new ArgumentOutOfRangeException(nameof(millisecondsTimeOutInterval), SR.ArgumentOutOfRange_NeedNonNegOrNegative1);
1203             StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller;
1204             return RegisterWaitForSingleObject(waitObject, callBack, state, (UInt32)millisecondsTimeOutInterval, executeOnlyOnce, ref stackMark, false);
1205         }
1206
1207         [System.Security.DynamicSecurityMethod] // Methods containing StackCrawlMark local var has to be marked DynamicSecurityMethod
1208         public static RegisteredWaitHandle RegisterWaitForSingleObject(  // throws RegisterWaitException
1209             WaitHandle waitObject,
1210             WaitOrTimerCallback callBack,
1211             Object state,
1212             long millisecondsTimeOutInterval,
1213             bool executeOnlyOnce    // NOTE: we do not allow other options that allow the callback to be queued as an APC
1214         )
1215         {
1216             if (millisecondsTimeOutInterval < -1)
1217                 throw new ArgumentOutOfRangeException(nameof(millisecondsTimeOutInterval), SR.ArgumentOutOfRange_NeedNonNegOrNegative1);
1218             StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller;
1219             return RegisterWaitForSingleObject(waitObject, callBack, state, (UInt32)millisecondsTimeOutInterval, executeOnlyOnce, ref stackMark, true);
1220         }
1221
1222         [System.Security.DynamicSecurityMethod] // Methods containing StackCrawlMark local var has to be marked DynamicSecurityMethod
1223         public static RegisteredWaitHandle UnsafeRegisterWaitForSingleObject(  // throws RegisterWaitException
1224             WaitHandle waitObject,
1225             WaitOrTimerCallback callBack,
1226             Object state,
1227             long millisecondsTimeOutInterval,
1228             bool executeOnlyOnce    // NOTE: we do not allow other options that allow the callback to be queued as an APC
1229         )
1230         {
1231             if (millisecondsTimeOutInterval < -1)
1232                 throw new ArgumentOutOfRangeException(nameof(millisecondsTimeOutInterval), SR.ArgumentOutOfRange_NeedNonNegOrNegative1);
1233             StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller;
1234             return RegisterWaitForSingleObject(waitObject, callBack, state, (UInt32)millisecondsTimeOutInterval, executeOnlyOnce, ref stackMark, false);
1235         }
1236
1237         [System.Security.DynamicSecurityMethod] // Methods containing StackCrawlMark local var has to be marked DynamicSecurityMethod
1238         public static RegisteredWaitHandle RegisterWaitForSingleObject(
1239                           WaitHandle waitObject,
1240                           WaitOrTimerCallback callBack,
1241                           Object state,
1242                           TimeSpan timeout,
1243                           bool executeOnlyOnce
1244                           )
1245         {
1246             long tm = (long)timeout.TotalMilliseconds;
1247             if (tm < -1)
1248                 throw new ArgumentOutOfRangeException(nameof(timeout), SR.ArgumentOutOfRange_NeedNonNegOrNegative1);
1249             if (tm > (long)Int32.MaxValue)
1250                 throw new ArgumentOutOfRangeException(nameof(timeout), SR.ArgumentOutOfRange_LessEqualToIntegerMaxVal);
1251             StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller;
1252             return RegisterWaitForSingleObject(waitObject, callBack, state, (UInt32)tm, executeOnlyOnce, ref stackMark, true);
1253         }
1254
1255         [System.Security.DynamicSecurityMethod] // Methods containing StackCrawlMark local var has to be marked DynamicSecurityMethod
1256         public static RegisteredWaitHandle UnsafeRegisterWaitForSingleObject(
1257                           WaitHandle waitObject,
1258                           WaitOrTimerCallback callBack,
1259                           Object state,
1260                           TimeSpan timeout,
1261                           bool executeOnlyOnce
1262                           )
1263         {
1264             long tm = (long)timeout.TotalMilliseconds;
1265             if (tm < -1)
1266                 throw new ArgumentOutOfRangeException(nameof(timeout), SR.ArgumentOutOfRange_NeedNonNegOrNegative1);
1267             if (tm > (long)Int32.MaxValue)
1268                 throw new ArgumentOutOfRangeException(nameof(timeout), SR.ArgumentOutOfRange_LessEqualToIntegerMaxVal);
1269             StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller;
1270             return RegisterWaitForSingleObject(waitObject, callBack, state, (UInt32)tm, executeOnlyOnce, ref stackMark, false);
1271         }
1272
1273         public static bool QueueUserWorkItem(WaitCallback callBack) =>
1274             QueueUserWorkItem(callBack, null, preferLocal: false);
1275
1276         public static bool QueueUserWorkItem(WaitCallback callBack, object state) =>
1277             QueueUserWorkItem(callBack, state, preferLocal: false);
1278
1279         public static bool QueueUserWorkItem(WaitCallback callBack, object state, bool preferLocal)
1280         {
1281             if (callBack == null)
1282             {
1283                 ThrowHelper.ThrowArgumentNullException(ExceptionArgument.callBack);
1284             }
1285
1286             EnsureVMInitialized();
1287
1288             ExecutionContext context = ExecutionContext.Capture();
1289
1290             IThreadPoolWorkItem tpcallBack = (context != null && context.IsDefault) ?
1291                 new QueueUserWorkItemCallbackDefaultContext(callBack, state) :
1292                 (IThreadPoolWorkItem)new QueueUserWorkItemCallback(callBack, state, context);
1293
1294             ThreadPoolGlobals.workQueue.Enqueue(tpcallBack, forceGlobal: !preferLocal);
1295
1296             return true;
1297         }
1298
1299         public static bool UnsafeQueueUserWorkItem(WaitCallback callBack, Object state)
1300         {
1301             if (callBack == null)
1302             {
1303                 ThrowHelper.ThrowArgumentNullException(ExceptionArgument.callBack);
1304             }
1305
1306             EnsureVMInitialized();
1307
1308             IThreadPoolWorkItem tpcallBack = new QueueUserWorkItemCallback(callBack, state, null);
1309
1310             ThreadPoolGlobals.workQueue.Enqueue(tpcallBack, forceGlobal: true);
1311
1312             return true;
1313         }
1314
1315         internal static void UnsafeQueueCustomWorkItem(IThreadPoolWorkItem workItem, bool forceGlobal)
1316         {
1317             Debug.Assert(null != workItem);
1318             EnsureVMInitialized();
1319             ThreadPoolGlobals.workQueue.Enqueue(workItem, forceGlobal);
1320         }
1321
1322         // This method tries to take the target callback out of the current thread's queue.
1323         internal static bool TryPopCustomWorkItem(IThreadPoolWorkItem workItem)
1324         {
1325             Debug.Assert(null != workItem);
1326             return
1327                 ThreadPoolGlobals.vmTpInitialized && // if not initialized, so there's no way this workitem was ever queued.
1328                 ThreadPoolGlobals.workQueue.LocalFindAndPop(workItem);
1329         }
1330
1331         // Get all workitems.  Called by TaskScheduler in its debugger hooks.
1332         internal static IEnumerable<IThreadPoolWorkItem> GetQueuedWorkItems()
1333         {
1334             // Enumerate global queue
1335             foreach (IThreadPoolWorkItem workItem in ThreadPoolGlobals.workQueue.workItems)
1336             {
1337                 yield return workItem;
1338             }
1339
1340             // Enumerate each local queue
1341             foreach (ThreadPoolWorkQueue.WorkStealingQueue wsq in ThreadPoolWorkQueue.WorkStealingQueueList.Queues)
1342             {
1343                 if (wsq != null && wsq.m_array != null)
1344                 {
1345                     IThreadPoolWorkItem[] items = wsq.m_array;
1346                     for (int i = 0; i < items.Length; i++)
1347                     {
1348                         IThreadPoolWorkItem item = items[i];
1349                         if (item != null)
1350                         {
1351                             yield return item;
1352                         }
1353                     }
1354                 }
1355             }
1356         }
1357
1358         internal static IEnumerable<IThreadPoolWorkItem> GetLocallyQueuedWorkItems()
1359         {
1360             ThreadPoolWorkQueue.WorkStealingQueue wsq = ThreadPoolWorkQueueThreadLocals.threadLocals.workStealingQueue;
1361             if (wsq != null && wsq.m_array != null)
1362             {
1363                 IThreadPoolWorkItem[] items = wsq.m_array;
1364                 for (int i = 0; i < items.Length; i++)
1365                 {
1366                     IThreadPoolWorkItem item = items[i];
1367                     if (item != null)
1368                         yield return item;
1369                 }
1370             }
1371         }
1372
1373         internal static IEnumerable<IThreadPoolWorkItem> GetGloballyQueuedWorkItems() => ThreadPoolGlobals.workQueue.workItems;
1374
1375         private static object[] ToObjectArray(IEnumerable<IThreadPoolWorkItem> workitems)
1376         {
1377             int i = 0;
1378             foreach (IThreadPoolWorkItem item in workitems)
1379             {
1380                 i++;
1381             }
1382
1383             object[] result = new object[i];
1384             i = 0;
1385             foreach (IThreadPoolWorkItem item in workitems)
1386             {
1387                 if (i < result.Length) //just in case someone calls us while the queues are in motion
1388                     result[i] = item;
1389                 i++;
1390             }
1391
1392             return result;
1393         }
1394
1395         // This is the method the debugger will actually call, if it ends up calling
1396         // into ThreadPool directly.  Tests can use this to simulate a debugger, as well.
1397         internal static object[] GetQueuedWorkItemsForDebugger() =>
1398             ToObjectArray(GetQueuedWorkItems());
1399
1400         internal static object[] GetGloballyQueuedWorkItemsForDebugger() =>
1401             ToObjectArray(GetGloballyQueuedWorkItems());
1402
1403         internal static object[] GetLocallyQueuedWorkItemsForDebugger() =>
1404             ToObjectArray(GetLocallyQueuedWorkItems());
1405
1406         [DllImport(JitHelpers.QCall, CharSet = CharSet.Unicode)]
1407         internal static extern bool RequestWorkerThread();
1408
1409         [MethodImplAttribute(MethodImplOptions.InternalCall)]
1410         unsafe private static extern bool PostQueuedCompletionStatus(NativeOverlapped* overlapped);
1411
1412         [CLSCompliant(false)]
1413         unsafe public static bool UnsafeQueueNativeOverlapped(NativeOverlapped* overlapped) =>
1414             PostQueuedCompletionStatus(overlapped);
1415
1416         // The thread pool maintains a per-appdomain managed work queue.
1417         // New thread pool entries are added in the managed queue.
1418         // The VM is responsible for the actual growing/shrinking of 
1419         // threads. 
1420         private static void EnsureVMInitialized()
1421         {
1422             if (!ThreadPoolGlobals.vmTpInitialized)
1423             {
1424                 EnsureVMInitializedCore(); // separate out to help with inlining
1425             }
1426         }
1427
1428         private static void EnsureVMInitializedCore()
1429         {
1430             ThreadPool.InitializeVMTp(ref ThreadPoolGlobals.enableWorkerTracking);
1431             ThreadPoolGlobals.vmTpInitialized = true;
1432         }
1433
1434         // Native methods: 
1435
1436         [MethodImplAttribute(MethodImplOptions.InternalCall)]
1437         private static extern bool SetMinThreadsNative(int workerThreads, int completionPortThreads);
1438
1439         [MethodImplAttribute(MethodImplOptions.InternalCall)]
1440         private static extern bool SetMaxThreadsNative(int workerThreads, int completionPortThreads);
1441
1442         [MethodImplAttribute(MethodImplOptions.InternalCall)]
1443         private static extern void GetMinThreadsNative(out int workerThreads, out int completionPortThreads);
1444
1445         [MethodImplAttribute(MethodImplOptions.InternalCall)]
1446         private static extern void GetMaxThreadsNative(out int workerThreads, out int completionPortThreads);
1447
1448         [MethodImplAttribute(MethodImplOptions.InternalCall)]
1449         private static extern void GetAvailableThreadsNative(out int workerThreads, out int completionPortThreads);
1450
1451         [MethodImplAttribute(MethodImplOptions.InternalCall)]
1452         internal static extern bool NotifyWorkItemComplete();
1453
1454         [MethodImplAttribute(MethodImplOptions.InternalCall)]
1455         internal static extern void ReportThreadStatus(bool isWorking);
1456
1457         internal static void NotifyWorkItemProgress()
1458         {
1459             if (!ThreadPoolGlobals.vmTpInitialized)
1460                 ThreadPool.InitializeVMTp(ref ThreadPoolGlobals.enableWorkerTracking);
1461             NotifyWorkItemProgressNative();
1462         }
1463
1464         [MethodImplAttribute(MethodImplOptions.InternalCall)]
1465         internal static extern void NotifyWorkItemProgressNative();
1466
1467         [DllImport(JitHelpers.QCall, CharSet = CharSet.Unicode)]
1468         private static extern void InitializeVMTp(ref bool enableWorkerTracking);
1469
1470         [MethodImplAttribute(MethodImplOptions.InternalCall)]
1471         private static extern IntPtr RegisterWaitForSingleObjectNative(
1472              WaitHandle waitHandle,
1473              Object state,
1474              uint timeOutInterval,
1475              bool executeOnlyOnce,
1476              RegisteredWaitHandle registeredWaitHandle,
1477              ref StackCrawlMark stackMark,
1478              bool compressStack
1479              );
1480
1481
1482         [Obsolete("ThreadPool.BindHandle(IntPtr) has been deprecated.  Please use ThreadPool.BindHandle(SafeHandle) instead.", false)]
1483         public static bool BindHandle(IntPtr osHandle)
1484         {
1485             return BindIOCompletionCallbackNative(osHandle);
1486         }
1487
1488         public static bool BindHandle(SafeHandle osHandle)
1489         {
1490             if (osHandle == null)
1491                 throw new ArgumentNullException(nameof(osHandle));
1492
1493             bool ret = false;
1494             bool mustReleaseSafeHandle = false;
1495             RuntimeHelpers.PrepareConstrainedRegions();
1496             try
1497             {
1498                 osHandle.DangerousAddRef(ref mustReleaseSafeHandle);
1499                 ret = BindIOCompletionCallbackNative(osHandle.DangerousGetHandle());
1500             }
1501             finally
1502             {
1503                 if (mustReleaseSafeHandle)
1504                     osHandle.DangerousRelease();
1505             }
1506             return ret;
1507         }
1508
1509         [MethodImplAttribute(MethodImplOptions.InternalCall)]
1510         private static extern bool BindIOCompletionCallbackNative(IntPtr fileHandle);
1511     }
1512 }