851996e18d9894e5803542ab9ae7b8881e345ddd
[mono.git] / mcs / class / referencesource / mscorlib / system / threading / threadpool.cs
1 // ==++==
2 // 
3 //   Copyright (c) Microsoft Corporation.  All rights reserved.
4 // 
5 // ==--==
6 //
7 // <OWNER>[....]</OWNER>
8 /*=============================================================================
9 **
10 ** Class: ThreadPool
11 **
12 **
13 ** Purpose: Class for creating and managing a threadpool
14 **
15 **
16 =============================================================================*/
17
18 #pragma warning disable 0420
19
20 /*
21  * Below you'll notice two sets of APIs that are separated by the
22  * use of 'Unsafe' in their names.  The unsafe versions are called
23  * that because they do not propagate the calling stack onto the
24  * worker thread.  This allows code to lose the calling stack and 
25  * thereby elevate its security privileges.  Note that this operation
26  * is much akin to the combined ability to control security policy
27  * and control security evidence.  With these privileges, a person 
28  * can gain the right to load assemblies that are fully trusted which
29  * then assert full trust and can call any code they want regardless
30  * of the previous stack information.
31  */
32
33 namespace System.Threading
34 {
35     using System.Security;
36     using System.Runtime.Remoting;
37     using System.Security.Permissions;
38     using System;
39     using System.Runtime.CompilerServices;
40     using System.Runtime.ConstrainedExecution;
41     using System.Runtime.InteropServices;
42     using System.Runtime.Versioning;
43     using System.Collections.Generic;
44     using System.Diagnostics.Contracts;
45     using System.Diagnostics.CodeAnalysis;
46     using System.Diagnostics.Tracing;
47 #if !MONO
48     using Microsoft.Win32;
49 #endif
50
51     //
52     // Interface to something that can be queued to the TP.  This is implemented by
53     // QueueUserWorkItemCallback, Task, and potentially other internal types.
54     // For example, SemaphoreSlim represents callbacks using its own type that
55     // implements IThreadPoolWorkItem.
56     //
57     // If we decide to expose some of the workstealing
58     // stuff, this is NOT the thing we want to expose to the public.
59     //
60     internal interface IThreadPoolWorkItem
61     {
62         [SecurityCritical]
63         void ExecuteWorkItem();
64         [SecurityCritical]
65         void MarkAborted(ThreadAbortException tae);
66     }
67
68     [System.Runtime.InteropServices.ComVisible(true)]
69     public delegate void WaitCallback(Object state);
70
71     [System.Runtime.InteropServices.ComVisible(true)]
72     public delegate void WaitOrTimerCallback(Object state, bool timedOut);  // signalled or timed out
73
74     [System.Security.SecurityCritical]
75     [CLSCompliant(false)]
76     [System.Runtime.InteropServices.ComVisible(true)]
77     unsafe public delegate void IOCompletionCallback(uint errorCode, uint numBytes, NativeOverlapped* pOVERLAP);
78
79     internal static class ThreadPoolGlobals
80     {
81         //Per-appDomain quantum (in ms) for which the thread keeps processing
82         //requests in the current domain.
83         public static uint tpQuantum = 30U;
84
85         public static int processorCount = Environment.ProcessorCount;
86
87         public static bool tpHosted = ThreadPool.IsThreadPoolHosted(); 
88
89         public static volatile bool vmTpInitialized;
90         public static bool enableWorkerTracking;
91
92         [SecurityCritical]
93         public static ThreadPoolWorkQueue workQueue = new ThreadPoolWorkQueue();
94
95         [System.Security.SecuritySafeCritical] // static constructors should be safe to call
96         static ThreadPoolGlobals()
97         {
98         }
99     }
100
101     internal sealed class ThreadPoolWorkQueue
102     {
103         // Simple sparsely populated array to allow lock-free reading.
104         internal class SparseArray<T> where T : class
105         {
106             private volatile T[] m_array;
107
108             internal SparseArray(int initialSize)
109             {
110                 m_array = new T[initialSize];
111             }
112
113             internal T[] Current
114             {
115                 get { return m_array; }
116             }
117
118             internal int Add(T e)
119             {
120                 while (true)
121                 {
122                     T[] array = m_array;
123                     lock (array)
124                     {
125                         for (int i = 0; i < array.Length; i++)
126                         {
127                             if (array[i] == null)
128                             {
129                                 Volatile.Write(ref array[i], e);
130                                 return i;
131                             }
132                             else if (i == array.Length - 1)
133                             {
134                                 // Must resize. If we ----d and lost, we start over again.
135                                 if (array != m_array)
136                                     continue;
137
138                                 T[] newArray = new T[array.Length * 2];
139                                 Array.Copy(array, newArray, i + 1);
140                                 newArray[i + 1] = e;
141                                 m_array = newArray;
142                                 return i + 1;
143                             }
144                         }
145                     }
146                 }
147             }
148
149             internal void Remove(T e)
150             {
151                 T[] array = m_array;
152                 lock (array)
153                 {
154                     for (int i = 0; i < m_array.Length; i++)
155                     {
156                         if (m_array[i] == e)
157                         {
158                             Volatile.Write(ref m_array[i], null);
159                             break;
160                         }
161                     }
162                 }
163             }
164         }
165
166         internal class WorkStealingQueue
167         {
168             private const int INITIAL_SIZE = 32;
169             internal volatile IThreadPoolWorkItem[] m_array = new IThreadPoolWorkItem[INITIAL_SIZE];
170             private volatile int m_mask = INITIAL_SIZE - 1;
171
172 #if DEBUG
173             // in debug builds, start at the end so we exercise the index reset logic.
174             private const int START_INDEX = int.MaxValue;
175 #else
176             private const int START_INDEX = 0;
177 #endif
178
179             private volatile int m_headIndex = START_INDEX;
180             private volatile int m_tailIndex = START_INDEX;
181
182             private SpinLock m_foreignLock = new SpinLock(false);
183
184             public void LocalPush(IThreadPoolWorkItem obj)
185             {
186                 int tail = m_tailIndex;
187
188                 // We're going to increment the tail; if we'll overflow, then we need to reset our counts
189                 if (tail == int.MaxValue)
190                 {
191                     bool lockTaken = false;
192                     try
193                     {
194                         m_foreignLock.Enter(ref lockTaken);
195
196                         if (m_tailIndex == int.MaxValue)
197                         {
198                             //
199                             // Rather than resetting to zero, we'll just mask off the bits we don't care about.
200                             // This way we don't need to rearrange the items already in the queue; they'll be found
201                             // correctly exactly where they are.  One subtlety here is that we need to make sure that
202                             // if head is currently < tail, it remains that way.  This happens to just fall out from
203                             // the bit-masking, because we only do this if tail == int.MaxValue, meaning that all
204                             // bits are set, so all of the bits we're keeping will also be set.  Thus it's impossible
205                             // for the head to end up > than the tail, since you can't set any more bits than all of 
206                             // them.
207                             //
208                             m_headIndex = m_headIndex & m_mask;
209                             m_tailIndex = tail = m_tailIndex & m_mask;
210                             Contract.Assert(m_headIndex <= m_tailIndex);
211                         }
212                     }
213                     finally
214                     {
215                         if (lockTaken)
216                             m_foreignLock.Exit(true);
217                     }
218                 }
219
220                 // When there are at least 2 elements' worth of space, we can take the fast path.
221                 if (tail < m_headIndex + m_mask)
222                 {
223                     Volatile.Write(ref m_array[tail & m_mask], obj);
224                     m_tailIndex = tail + 1;
225                 }
226                 else
227                 {
228                     // We need to contend with foreign pops, so we lock.
229                     bool lockTaken = false;
230                     try
231                     {
232                         m_foreignLock.Enter(ref lockTaken);
233
234                         int head = m_headIndex;
235                         int count = m_tailIndex - m_headIndex;
236
237                         // If there is still space (one left), just add the element.
238                         if (count >= m_mask)
239                         {
240                             // We're full; expand the queue by doubling its size.
241                             IThreadPoolWorkItem[] newArray = new IThreadPoolWorkItem[m_array.Length << 1];
242                             for (int i = 0; i < m_array.Length; i++)
243                                 newArray[i] = m_array[(i + head) & m_mask];
244
245                             // Reset the field values, incl. the mask.
246                             m_array = newArray;
247                             m_headIndex = 0;
248                             m_tailIndex = tail = count;
249                             m_mask = (m_mask << 1) | 1;
250                         }
251
252                         Volatile.Write(ref m_array[tail & m_mask], obj);
253                         m_tailIndex = tail + 1;
254                     }
255                     finally
256                     {
257                         if (lockTaken)
258                             m_foreignLock.Exit(false);
259                     }
260                 }
261             }
262
263             [SuppressMessage("Microsoft.Concurrency", "CA8001", Justification = "Reviewed for thread safety")]
264             public bool LocalFindAndPop(IThreadPoolWorkItem obj)
265             {
266                 // Fast path: check the tail. If equal, we can skip the lock.
267                 if (m_array[(m_tailIndex - 1) & m_mask] == obj)
268                 {
269                     IThreadPoolWorkItem unused;
270                     if (LocalPop(out unused))
271                     {
272                         Contract.Assert(unused == obj);
273                         return true;
274                     }
275                     return false;
276                 }
277
278                 // Else, do an O(N) search for the work item. The theory of work stealing and our
279                 // inlining logic is that most waits will happen on recently queued work.  And
280                 // since recently queued work will be close to the tail end (which is where we
281                 // begin our search), we will likely find it quickly.  In the worst case, we
282                 // will traverse the whole local queue; this is typically not going to be a
283                 // problem (although degenerate cases are clearly an issue) because local work
284                 // queues tend to be somewhat shallow in length, and because if we fail to find
285                 // the work item, we are about to block anyway (which is very expensive).
286                 for (int i = m_tailIndex - 2; i >= m_headIndex; i--)
287                 {
288                     if (m_array[i & m_mask] == obj)
289                     {
290                         // If we found the element, block out steals to avoid interference.
291                         // @
292                         bool lockTaken = false;
293                         try
294                         {
295                             m_foreignLock.Enter(ref lockTaken);
296
297                             // If we lost the ----, bail.
298                             if (m_array[i & m_mask] == null)
299                                 return false;
300
301                             // Otherwise, null out the element.
302                             Volatile.Write(ref m_array[i & m_mask], null);
303
304                             // And then check to see if we can fix up the indexes (if we're at
305                             // the edge).  If we can't, we just leave nulls in the array and they'll
306                             // get filtered out eventually (but may lead to superflous resizing).
307                             if (i == m_tailIndex)
308                                 m_tailIndex -= 1;
309                             else if (i == m_headIndex)
310                                 m_headIndex += 1;
311
312                             return true;
313                         }
314                         finally
315                         {
316                             if (lockTaken)
317                                 m_foreignLock.Exit(false);
318                         }
319                     }
320                 }
321
322                 return false;
323             }
324
325             [SuppressMessage("Microsoft.Concurrency", "CA8001", Justification = "Reviewed for thread safety")]
326             public bool LocalPop(out IThreadPoolWorkItem obj)
327             {
328                 while (true)
329                 {
330                     // Decrement the tail using a fence to ensure subsequent read doesn't come before.
331                     int tail = m_tailIndex;
332                     if (m_headIndex >= tail)
333                     {
334                         obj = null;
335                         return false;
336                     }
337
338                     tail -= 1;
339                     Interlocked.Exchange(ref m_tailIndex, tail);
340
341                     // If there is no interaction with a take, we can head down the fast path.
342                     if (m_headIndex <= tail)
343                     {
344                         int idx = tail & m_mask;
345                         obj = Volatile.Read(ref m_array[idx]);
346
347                         // Check for nulls in the array.
348                         if (obj == null) continue;
349
350                         m_array[idx] = null;
351                         return true;
352                     }
353                     else
354                     {
355                         // Interaction with takes: 0 or 1 elements left.
356                         bool lockTaken = false;
357                         try
358                         {
359                             m_foreignLock.Enter(ref lockTaken);
360
361                             if (m_headIndex <= tail)
362                             {
363                                 // Element still available. Take it.
364                                 int idx = tail & m_mask;
365                                 obj = Volatile.Read(ref m_array[idx]);
366
367                                 // Check for nulls in the array.
368                                 if (obj == null) continue;
369
370                                 m_array[idx] = null;
371                                 return true;
372                             }
373                             else
374                             {
375                                 // We lost the ----, element was stolen, restore the tail.
376                                 m_tailIndex = tail + 1;
377                                 obj = null;
378                                 return false;
379                             }
380                         }
381                         finally
382                         {
383                             if (lockTaken)
384                                 m_foreignLock.Exit(false);
385                         }
386                     }
387                 }
388             }
389
390             public bool TrySteal(out IThreadPoolWorkItem obj, ref bool missedSteal)
391             {
392                 return TrySteal(out obj, ref missedSteal, 0); // no blocking by default.
393             }
394
395             private bool TrySteal(out IThreadPoolWorkItem obj, ref bool missedSteal, int millisecondsTimeout)
396             {
397                 obj = null;
398
399                 while (true)
400                 {
401                     if (m_headIndex >= m_tailIndex)
402                         return false;
403
404                     bool taken = false;
405                     try
406                     {
407                         m_foreignLock.TryEnter(millisecondsTimeout, ref taken);
408                         if (taken)
409                         {
410                             // Increment head, and ensure read of tail doesn't move before it (fence).
411                             int head = m_headIndex;
412                             Interlocked.Exchange(ref m_headIndex, head + 1);
413
414                             if (head < m_tailIndex)
415                             {
416                                 int idx = head & m_mask;
417                                 obj = Volatile.Read(ref m_array[idx]);
418
419                                 // Check for nulls in the array.
420                                 if (obj == null) continue;
421
422                                 m_array[idx] = null;
423                                 return true;
424                             }
425                             else
426                             {
427                                 // Failed, restore head.
428                                 m_headIndex = head;
429                                 obj = null;
430                                 missedSteal = true;
431                             }
432                         }
433                         else
434                         {
435                             missedSteal = true;
436                         }
437                     }
438                     finally
439                     {
440                         if (taken)
441                             m_foreignLock.Exit(false);
442                     }
443
444                     return false;
445                 }
446             }
447         }
448
449         internal class QueueSegment
450         {
451             // Holds a segment of the queue.  Enqueues/Dequeues start at element 0, and work their way up.
452             internal readonly IThreadPoolWorkItem[] nodes;
453             private const int QueueSegmentLength = 256;
454
455             // Holds the indexes of the lowest and highest valid elements of the nodes array.
456             // The low index is in the lower 16 bits, high index is in the upper 16 bits.
457             // Use GetIndexes and CompareExchangeIndexes to manipulate this.
458             private volatile int indexes;
459
460             // The next segment in the queue.
461             public volatile QueueSegment Next;
462
463
464             const int SixteenBits = 0xffff;
465
466             void GetIndexes(out int upper, out int lower)
467             {
468                 int i = indexes;
469                 upper = (i >> 16) & SixteenBits;
470                 lower = i & SixteenBits;
471
472                 Contract.Assert(upper >= lower);
473                 Contract.Assert(upper <= nodes.Length);
474                 Contract.Assert(lower <= nodes.Length);
475                 Contract.Assert(upper >= 0);
476                 Contract.Assert(lower >= 0);
477             }
478
479             bool CompareExchangeIndexes(ref int prevUpper, int newUpper, ref int prevLower, int newLower)
480             {
481                 Contract.Assert(newUpper >= newLower);
482                 Contract.Assert(newUpper <= nodes.Length);
483                 Contract.Assert(newLower <= nodes.Length);
484                 Contract.Assert(newUpper >= 0);
485                 Contract.Assert(newLower >= 0);
486                 Contract.Assert(newUpper >= prevUpper);
487                 Contract.Assert(newLower >= prevLower);
488                 Contract.Assert(newUpper == prevUpper ^ newLower == prevLower);
489
490                 int oldIndexes = (prevUpper << 16) | (prevLower & SixteenBits);
491                 int newIndexes = (newUpper << 16) | (newLower & SixteenBits);
492                 int prevIndexes = Interlocked.CompareExchange(ref indexes, newIndexes, oldIndexes);
493                 prevUpper = (prevIndexes >> 16) & SixteenBits;
494                 prevLower = prevIndexes & SixteenBits;
495                 return prevIndexes == oldIndexes;
496             }
497
498             [ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)]
499             public QueueSegment()
500             {
501                 Contract.Assert(QueueSegmentLength <= SixteenBits);
502                 nodes = new IThreadPoolWorkItem[QueueSegmentLength];
503             }
504
505
506             public bool IsUsedUp()
507             {
508                 int upper, lower;
509                 GetIndexes(out upper, out lower);
510                 return (upper == nodes.Length) && 
511                        (lower == nodes.Length);
512             }
513
514             public bool TryEnqueue(IThreadPoolWorkItem node)
515             {
516                 //
517                 // If there's room in this segment, atomically increment the upper count (to reserve
518                 // space for this node), then store the node.
519                 // Note that this leaves a window where it will look like there is data in that
520                 // array slot, but it hasn't been written yet.  This is taken care of in TryDequeue
521                 // with a busy-wait loop, waiting for the element to become non-null.  This implies
522                 // that we can never store null nodes in this data structure.
523                 //
524                 Contract.Assert(null != node);
525
526                 int upper, lower;
527                 GetIndexes(out upper, out lower);
528
529                 while (true)
530                 {
531                     if (upper == nodes.Length)
532                         return false;
533
534                     if (CompareExchangeIndexes(ref upper, upper + 1, ref lower, lower))
535                     {
536                         Contract.Assert(Volatile.Read(ref nodes[upper]) == null);
537                         Volatile.Write(ref nodes[upper], node);
538                         return true;
539                     }
540                 }
541             }
542
543             [SuppressMessage("Microsoft.Concurrency", "CA8001", Justification = "Reviewed for thread safety")]
544             public bool TryDequeue(out IThreadPoolWorkItem node)
545             {
546                 //
547                 // If there are nodes in this segment, increment the lower count, then take the
548                 // element we find there.
549                 //
550                 int upper, lower;
551                 GetIndexes(out upper, out lower);
552
553                 while(true)
554                 {
555                     if (lower == upper)
556                     {
557                         node = null;
558                         return false;
559                     }
560
561                     if (CompareExchangeIndexes(ref upper, upper, ref lower, lower + 1))
562                     {
563                         // It's possible that a concurrent call to Enqueue hasn't yet
564                         // written the node reference to the array.  We need to spin until
565                         // it shows up.
566                         SpinWait spinner = new SpinWait();
567                         while ((node = Volatile.Read(ref nodes[lower])) == null)
568                             spinner.SpinOnce();
569
570                         // Null-out the reference so the object can be GC'd earlier.
571                         nodes[lower] = null;
572
573                         return true;
574                     }
575                 }
576             }
577         }
578
579         // The head and tail of the queue.  We enqueue to the head, and dequeue from the tail.
580         internal volatile QueueSegment queueHead;
581         internal volatile QueueSegment queueTail;
582         internal bool loggingEnabled;
583
584         internal static SparseArray<WorkStealingQueue> allThreadQueues = new SparseArray<WorkStealingQueue>(16); //
585
586         private volatile int numOutstandingThreadRequests = 0;
587       
588         public ThreadPoolWorkQueue()
589         {
590             queueTail = queueHead = new QueueSegment();
591 #if !MONO
592             loggingEnabled = FrameworkEventSource.Log.IsEnabled(EventLevel.Verbose, FrameworkEventSource.Keywords.ThreadPool|FrameworkEventSource.Keywords.ThreadTransfer);
593 #endif
594         }
595
596         [SecurityCritical]
597         public ThreadPoolWorkQueueThreadLocals EnsureCurrentThreadHasQueue()
598         {
599             if (null == ThreadPoolWorkQueueThreadLocals.threadLocals)
600                 ThreadPoolWorkQueueThreadLocals.threadLocals = new ThreadPoolWorkQueueThreadLocals(this);
601             return ThreadPoolWorkQueueThreadLocals.threadLocals;
602         }
603
604         [SecurityCritical]
605         internal void EnsureThreadRequested()
606         {
607             //
608             // If we have not yet requested #procs threads from the VM, then request a new thread.
609             // Note that there is a separate count in the VM which will also be incremented in this case, 
610             // which is handled by RequestWorkerThread.
611             //
612             int count = numOutstandingThreadRequests;
613             while (count < ThreadPoolGlobals.processorCount)
614             {
615                 int prev = Interlocked.CompareExchange(ref numOutstandingThreadRequests, count+1, count);
616                 if (prev == count)
617                 {
618                     ThreadPool.RequestWorkerThread();
619                     break;
620                 }
621                 count = prev;
622             }
623         }
624
625         [SecurityCritical]
626         internal void MarkThreadRequestSatisfied()
627         {
628             //
629             // The VM has called us, so one of our outstanding thread requests has been satisfied.
630             // Decrement the count so that future calls to EnsureThreadRequested will succeed.
631             // Note that there is a separate count in the VM which has already been decremented by the VM
632             // by the time we reach this point.
633             //
634             int count = numOutstandingThreadRequests;
635             while (count > 0)
636             {
637                 int prev = Interlocked.CompareExchange(ref numOutstandingThreadRequests, count - 1, count);
638                 if (prev == count)
639                 {
640                     break;
641                 }
642                 count = prev;
643             }
644         }
645
646         [SecurityCritical]
647         public void Enqueue(IThreadPoolWorkItem callback, bool forceGlobal)
648         {
649             ThreadPoolWorkQueueThreadLocals tl = null;
650             if (!forceGlobal)
651                 tl = ThreadPoolWorkQueueThreadLocals.threadLocals;
652
653 #if !MONO
654             if (loggingEnabled)
655                 System.Diagnostics.Tracing.FrameworkEventSource.Log.ThreadPoolEnqueueWorkObject(callback);
656 #endif            
657             if (null != tl)
658             {
659                 tl.workStealingQueue.LocalPush(callback);
660             }
661             else
662             {
663                 QueueSegment head = queueHead;
664
665                 while (!head.TryEnqueue(callback))
666                 {
667                     Interlocked.CompareExchange(ref head.Next, new QueueSegment(), null);
668
669                     while (head.Next != null)
670                     {
671                         Interlocked.CompareExchange(ref queueHead, head.Next, head);
672                         head = queueHead;
673                     }
674                 }
675             }
676
677             EnsureThreadRequested();
678         }
679
680         [SecurityCritical]
681         internal bool LocalFindAndPop(IThreadPoolWorkItem callback)
682         {
683             ThreadPoolWorkQueueThreadLocals tl = ThreadPoolWorkQueueThreadLocals.threadLocals;
684             if (null == tl)
685                 return false;
686
687             return tl.workStealingQueue.LocalFindAndPop(callback);
688         }
689
690         [SecurityCritical]
691         public void Dequeue(ThreadPoolWorkQueueThreadLocals tl, out IThreadPoolWorkItem callback, out bool missedSteal)
692         {
693             callback = null;
694             missedSteal = false;
695             WorkStealingQueue wsq = tl.workStealingQueue;
696
697             if (wsq.LocalPop(out callback))
698                 Contract.Assert(null != callback);
699
700             if (null == callback)
701             {
702                 QueueSegment tail = queueTail;
703                 while (true)
704                 {
705                     if (tail.TryDequeue(out callback))
706                     {
707                         Contract.Assert(null != callback);
708                         break;
709                     }
710
711                     if (null == tail.Next || !tail.IsUsedUp())
712                     {
713                         break;
714                     }
715                     else
716                     {
717                         Interlocked.CompareExchange(ref queueTail, tail.Next, tail);
718                         tail = queueTail;
719                     }
720                 }
721             }
722
723             if (null == callback)
724             {
725                 WorkStealingQueue[] otherQueues = allThreadQueues.Current;
726                 int i = tl.random.Next(otherQueues.Length);
727                 int c = otherQueues.Length;
728                 while (c > 0)
729                 {
730                     WorkStealingQueue otherQueue = Volatile.Read(ref otherQueues[i % otherQueues.Length]);
731                     if (otherQueue != null &&
732                         otherQueue != wsq &&
733                         otherQueue.TrySteal(out callback, ref missedSteal))
734                     {
735                         Contract.Assert(null != callback);
736                         break;
737                     }
738                     i++;
739                     c--;
740                 }
741             }
742         }
743
744         [SecurityCritical]
745         static internal bool Dispatch()
746         {
747             var workQueue = ThreadPoolGlobals.workQueue;
748             //
749             // The clock is ticking!  We have ThreadPoolGlobals.tpQuantum milliseconds to get some work done, and then
750             // we need to return to the VM.
751             //
752             int quantumStartTime = Environment.TickCount;
753
754             //
755             // Update our records to indicate that an outstanding request for a thread has now been fulfilled.
756             // From this point on, we are responsible for requesting another thread if we stop working for any
757             // reason, and we believe there might still be work in the queue.
758             //
759             // Note that if this thread is aborted before we get a chance to request another one, the VM will
760             // record a thread request on our behalf.  So we don't need to worry about getting aborted right here.
761             //
762             workQueue.MarkThreadRequestSatisfied();
763
764 #if !MONO
765             // Has the desire for logging changed since the last time we entered?
766             workQueue.loggingEnabled = FrameworkEventSource.Log.IsEnabled(EventLevel.Verbose, FrameworkEventSource.Keywords.ThreadPool|FrameworkEventSource.Keywords.ThreadTransfer);
767 #endif
768             //
769             // Assume that we're going to need another thread if this one returns to the VM.  We'll set this to 
770             // false later, but only if we're absolutely certain that the queue is empty.
771             //
772             bool needAnotherThread = true;
773             IThreadPoolWorkItem workItem = null;
774             try
775             {
776                 //
777                 // Set up our thread-local data
778                 //
779                 ThreadPoolWorkQueueThreadLocals tl = workQueue.EnsureCurrentThreadHasQueue();
780
781                 //
782                 // Loop until our quantum expires.
783                 //
784                 while ((Environment.TickCount - quantumStartTime) < ThreadPoolGlobals.tpQuantum)
785                 {
786                     //
787                     // Dequeue and EnsureThreadRequested must be protected from ThreadAbortException.  
788                     // These are fast, so this will not delay aborts/AD-unloads for very long.
789                     //
790                     try { }
791                     finally
792                     {
793                         bool missedSteal = false;
794                         workQueue.Dequeue(tl, out workItem, out missedSteal);
795
796                         if (workItem == null)
797                         {
798                             //
799                             // No work.  We're going to return to the VM once we leave this protected region.
800                             // If we missed a steal, though, there may be more work in the queue.
801                             // Instead of looping around and trying again, we'll just request another thread.  This way
802                             // we won't starve other AppDomains while we spin trying to get locks, and hopefully the thread
803                             // that owns the contended work-stealing queue will pick up its own workitems in the meantime, 
804                             // which will be more efficient than this thread doing it anyway.
805                             //
806                             needAnotherThread = missedSteal;
807                         }
808                         else
809                         {
810                             //
811                             // If we found work, there may be more work.  Ask for another thread so that the other work can be processed
812                             // in parallel.  Note that this will only ask for a max of #procs threads, so it's safe to call it for every dequeue.
813                             //
814                             workQueue.EnsureThreadRequested();
815                         }
816                     }
817
818                     if (workItem == null)
819                     {
820                         // Tell the VM we're returning normally, not because Hill Climbing asked us to return.
821                         return true;
822                     }
823                     else
824                     {
825 #if !MONO
826                         if (workQueue.loggingEnabled)
827                             System.Diagnostics.Tracing.FrameworkEventSource.Log.ThreadPoolDequeueWorkObject(workItem);
828 #endif
829                         //
830                         // Execute the workitem outside of any finally blocks, so that it can be aborted if needed.
831                         //
832                         if (ThreadPoolGlobals.enableWorkerTracking)
833                         {
834                             bool reportedStatus = false;
835                             try
836                             {
837                                 try { }
838                                 finally
839                                 {
840                                     ThreadPool.ReportThreadStatus(true);
841                                     reportedStatus = true;
842                                 }
843                                 workItem.ExecuteWorkItem();
844                                 workItem = null;
845                             }
846                             finally
847                             {
848                                 if (reportedStatus)
849                                     ThreadPool.ReportThreadStatus(false);
850                             }
851                         }
852                         else
853                         {
854                             workItem.ExecuteWorkItem();
855                             workItem = null;
856                         }
857
858                         // 
859                         // Notify the VM that we executed this workitem.  This is also our opportunity to ask whether Hill Climbing wants
860                         // us to return the thread to the pool or not.
861                         //
862                         if (!ThreadPool.NotifyWorkItemComplete())
863                             return false;
864                     }
865                 }
866                 // If we get here, it's because our quantum expired.  Tell the VM we're returning normally.
867                 return true;
868             }
869             catch (ThreadAbortException tae)
870             {
871                 //
872                 // This is here to catch the case where this thread is aborted between the time we exit the finally block in the dispatch
873                 // loop, and the time we execute the work item.  QueueUserWorkItemCallback uses this to update its accounting of whether
874                 // it was executed or not (in debug builds only).  Task uses this to communicate the ThreadAbortException to anyone
875                 // who waits for the task to complete.
876                 //
877                 if (workItem != null)
878                     workItem.MarkAborted(tae);
879                 
880                 //
881                 // In this case, the VM is going to request another thread on our behalf.  No need to do it twice.
882                 //
883                 needAnotherThread = false;
884                 // throw;  //no need to explicitly rethrow a ThreadAbortException, and doing so causes allocations on amd64.
885             }
886             finally
887             {
888                 //
889                 // If we are exiting for any reason other than that the queue is definitely empty, ask for another
890                 // thread to pick up where we left off.
891                 //
892                 if (needAnotherThread)
893                     workQueue.EnsureThreadRequested();
894             }
895
896             // 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.
897             Contract.Assert(false);
898             return true;
899         }
900     }
901
902     // Holds a WorkStealingQueue, and remmoves it from the list when this object is no longer referened.
903     internal sealed class ThreadPoolWorkQueueThreadLocals
904     {
905         [ThreadStatic]
906         [SecurityCritical]
907         public static ThreadPoolWorkQueueThreadLocals threadLocals;
908
909         public readonly ThreadPoolWorkQueue workQueue;
910         public readonly ThreadPoolWorkQueue.WorkStealingQueue workStealingQueue;
911         public readonly Random random = new Random(Thread.CurrentThread.ManagedThreadId);
912
913         public ThreadPoolWorkQueueThreadLocals(ThreadPoolWorkQueue tpq)
914         {
915             workQueue = tpq;
916             workStealingQueue = new ThreadPoolWorkQueue.WorkStealingQueue();
917             ThreadPoolWorkQueue.allThreadQueues.Add(workStealingQueue);
918         }
919
920         [SecurityCritical]
921         private void CleanUp()
922         {
923             if (null != workStealingQueue)
924             {
925                 if (null != workQueue)
926                 {
927                     bool done = false;
928                     while (!done)
929                     {
930                         // Ensure that we won't be aborted between LocalPop and Enqueue.
931                         try { }
932                         finally
933                         {
934                             IThreadPoolWorkItem cb = null;
935                             if (workStealingQueue.LocalPop(out cb))
936                             {
937                                 Contract.Assert(null != cb);
938                                 workQueue.Enqueue(cb, true);
939                             }
940                             else
941                             {
942                                 done = true;
943                             }
944                         }
945                     }
946                 }
947
948                 ThreadPoolWorkQueue.allThreadQueues.Remove(workStealingQueue);
949             }
950         }
951
952         [SecuritySafeCritical]
953         ~ThreadPoolWorkQueueThreadLocals()
954         {
955             // Since the purpose of calling CleanUp is to transfer any pending workitems into the global
956             // queue so that they will be executed by another thread, there's no point in doing this cleanup
957             // if we're in the process of shutting down or unloading the AD.  In those cases, the work won't
958             // execute anyway.  And there are subtle ----s involved there that would lead us to do the wrong
959             // thing anyway.  So we'll only clean up if this is a "normal" finalization.
960             if (!(Environment.HasShutdownStarted || AppDomain.CurrentDomain.IsFinalizingForUnload()))
961                 CleanUp();
962         }
963     }
964
965 #if !MONO
966     internal sealed class RegisteredWaitHandleSafe : CriticalFinalizerObject
967     {
968         private static IntPtr InvalidHandle
969         {
970             [System.Security.SecuritySafeCritical]  // auto-generated
971             get
972             {
973                 return new IntPtr(-1);
974             }
975         }
976         private IntPtr registeredWaitHandle;
977         private WaitHandle m_internalWaitObject;
978         private bool bReleaseNeeded = false;
979         private volatile int m_lock = 0;
980
981         #if FEATURE_CORECLR
982         [System.Security.SecuritySafeCritical] // auto-generated
983         #endif
984         internal RegisteredWaitHandleSafe()
985         {
986             registeredWaitHandle = InvalidHandle;
987         }
988
989         internal IntPtr GetHandle()
990         {
991            return registeredWaitHandle;
992         }
993     
994         internal void SetHandle(IntPtr handle)
995         {
996             registeredWaitHandle = handle;
997         }
998
999         [System.Security.SecurityCritical]  // auto-generated
1000         [ResourceExposure(ResourceScope.None)]
1001         [ResourceConsumption(ResourceScope.Machine, ResourceScope.Machine)]
1002         [ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)]
1003         internal void SetWaitObject(WaitHandle waitObject)
1004         {
1005             // needed for DangerousAddRef
1006             RuntimeHelpers.PrepareConstrainedRegions();
1007             try
1008             {
1009             }
1010             finally
1011             {
1012                 m_internalWaitObject = waitObject;
1013                 if (waitObject != null)
1014                 {
1015                     m_internalWaitObject.SafeWaitHandle.DangerousAddRef(ref bReleaseNeeded);
1016                 }
1017             }
1018         }
1019
1020         [System.Security.SecurityCritical]  // auto-generated
1021         [ResourceExposure(ResourceScope.None)]
1022         [ResourceConsumption(ResourceScope.Machine, ResourceScope.Machine)]
1023         [ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)]
1024         internal bool Unregister(
1025              WaitHandle     waitObject          // object to be notified when all callbacks to delegates have completed
1026              )
1027         {
1028             bool result = false;
1029             // needed for DangerousRelease
1030             RuntimeHelpers.PrepareConstrainedRegions();
1031             try
1032             {
1033             }
1034             finally
1035             {
1036                 // lock(this) cannot be used reliably in Cer since thin lock could be
1037                 // promoted to syncblock and that is not a guaranteed operation
1038                 bool bLockTaken = false;
1039                 do 
1040                 {
1041                     if (Interlocked.CompareExchange(ref m_lock, 1, 0) == 0)
1042                     {
1043                         bLockTaken = true;
1044                         try
1045                         {
1046                             if (ValidHandle())
1047                             {
1048                                 result = UnregisterWaitNative(GetHandle(), waitObject == null ? null : waitObject.SafeWaitHandle);
1049                                 if (result == true)
1050                                 {
1051                                     if (bReleaseNeeded)
1052                                     {
1053                                         m_internalWaitObject.SafeWaitHandle.DangerousRelease();
1054                                         bReleaseNeeded = false;
1055                                     }
1056                                     // if result not true don't release/suppress here so finalizer can make another attempt
1057                                     SetHandle(InvalidHandle);
1058                                     m_internalWaitObject = null;
1059                                     GC.SuppressFinalize(this);
1060                                 }
1061                             }
1062                         }
1063                         finally
1064                         {
1065                             m_lock = 0;
1066                         }
1067                     }
1068                     Thread.SpinWait(1);     // yield to processor
1069                 }
1070                 while (!bLockTaken);
1071             }
1072             return result;
1073         }
1074
1075         private bool ValidHandle()
1076         {
1077             return (registeredWaitHandle != InvalidHandle && registeredWaitHandle != IntPtr.Zero);
1078         }
1079
1080         [System.Security.SecuritySafeCritical]  // auto-generated
1081         [ResourceExposure(ResourceScope.None)]
1082         [ResourceConsumption(ResourceScope.Machine, ResourceScope.Machine)]
1083         ~RegisteredWaitHandleSafe()
1084         {
1085             // if the app has already unregistered the wait, there is nothing to cleanup
1086             // we can detect this by checking the handle. Normally, there is no ---- here
1087             // so no need to protect reading of handle. However, if this object gets 
1088             // resurrected and then someone does an unregister, it would introduce a ----
1089             //
1090             // PrepareConstrainedRegions call not needed since finalizer already in Cer
1091             //
1092             // lock(this) cannot be used reliably even in Cer since thin lock could be
1093             // promoted to syncblock and that is not a guaranteed operation
1094             //
1095             // Note that we will not "spin" to get this lock.  We make only a single attempt;
1096             // if we can't get the lock, it means some other thread is in the middle of a call
1097             // to Unregister, which will do the work of the finalizer anyway.
1098             //
1099             // Further, it's actually critical that we *not* wait for the lock here, because
1100             // the other thread that's in the middle of Unregister may be suspended for shutdown.
1101             // Then, during the live-object finalization phase of shutdown, this thread would
1102             // end up spinning forever, as the other thread would never release the lock.
1103             // This will result in a "leak" of sorts (since the handle will not be cleaned up)
1104             // but the process is exiting anyway.
1105             //
1106             // During AD-unload, we don\92t finalize live objects until all threads have been 
1107             // aborted out of the AD.  Since these locked regions are CERs, we won\92t abort them 
1108             // while the lock is held.  So there should be no leak on AD-unload.
1109             //
1110             if (Interlocked.CompareExchange(ref m_lock, 1, 0) == 0)
1111             {
1112                 try
1113                 {
1114                     if (ValidHandle())
1115                     {
1116                         WaitHandleCleanupNative(registeredWaitHandle);
1117                         if (bReleaseNeeded)
1118                         {
1119                             m_internalWaitObject.SafeWaitHandle.DangerousRelease();
1120                             bReleaseNeeded = false;
1121                         }
1122                         SetHandle(InvalidHandle);
1123                         m_internalWaitObject = null;
1124                     }
1125                 }
1126                 finally
1127                 {
1128                     m_lock = 0;
1129                 }
1130             }
1131         }
1132
1133         [System.Security.SecurityCritical]  // auto-generated
1134         [ResourceExposure(ResourceScope.Machine)]
1135         [MethodImplAttribute(MethodImplOptions.InternalCall)]
1136         private static extern void WaitHandleCleanupNative(IntPtr handle);
1137
1138         [System.Security.SecurityCritical]  // auto-generated
1139         [ResourceExposure(ResourceScope.Machine)]
1140         [MethodImplAttribute(MethodImplOptions.InternalCall)]
1141         private static extern bool UnregisterWaitNative(IntPtr handle, SafeHandle waitObject);
1142     }
1143
1144     [System.Runtime.InteropServices.ComVisible(true)]
1145 #if FEATURE_REMOTING
1146     public sealed class RegisteredWaitHandle : MarshalByRefObject {
1147 #else // FEATURE_REMOTING
1148     public sealed class RegisteredWaitHandle {
1149 #endif // FEATURE_REMOTING
1150         private RegisteredWaitHandleSafe internalRegisteredWait;
1151
1152         internal RegisteredWaitHandle()
1153         {
1154             internalRegisteredWait = new RegisteredWaitHandleSafe();
1155         }
1156
1157         internal void SetHandle(IntPtr handle)
1158         {
1159            internalRegisteredWait.SetHandle(handle);
1160         }
1161
1162         [System.Security.SecurityCritical]  // auto-generated
1163         internal void SetWaitObject(WaitHandle waitObject)
1164         {
1165            internalRegisteredWait.SetWaitObject(waitObject);
1166         }
1167
1168         [System.Security.SecuritySafeCritical]  // auto-generated
1169         [System.Runtime.InteropServices.ComVisible(true)]
1170         // This is the only public method on this class
1171         public bool Unregister(
1172              WaitHandle     waitObject          // object to be notified when all callbacks to delegates have completed
1173         )
1174         {
1175             return internalRegisteredWait.Unregister(waitObject);
1176         }
1177     }
1178 #endif // !MONO
1179
1180     //
1181     // This type is necessary because VS 2010's debugger looks for a method named _ThreadPoolWaitCallbacck.PerformWaitCallback
1182     // 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
1183     // still need to maintain compatibility with VS 2010.  When compat with VS 2010 is no longer an issue, this type may be
1184     // removed.
1185     //
1186     internal static class _ThreadPoolWaitCallback
1187     {
1188         [System.Security.SecurityCritical]
1189         static internal bool PerformWaitCallback()
1190         {
1191             return ThreadPoolWorkQueue.Dispatch();
1192         }
1193     }
1194
1195     internal sealed class QueueUserWorkItemCallback : IThreadPoolWorkItem
1196     {
1197         [System.Security.SecuritySafeCritical]
1198         static QueueUserWorkItemCallback() {}
1199
1200         private WaitCallback callback;
1201         private ExecutionContext context;
1202         private Object state;
1203
1204 #if DEBUG
1205         volatile int executed;
1206
1207         ~QueueUserWorkItemCallback()
1208         {
1209             Contract.Assert(
1210                 executed != 0 || Environment.HasShutdownStarted || AppDomain.CurrentDomain.IsFinalizingForUnload(), 
1211                 "A QueueUserWorkItemCallback was never called!");
1212         }
1213
1214         void MarkExecuted(bool aborted)
1215         {
1216             GC.SuppressFinalize(this);
1217             Contract.Assert(
1218                 0 == Interlocked.Exchange(ref executed, 1) || aborted,
1219                 "A QueueUserWorkItemCallback was called twice!");
1220         }
1221 #endif
1222
1223         [SecurityCritical]
1224         internal QueueUserWorkItemCallback(WaitCallback waitCallback, Object stateObj, bool compressStack, ref StackCrawlMark stackMark)
1225         {
1226             callback = waitCallback;
1227             state = stateObj;
1228             if (compressStack && !ExecutionContext.IsFlowSuppressed())
1229             {
1230                 // clone the exection context
1231                 context = ExecutionContext.Capture(
1232                     ref stackMark,
1233                     ExecutionContext.CaptureOptions.IgnoreSyncCtx | ExecutionContext.CaptureOptions.OptimizeDefaultCase);
1234             }
1235         }
1236
1237         //
1238         // internal test hook - used by tests to exercise work-stealing, etc.
1239         //
1240         internal QueueUserWorkItemCallback(WaitCallback waitCallback, Object stateObj, ExecutionContext ec)
1241         {
1242             callback = waitCallback;
1243             state = stateObj;
1244             context = ec;
1245         }
1246
1247         [SecurityCritical]
1248         void IThreadPoolWorkItem.ExecuteWorkItem()
1249         {
1250 #if DEBUG
1251             MarkExecuted(false);
1252 #endif
1253
1254             // call directly if it is an unsafe call OR EC flow is suppressed
1255             if (context == null)
1256             {
1257                 WaitCallback cb = callback;
1258                 callback = null;
1259                 cb(state);
1260             }
1261             else
1262             {
1263                 ExecutionContext.Run(context, ccb, this, true);
1264             }
1265         }
1266
1267         [SecurityCritical]
1268         void IThreadPoolWorkItem.MarkAborted(ThreadAbortException tae)
1269         {
1270 #if DEBUG
1271             // this workitem didn't execute because we got a ThreadAbortException prior to the call to ExecuteWorkItem.  
1272             // This counts as being executed for our purposes.
1273             MarkExecuted(true);
1274 #endif
1275         }
1276
1277         [System.Security.SecurityCritical]
1278         static internal ContextCallback ccb = new ContextCallback(WaitCallback_Context);
1279
1280         [System.Security.SecurityCritical]
1281         static private void WaitCallback_Context(Object state)
1282         {
1283             QueueUserWorkItemCallback obj = (QueueUserWorkItemCallback)state;
1284             WaitCallback wc = obj.callback as WaitCallback;
1285             Contract.Assert(null != wc);
1286             wc(obj.state);
1287         }
1288     }
1289
1290     internal class _ThreadPoolWaitOrTimerCallback
1291     {
1292         [System.Security.SecuritySafeCritical]
1293         static _ThreadPoolWaitOrTimerCallback() {}
1294
1295         WaitOrTimerCallback _waitOrTimerCallback;
1296         ExecutionContext _executionContext;
1297         Object _state;
1298         [System.Security.SecurityCritical]
1299         static private ContextCallback _ccbt = new ContextCallback(WaitOrTimerCallback_Context_t);
1300         [System.Security.SecurityCritical]
1301         static private ContextCallback _ccbf = new ContextCallback(WaitOrTimerCallback_Context_f);
1302
1303         [System.Security.SecurityCritical]  // auto-generated
1304         internal _ThreadPoolWaitOrTimerCallback(WaitOrTimerCallback waitOrTimerCallback, Object state, bool compressStack, ref StackCrawlMark stackMark)
1305         {
1306             _waitOrTimerCallback = waitOrTimerCallback;
1307             _state = state;
1308
1309             if (compressStack && !ExecutionContext.IsFlowSuppressed())
1310             {
1311                 // capture the exection context
1312                 _executionContext = ExecutionContext.Capture(
1313                     ref stackMark,
1314                     ExecutionContext.CaptureOptions.IgnoreSyncCtx | ExecutionContext.CaptureOptions.OptimizeDefaultCase);
1315             }
1316         }
1317         
1318         [System.Security.SecurityCritical]
1319         static private void WaitOrTimerCallback_Context_t(Object state)
1320         {
1321             WaitOrTimerCallback_Context(state, true);
1322         }
1323
1324         [System.Security.SecurityCritical]
1325         static private void WaitOrTimerCallback_Context_f(Object state)
1326         {
1327             WaitOrTimerCallback_Context(state, false);
1328         }
1329
1330         static private void WaitOrTimerCallback_Context(Object state, bool timedOut)
1331         {
1332             _ThreadPoolWaitOrTimerCallback helper = (_ThreadPoolWaitOrTimerCallback)state;
1333             helper._waitOrTimerCallback(helper._state, timedOut);
1334         }
1335             
1336         // call back helper
1337         [System.Security.SecurityCritical]  // auto-generated
1338         static internal void PerformWaitOrTimerCallback(Object state, bool timedOut)
1339         {
1340             _ThreadPoolWaitOrTimerCallback helper = (_ThreadPoolWaitOrTimerCallback)state; 
1341             Contract.Assert(helper != null, "Null state passed to PerformWaitOrTimerCallback!");
1342             // call directly if it is an unsafe call OR EC flow is suppressed
1343             if (helper._executionContext == null)
1344             {
1345                 WaitOrTimerCallback callback = helper._waitOrTimerCallback;
1346                 callback(helper._state, timedOut);
1347             }
1348             else
1349             {
1350                 using (ExecutionContext executionContext = helper._executionContext.CreateCopy())
1351                 {
1352                 if (timedOut)
1353                         ExecutionContext.Run(executionContext, _ccbt, helper, true);
1354                 else
1355                         ExecutionContext.Run(executionContext, _ccbf, helper, true);
1356                 }
1357             }
1358         }    
1359
1360     }
1361
1362     [HostProtection(Synchronization=true, ExternalThreading=true)]
1363     public static class ThreadPool
1364     {
1365         #if FEATURE_CORECLR
1366         [System.Security.SecurityCritical] // auto-generated
1367         #else
1368         [System.Security.SecuritySafeCritical]
1369         #endif
1370 #pragma warning disable 618
1371         [SecurityPermissionAttribute(SecurityAction.Demand, ControlThread = true)]
1372 #pragma warning restore 618
1373         public static bool SetMaxThreads(int workerThreads, int completionPortThreads)
1374         {
1375             return SetMaxThreadsNative(workerThreads, completionPortThreads);
1376         }
1377
1378         [System.Security.SecuritySafeCritical]  // auto-generated
1379         public static void GetMaxThreads(out int workerThreads, out int completionPortThreads)
1380         {
1381             GetMaxThreadsNative(out workerThreads, out completionPortThreads);
1382         }
1383
1384         #if FEATURE_CORECLR
1385         [System.Security.SecurityCritical] // auto-generated
1386         #else
1387         [System.Security.SecuritySafeCritical]
1388         #endif
1389 #pragma warning disable 618
1390         [SecurityPermissionAttribute(SecurityAction.Demand, ControlThread = true)]
1391 #pragma warning restore 618
1392         public static bool SetMinThreads(int workerThreads, int completionPortThreads)
1393         {
1394             return SetMinThreadsNative(workerThreads, completionPortThreads);
1395         }
1396
1397         [System.Security.SecuritySafeCritical]  // auto-generated
1398         public static void GetMinThreads(out int workerThreads, out int completionPortThreads)
1399         {
1400             GetMinThreadsNative(out workerThreads, out completionPortThreads);
1401         }
1402
1403         [System.Security.SecuritySafeCritical]  // auto-generated
1404         public static void GetAvailableThreads(out int workerThreads, out int completionPortThreads)
1405         {
1406             GetAvailableThreadsNative(out workerThreads, out completionPortThreads);
1407         }
1408
1409         [System.Security.SecuritySafeCritical]  // auto-generated
1410         [CLSCompliant(false)]
1411         [MethodImplAttribute(MethodImplOptions.NoInlining)] // Methods containing StackCrawlMark local var has to be marked non-inlineable            
1412         public static RegisteredWaitHandle RegisterWaitForSingleObject(  // throws RegisterWaitException
1413              WaitHandle             waitObject,
1414              WaitOrTimerCallback    callBack,
1415              Object                 state,
1416              uint               millisecondsTimeOutInterval,
1417              bool               executeOnlyOnce    // NOTE: we do not allow other options that allow the callback to be queued as an APC
1418              )
1419         {
1420             StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller;
1421             return RegisterWaitForSingleObject(waitObject,callBack,state,millisecondsTimeOutInterval,executeOnlyOnce,ref stackMark,true);
1422         }
1423
1424         [System.Security.SecurityCritical]  // auto-generated_required
1425         [CLSCompliant(false)]
1426         [MethodImplAttribute(MethodImplOptions.NoInlining)] // Methods containing StackCrawlMark local var has to be marked non-inlineable
1427         public static RegisteredWaitHandle UnsafeRegisterWaitForSingleObject(  // throws RegisterWaitException
1428              WaitHandle             waitObject,
1429              WaitOrTimerCallback    callBack,
1430              Object                 state,
1431              uint               millisecondsTimeOutInterval,
1432              bool               executeOnlyOnce    // NOTE: we do not allow other options that allow the callback to be queued as an APC
1433              )
1434         {
1435             StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller;
1436             return RegisterWaitForSingleObject(waitObject,callBack,state,millisecondsTimeOutInterval,executeOnlyOnce,ref stackMark,false);
1437         }
1438
1439
1440         [System.Security.SecurityCritical]  // auto-generated
1441         private static RegisteredWaitHandle RegisterWaitForSingleObject(  // throws RegisterWaitException
1442              WaitHandle             waitObject,
1443              WaitOrTimerCallback    callBack,
1444              Object                 state,
1445              uint               millisecondsTimeOutInterval,
1446              bool               executeOnlyOnce,   // NOTE: we do not allow other options that allow the callback to be queued as an APC
1447              ref StackCrawlMark stackMark,
1448              bool               compressStack
1449              )
1450         {
1451 #if !MONO
1452 #if FEATURE_REMOTING
1453             if (RemotingServices.IsTransparentProxy(waitObject))
1454                 throw new InvalidOperationException(Environment.GetResourceString("InvalidOperation_WaitOnTransparentProxy"));
1455             Contract.EndContractBlock();
1456 #endif            
1457
1458             RegisteredWaitHandle registeredWaitHandle = new RegisteredWaitHandle();
1459
1460             if (callBack != null)
1461             {
1462                 _ThreadPoolWaitOrTimerCallback callBackHelper = new _ThreadPoolWaitOrTimerCallback(callBack, state, compressStack, ref stackMark);
1463                 state = (Object)callBackHelper;
1464                 // call SetWaitObject before native call so that waitObject won't be closed before threadpoolmgr registration
1465                 // this could occur if callback were to fire before SetWaitObject does its addref
1466                 registeredWaitHandle.SetWaitObject(waitObject);
1467                 IntPtr nativeRegisteredWaitHandle = RegisterWaitForSingleObjectNative(waitObject,
1468                                                                                state, 
1469                                                                                millisecondsTimeOutInterval,
1470                                                                                executeOnlyOnce,
1471                                                                                registeredWaitHandle,
1472                                                                                ref stackMark,
1473                                                                                compressStack);
1474                 registeredWaitHandle.SetHandle(nativeRegisteredWaitHandle);
1475             }
1476             else
1477             {
1478                 throw new ArgumentNullException("WaitOrTimerCallback");
1479             }
1480             return registeredWaitHandle;
1481 #else
1482             if (waitObject == null)
1483                 throw new ArgumentNullException ("waitObject");
1484             if (callBack == null)
1485                 throw new ArgumentNullException ("callBack");
1486             if (millisecondsTimeOutInterval != Timeout.UnsignedInfinite && millisecondsTimeOutInterval > Int32.MaxValue)
1487                 throw new NotSupportedException ("Timeout is too big. Maximum is Int32.MaxValue");
1488
1489             RegisteredWaitHandle waiter = new RegisteredWaitHandle (waitObject, callBack, state, new TimeSpan (0, 0, 0, 0, (int) millisecondsTimeOutInterval), executeOnlyOnce);
1490             QueueUserWorkItem (new WaitCallback (waiter.Wait), null);
1491
1492             return waiter;
1493 #endif
1494         }
1495
1496
1497         [System.Security.SecuritySafeCritical]  // auto-generated
1498         [MethodImplAttribute(MethodImplOptions.NoInlining)] // Methods containing StackCrawlMark local var has to be marked non-inlineable
1499         public static RegisteredWaitHandle RegisterWaitForSingleObject(  // throws RegisterWaitException
1500              WaitHandle             waitObject,
1501              WaitOrTimerCallback    callBack,
1502              Object                 state,
1503              int                    millisecondsTimeOutInterval,
1504              bool               executeOnlyOnce    // NOTE: we do not allow other options that allow the callback to be queued as an APC
1505              )
1506         {
1507             if (millisecondsTimeOutInterval < -1)
1508                 throw new ArgumentOutOfRangeException("millisecondsTimeOutInterval", Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegOrNegative1"));
1509             Contract.EndContractBlock();
1510             StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller;
1511             return RegisterWaitForSingleObject(waitObject,callBack,state,millisecondsTimeOutInterval == Timeout.Infinite ? Timeout.UnsignedInfinite : (UInt32)millisecondsTimeOutInterval,executeOnlyOnce,ref stackMark,true);
1512         }
1513
1514         [System.Security.SecurityCritical]  // auto-generated_required
1515         [MethodImplAttribute(MethodImplOptions.NoInlining)] // Methods containing StackCrawlMark local var has to be marked non-inlineable            
1516         public static RegisteredWaitHandle UnsafeRegisterWaitForSingleObject(  // throws RegisterWaitException
1517              WaitHandle             waitObject,
1518              WaitOrTimerCallback    callBack,
1519              Object                 state,
1520              int                    millisecondsTimeOutInterval,
1521              bool               executeOnlyOnce    // NOTE: we do not allow other options that allow the callback to be queued as an APC
1522              )
1523         {
1524             if (millisecondsTimeOutInterval < -1)
1525                 throw new ArgumentOutOfRangeException("millisecondsTimeOutInterval", Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegOrNegative1"));
1526             Contract.EndContractBlock();
1527             StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller;
1528             return RegisterWaitForSingleObject(waitObject,callBack,state,millisecondsTimeOutInterval == Timeout.Infinite ? Timeout.UnsignedInfinite : (UInt32)millisecondsTimeOutInterval,executeOnlyOnce,ref stackMark,false);
1529         }
1530
1531         [System.Security.SecuritySafeCritical]  // auto-generated
1532         [MethodImplAttribute(MethodImplOptions.NoInlining)] // Methods containing StackCrawlMark local var has to be marked non-inlineable
1533         public static RegisteredWaitHandle RegisterWaitForSingleObject(  // throws RegisterWaitException
1534             WaitHandle          waitObject,
1535             WaitOrTimerCallback callBack,
1536             Object                  state,
1537             long                    millisecondsTimeOutInterval,
1538             bool                executeOnlyOnce    // NOTE: we do not allow other options that allow the callback to be queued as an APC
1539         )
1540         {
1541             if (millisecondsTimeOutInterval < -1)
1542                 throw new ArgumentOutOfRangeException("millisecondsTimeOutInterval", Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegOrNegative1"));
1543             Contract.EndContractBlock();
1544             StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller;
1545             return RegisterWaitForSingleObject(waitObject,callBack,state,millisecondsTimeOutInterval == Timeout.Infinite ? Timeout.UnsignedInfinite : (UInt32)millisecondsTimeOutInterval,executeOnlyOnce,ref stackMark,true);
1546         }
1547
1548         [System.Security.SecurityCritical]  // auto-generated_required
1549         [MethodImplAttribute(MethodImplOptions.NoInlining)] // Methods containing StackCrawlMark local var has to be marked non-inlineable
1550         public static RegisteredWaitHandle UnsafeRegisterWaitForSingleObject(  // throws RegisterWaitException
1551             WaitHandle          waitObject,
1552             WaitOrTimerCallback callBack,
1553             Object                  state,
1554             long                    millisecondsTimeOutInterval,
1555             bool                executeOnlyOnce    // NOTE: we do not allow other options that allow the callback to be queued as an APC
1556         )
1557         {
1558             if (millisecondsTimeOutInterval < -1)
1559                 throw new ArgumentOutOfRangeException("millisecondsTimeOutInterval", Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegOrNegative1"));
1560             Contract.EndContractBlock();
1561             StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller;
1562             return RegisterWaitForSingleObject(waitObject,callBack,state,millisecondsTimeOutInterval == Timeout.Infinite ? Timeout.UnsignedInfinite : (UInt32)millisecondsTimeOutInterval,executeOnlyOnce,ref stackMark,false);
1563         }
1564
1565         [System.Security.SecuritySafeCritical]  // auto-generated
1566         [MethodImplAttribute(MethodImplOptions.NoInlining)] // Methods containing StackCrawlMark local var has to be marked non-inlineable
1567         public static RegisteredWaitHandle RegisterWaitForSingleObject(
1568                           WaitHandle            waitObject,
1569                           WaitOrTimerCallback   callBack,
1570                           Object                state,
1571                           TimeSpan              timeout,
1572                           bool                  executeOnlyOnce
1573                           )
1574         {
1575             long tm = (long)timeout.TotalMilliseconds;
1576             if (tm < -1)
1577                 throw new ArgumentOutOfRangeException("timeout", Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegOrNegative1"));
1578             if (tm > (long) Int32.MaxValue)
1579                 throw new ArgumentOutOfRangeException("timeout", Environment.GetResourceString("ArgumentOutOfRange_LessEqualToIntegerMaxVal"));
1580             StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller;
1581             return RegisterWaitForSingleObject(waitObject,callBack,state,(UInt32)tm,executeOnlyOnce,ref stackMark,true);
1582         }
1583
1584         [System.Security.SecurityCritical]  // auto-generated_required
1585         [MethodImplAttribute(MethodImplOptions.NoInlining)] // Methods containing StackCrawlMark local var has to be marked non-inlineable
1586         public static RegisteredWaitHandle UnsafeRegisterWaitForSingleObject(
1587                           WaitHandle            waitObject,
1588                           WaitOrTimerCallback   callBack,
1589                           Object                state,
1590                           TimeSpan              timeout,
1591                           bool                  executeOnlyOnce
1592                           )
1593         {
1594             long tm = (long)timeout.TotalMilliseconds;
1595             if (tm < -1)
1596                 throw new ArgumentOutOfRangeException("timeout", Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegOrNegative1"));
1597             if (tm > (long) Int32.MaxValue)
1598                 throw new ArgumentOutOfRangeException("timeout", Environment.GetResourceString("ArgumentOutOfRange_LessEqualToIntegerMaxVal"));
1599             StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller;
1600             return RegisterWaitForSingleObject(waitObject,callBack,state,(UInt32)tm,executeOnlyOnce,ref stackMark,false);
1601         }
1602             
1603         [System.Security.SecuritySafeCritical]  // auto-generated
1604         [MethodImplAttribute(MethodImplOptions.NoInlining)] // Methods containing StackCrawlMark local var has to be marked non-inlineable    
1605         public static bool QueueUserWorkItem(           
1606              WaitCallback           callBack,     // NOTE: we do not expose options that allow the callback to be queued as an APC
1607              Object                 state
1608              )
1609         {
1610             StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller;
1611             return QueueUserWorkItemHelper(callBack,state,ref stackMark,true);
1612         }
1613         
1614         [System.Security.SecuritySafeCritical]  // auto-generated
1615         [MethodImplAttribute(MethodImplOptions.NoInlining)] // Methods containing StackCrawlMark local var has to be marked non-inlineable
1616         public static bool QueueUserWorkItem(           
1617              WaitCallback           callBack     // NOTE: we do not expose options that allow the callback to be queued as an APC
1618              )
1619         {
1620             StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller;
1621             return QueueUserWorkItemHelper(callBack,null,ref stackMark,true);
1622         }
1623     
1624         [System.Security.SecurityCritical]  // auto-generated_required
1625         [MethodImplAttribute(MethodImplOptions.NoInlining)] // Methods containing StackCrawlMark local var has to be marked non-inlineable
1626         public static bool UnsafeQueueUserWorkItem(
1627              WaitCallback           callBack,     // NOTE: we do not expose options that allow the callback to be queued as an APC
1628              Object                 state
1629              )
1630         {
1631             StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller;
1632             return QueueUserWorkItemHelper(callBack,state,ref stackMark,false);
1633         }
1634
1635         //ThreadPool has per-appdomain managed queue of work-items. The VM is
1636         //responsible for just scheduling threads into appdomains. After that
1637         //work-items are dispatched from the managed queue.
1638         [System.Security.SecurityCritical]  // auto-generated
1639         private static bool QueueUserWorkItemHelper(WaitCallback callBack, Object state, ref StackCrawlMark stackMark, bool compressStack )
1640         {
1641             bool success =  true;
1642
1643             if (callBack != null)
1644             {
1645                         //The thread pool maintains a per-appdomain managed work queue.
1646                 //New thread pool entries are added in the managed queue.
1647                 //The VM is responsible for the actual growing/shrinking of 
1648                 //threads. 
1649
1650                 EnsureVMInitialized();
1651
1652                 //
1653                 // If we are able to create the workitem, we need to get it in the queue without being interrupted
1654                 // by a ThreadAbortException.
1655                 //
1656                 try { }
1657                 finally
1658                 {
1659                     QueueUserWorkItemCallback tpcallBack = new QueueUserWorkItemCallback(callBack, state, compressStack, ref stackMark);
1660                     ThreadPoolGlobals.workQueue.Enqueue(tpcallBack, true);
1661                     success = true;
1662                 }
1663             }
1664             else
1665             {
1666                 throw new ArgumentNullException("WaitCallback");
1667             }
1668             return success;
1669         }
1670
1671         [SecurityCritical]
1672         internal static void UnsafeQueueCustomWorkItem(IThreadPoolWorkItem workItem, bool forceGlobal)
1673         {
1674             Contract.Assert(null != workItem);
1675             EnsureVMInitialized();
1676
1677             //
1678             // Enqueue needs to be protected from ThreadAbort
1679             //
1680             try { }
1681             finally
1682             {
1683                 ThreadPoolGlobals.workQueue.Enqueue(workItem, forceGlobal);
1684             }
1685         }
1686
1687         // This method tries to take the target callback out of the current thread's queue.
1688         [SecurityCritical]
1689         internal static bool TryPopCustomWorkItem(IThreadPoolWorkItem workItem)
1690         {
1691             Contract.Assert(null != workItem);
1692             if (!ThreadPoolGlobals.vmTpInitialized)
1693                 return false; //Not initialized, so there's no way this workitem was ever queued.
1694             return ThreadPoolGlobals.workQueue.LocalFindAndPop(workItem);
1695         }
1696
1697         // Get all workitems.  Called by TaskScheduler in its debugger hooks.
1698         [SecurityCritical]
1699         internal static IEnumerable<IThreadPoolWorkItem> GetQueuedWorkItems()
1700         {
1701             return EnumerateQueuedWorkItems(ThreadPoolWorkQueue.allThreadQueues.Current, ThreadPoolGlobals.workQueue.queueTail);
1702         }
1703
1704         internal static IEnumerable<IThreadPoolWorkItem> EnumerateQueuedWorkItems(ThreadPoolWorkQueue.WorkStealingQueue[] wsQueues, ThreadPoolWorkQueue.QueueSegment globalQueueTail)
1705         {
1706             if (wsQueues != null)
1707             {
1708                 // First, enumerate all workitems in thread-local queues.
1709                 foreach (ThreadPoolWorkQueue.WorkStealingQueue wsq in wsQueues)
1710                 {
1711                     if (wsq != null && wsq.m_array != null)
1712                     {
1713                         IThreadPoolWorkItem[] items = wsq.m_array;
1714                         for (int i = 0; i < items.Length; i++)
1715                         {
1716                             IThreadPoolWorkItem item = items[i];
1717                             if (item != null)
1718                                 yield return item;
1719                         }
1720                     }
1721                 }
1722             }
1723
1724             if (globalQueueTail != null)
1725             {
1726                 // Now the global queue
1727                 for (ThreadPoolWorkQueue.QueueSegment segment = globalQueueTail;
1728                     segment != null;
1729                     segment = segment.Next)
1730                 {
1731                     IThreadPoolWorkItem[] items = segment.nodes;
1732                     for (int i = 0; i < items.Length; i++)
1733                     {
1734                         IThreadPoolWorkItem item = items[i];
1735                         if (item != null)
1736                             yield return item;
1737                     }
1738                 }
1739             }
1740         }
1741
1742         [SecurityCritical]
1743         internal static IEnumerable<IThreadPoolWorkItem> GetLocallyQueuedWorkItems()
1744         {
1745             return EnumerateQueuedWorkItems(new ThreadPoolWorkQueue.WorkStealingQueue[] { ThreadPoolWorkQueueThreadLocals.threadLocals.workStealingQueue }, null);
1746         }
1747
1748         [SecurityCritical]
1749         internal static IEnumerable<IThreadPoolWorkItem> GetGloballyQueuedWorkItems()
1750         {
1751             return EnumerateQueuedWorkItems(null, ThreadPoolGlobals.workQueue.queueTail);
1752         }
1753
1754         private static object[] ToObjectArray(IEnumerable<IThreadPoolWorkItem> workitems)
1755         {
1756             int i = 0;
1757             foreach (IThreadPoolWorkItem item in workitems)
1758             {
1759                 i++;
1760             }
1761
1762             object[] result = new object[i];
1763             i = 0;
1764             foreach (IThreadPoolWorkItem item in workitems)
1765             {
1766                 if (i < result.Length) //just in case someone calls us while the queues are in motion
1767                     result[i] = item;
1768                 i++;
1769             }
1770
1771             return result;
1772         }
1773
1774         // This is the method the debugger will actually call, if it ends up calling
1775         // into ThreadPool directly.  Tests can use this to simulate a debugger, as well.
1776         [SecurityCritical]
1777         internal static object[] GetQueuedWorkItemsForDebugger()
1778         {
1779             return ToObjectArray(GetQueuedWorkItems());
1780         }
1781
1782         [SecurityCritical]
1783         internal static object[] GetGloballyQueuedWorkItemsForDebugger()
1784         {
1785             return ToObjectArray(GetGloballyQueuedWorkItems());
1786         }
1787
1788         [SecurityCritical]
1789         internal static object[] GetLocallyQueuedWorkItemsForDebugger()
1790         {
1791             return ToObjectArray(GetLocallyQueuedWorkItems());
1792         }
1793
1794         [System.Security.SecurityCritical]  // auto-generated
1795         [ResourceExposure(ResourceScope.None)]
1796         [MethodImplAttribute(MethodImplOptions.InternalCall)]
1797         internal static extern bool RequestWorkerThread();
1798
1799         [System.Security.SecurityCritical]  // auto-generated
1800         [ResourceExposure(ResourceScope.None)]
1801         [MethodImplAttribute(MethodImplOptions.InternalCall)]
1802         unsafe private static extern bool PostQueuedCompletionStatus(NativeOverlapped* overlapped);
1803
1804         [System.Security.SecurityCritical]  // auto-generated_required
1805         [CLSCompliant(false)]
1806         unsafe public static bool UnsafeQueueNativeOverlapped(NativeOverlapped* overlapped)
1807         {
1808 #if FEATURE_CORECLR && !FEATURE_LEGACYNETCF
1809             if(Environment.OSVersion.Platform == PlatformID.MacOSX)
1810                 throw new NotSupportedException(Environment.GetResourceString("Arg_NotSupportedException"));
1811             Contract.EndContractBlock();
1812 #endif
1813
1814             return PostQueuedCompletionStatus(overlapped);
1815         }
1816
1817         [SecurityCritical]
1818         private static void EnsureVMInitialized()
1819         {
1820             if (!ThreadPoolGlobals.vmTpInitialized)
1821             {
1822                 ThreadPool.InitializeVMTp(ref ThreadPoolGlobals.enableWorkerTracking);
1823                 ThreadPoolGlobals.vmTpInitialized = true;
1824             }
1825         }
1826
1827         // Native methods: 
1828     
1829         [System.Security.SecurityCritical]  // auto-generated
1830         [ResourceExposure(ResourceScope.None)]
1831         [MethodImplAttribute(MethodImplOptions.InternalCall)]
1832         private static extern bool SetMinThreadsNative(int workerThreads, int completionPortThreads);
1833
1834         [System.Security.SecurityCritical]  // auto-generated
1835         [ResourceExposure(ResourceScope.None)]
1836         [MethodImplAttribute(MethodImplOptions.InternalCall)]
1837         private static extern bool SetMaxThreadsNative(int workerThreads, int completionPortThreads);
1838
1839         [System.Security.SecurityCritical]  // auto-generated
1840         [ResourceExposure(ResourceScope.None)]
1841         [MethodImplAttribute(MethodImplOptions.InternalCall)]
1842         private static extern void GetMinThreadsNative(out int workerThreads, out int completionPortThreads);
1843
1844         [System.Security.SecurityCritical]  // auto-generated
1845         [ResourceExposure(ResourceScope.None)]
1846         [MethodImplAttribute(MethodImplOptions.InternalCall)]
1847         private static extern void GetMaxThreadsNative(out int workerThreads, out int completionPortThreads);
1848
1849         [System.Security.SecurityCritical]  // auto-generated
1850         [ResourceExposure(ResourceScope.None)]
1851         [MethodImplAttribute(MethodImplOptions.InternalCall)]
1852         private static extern void GetAvailableThreadsNative(out int workerThreads, out int completionPortThreads);
1853
1854         [System.Security.SecurityCritical]  // auto-generated
1855         [ResourceExposure(ResourceScope.None)]
1856         [MethodImplAttribute(MethodImplOptions.InternalCall)]
1857         internal static extern bool NotifyWorkItemComplete();
1858
1859         [System.Security.SecurityCritical]
1860         [ResourceExposure(ResourceScope.None)]
1861         [MethodImplAttribute(MethodImplOptions.InternalCall)]
1862         internal static extern void ReportThreadStatus(bool isWorking);
1863
1864         [System.Security.SecuritySafeCritical]
1865         internal static void NotifyWorkItemProgress()
1866         {
1867             if (!ThreadPoolGlobals.vmTpInitialized)
1868                 ThreadPool.InitializeVMTp(ref ThreadPoolGlobals.enableWorkerTracking);
1869             NotifyWorkItemProgressNative();
1870         }
1871
1872         [System.Security.SecurityCritical]  // auto-generated
1873         [ResourceExposure(ResourceScope.None)]
1874         [MethodImplAttribute(MethodImplOptions.InternalCall)]
1875         internal static extern void NotifyWorkItemProgressNative();
1876
1877         [System.Security.SecurityCritical]  // auto-generated
1878         [ResourceExposure(ResourceScope.None)]
1879         [MethodImplAttribute(MethodImplOptions.InternalCall)]
1880         internal static extern bool IsThreadPoolHosted();
1881
1882         [System.Security.SecurityCritical]  // auto-generated
1883         [ResourceExposure(ResourceScope.None)]
1884         [MethodImplAttribute(MethodImplOptions.InternalCall)]
1885         private static extern void InitializeVMTp(ref bool enableWorkerTracking);
1886
1887 #if !MONO
1888         [System.Security.SecurityCritical]  // auto-generated
1889         [ResourceExposure(ResourceScope.None)]
1890         [MethodImplAttribute(MethodImplOptions.InternalCall)]
1891         private static extern IntPtr RegisterWaitForSingleObjectNative(  
1892              WaitHandle             waitHandle,
1893              Object                 state,
1894              uint                   timeOutInterval,
1895              bool                   executeOnlyOnce,
1896              RegisteredWaitHandle   registeredWaitHandle,
1897              ref StackCrawlMark     stackMark,
1898              bool                   compressStack   
1899              );
1900 #endif
1901
1902 #if !FEATURE_CORECLR
1903         [System.Security.SecuritySafeCritical]  // auto-generated
1904         [Obsolete("ThreadPool.BindHandle(IntPtr) has been deprecated.  Please use ThreadPool.BindHandle(SafeHandle) instead.", false)]
1905         [SecurityPermissionAttribute( SecurityAction.Demand, Flags = SecurityPermissionFlag.UnmanagedCode)]
1906         public static bool BindHandle(
1907              IntPtr osHandle
1908              )
1909         {
1910             return BindIOCompletionCallbackNative(osHandle);
1911         }
1912 #endif
1913
1914         #if FEATURE_CORECLR
1915         [System.Security.SecurityCritical] // auto-generated
1916         #else
1917         [System.Security.SecuritySafeCritical]
1918         #endif        
1919 #pragma warning disable 618
1920         [SecurityPermissionAttribute(SecurityAction.Demand, Flags = SecurityPermissionFlag.UnmanagedCode)]
1921 #pragma warning restore 618
1922         public static bool BindHandle(SafeHandle osHandle)
1923         {
1924             #if FEATURE_CORECLR && !FEATURE_LEGACYNETCF
1925             if(Environment.OSVersion.Platform == PlatformID.MacOSX)
1926                 throw new NotSupportedException(Environment.GetResourceString("Arg_NotSupportedException"));
1927             Contract.EndContractBlock();
1928             #endif
1929
1930             if (osHandle == null)
1931                 throw new ArgumentNullException("osHandle");
1932             
1933             bool ret = false;
1934             bool mustReleaseSafeHandle = false;
1935             RuntimeHelpers.PrepareConstrainedRegions();
1936             try {
1937                 osHandle.DangerousAddRef(ref mustReleaseSafeHandle);
1938                 ret = BindIOCompletionCallbackNative(osHandle.DangerousGetHandle());
1939             }
1940             finally {
1941                 if (mustReleaseSafeHandle)
1942                     osHandle.DangerousRelease();
1943             }
1944             return ret;
1945         }
1946
1947         [System.Security.SecurityCritical]  // auto-generated
1948         [ResourceExposure(ResourceScope.None)]
1949         [MethodImplAttribute(MethodImplOptions.InternalCall)]
1950         [ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)]
1951         private static extern bool BindIOCompletionCallbackNative(IntPtr fileHandle);
1952     }
1953 }