[corlib] CoreRT System.Threading.Tasks (#6672)
[mono-project.git] / mcs / class / referencesource / mscorlib / system / threading / threadpool.cs
blobaaa517a33e493f8697e443ce6938a35dc4e1543d
1 // ==++==
2 //
3 // Copyright (c) Microsoft Corporation. All rights reserved.
4 //
5 // ==--==
6 //
7 // <OWNER>Microsoft</OWNER>
8 /*=============================================================================
9 **
10 ** Class: ThreadPool
13 ** Purpose: Class for creating and managing a threadpool
16 =============================================================================*/
18 #pragma warning disable 0420
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.
33 namespace System.Threading
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
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.
57 // If we decide to expose some of the workstealing
58 // stuff, this is NOT the thing we want to expose to the public.
60 internal interface IThreadPoolWorkItem
62 [SecurityCritical]
63 void ExecuteWorkItem();
64 [SecurityCritical]
65 void MarkAborted(ThreadAbortException tae);
68 [System.Runtime.InteropServices.ComVisible(true)]
69 public delegate void WaitCallback(Object state);
71 [System.Runtime.InteropServices.ComVisible(true)]
72 public delegate void WaitOrTimerCallback(Object state, bool timedOut); // signalled or timed out
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);
79 internal static class ThreadPoolGlobals
81 //Per-appDomain quantum (in ms) for which the thread keeps processing
82 //requests in the current domain.
83 public static uint tpQuantum = 30U;
85 public static int processorCount = Environment.ProcessorCount;
87 public static bool tpHosted = ThreadPool.IsThreadPoolHosted();
89 public static volatile bool vmTpInitialized;
90 public static bool enableWorkerTracking;
92 [SecurityCritical]
93 public static ThreadPoolWorkQueue workQueue = new ThreadPoolWorkQueue();
95 [System.Security.SecuritySafeCritical] // static constructors should be safe to call
96 static ThreadPoolGlobals()
101 internal sealed class ThreadPoolWorkQueue
103 // Simple sparsely populated array to allow lock-free reading.
104 internal class SparseArray<T> where T : class
106 private volatile T[] m_array;
108 internal SparseArray(int initialSize)
110 m_array = new T[initialSize];
113 internal T[] Current
115 get { return m_array; }
118 internal int Add(T e)
120 while (true)
122 T[] array = m_array;
123 lock (array)
125 for (int i = 0; i < array.Length; i++)
127 if (array[i] == null)
129 Volatile.Write(ref array[i], e);
130 return i;
132 else if (i == array.Length - 1)
134 // Must resize. If we raced and lost, we start over again.
135 if (array != m_array)
136 continue;
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;
149 internal void Remove(T e)
151 T[] array = m_array;
152 lock (array)
154 for (int i = 0; i < m_array.Length; i++)
156 if (m_array[i] == e)
158 Volatile.Write(ref m_array[i], null);
159 break;
166 internal class WorkStealingQueue
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;
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
179 private volatile int m_headIndex = START_INDEX;
180 private volatile int m_tailIndex = START_INDEX;
182 private SpinLock m_foreignLock = new SpinLock(false);
184 public void LocalPush(IThreadPoolWorkItem obj)
186 int tail = m_tailIndex;
188 // We're going to increment the tail; if we'll overflow, then we need to reset our counts
189 if (tail == int.MaxValue)
191 bool lockTaken = false;
194 m_foreignLock.Enter(ref lockTaken);
196 if (m_tailIndex == int.MaxValue)
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.
208 m_headIndex = m_headIndex & m_mask;
209 m_tailIndex = tail = m_tailIndex & m_mask;
210 Contract.Assert(m_headIndex <= m_tailIndex);
213 finally
215 if (lockTaken)
216 m_foreignLock.Exit(true);
220 // When there are at least 2 elements' worth of space, we can take the fast path.
221 if (tail < m_headIndex + m_mask)
223 Volatile.Write(ref m_array[tail & m_mask], obj);
224 m_tailIndex = tail + 1;
226 else
228 // We need to contend with foreign pops, so we lock.
229 bool lockTaken = false;
232 m_foreignLock.Enter(ref lockTaken);
234 int head = m_headIndex;
235 int count = m_tailIndex - m_headIndex;
237 // If there is still space (one left), just add the element.
238 if (count >= m_mask)
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];
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;
252 Volatile.Write(ref m_array[tail & m_mask], obj);
253 m_tailIndex = tail + 1;
255 finally
257 if (lockTaken)
258 m_foreignLock.Exit(false);
263 [SuppressMessage("Microsoft.Concurrency", "CA8001", Justification = "Reviewed for thread safety")]
264 public bool LocalFindAndPop(IThreadPoolWorkItem obj)
266 // Fast path: check the tail. If equal, we can skip the lock.
267 if (m_array[(m_tailIndex - 1) & m_mask] == obj)
269 IThreadPoolWorkItem unused;
270 if (LocalPop(out unused))
272 Contract.Assert(unused == obj);
273 return true;
275 return false;
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--)
288 if (m_array[i & m_mask] == obj)
290 // If we found the element, block out steals to avoid interference.
291 // @
292 bool lockTaken = false;
295 m_foreignLock.Enter(ref lockTaken);
297 // If we lost the ----, bail.
298 if (m_array[i & m_mask] == null)
299 return false;
301 // Otherwise, null out the element.
302 Volatile.Write(ref m_array[i & m_mask], null);
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;
312 return true;
314 finally
316 if (lockTaken)
317 m_foreignLock.Exit(false);
322 return false;
325 [SuppressMessage("Microsoft.Concurrency", "CA8001", Justification = "Reviewed for thread safety")]
326 public bool LocalPop(out IThreadPoolWorkItem obj)
328 while (true)
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)
334 obj = null;
335 return false;
338 tail -= 1;
339 Interlocked.Exchange(ref m_tailIndex, tail);
341 // If there is no interaction with a take, we can head down the fast path.
342 if (m_headIndex <= tail)
344 int idx = tail & m_mask;
345 obj = Volatile.Read(ref m_array[idx]);
347 // Check for nulls in the array.
348 if (obj == null) continue;
350 m_array[idx] = null;
351 return true;
353 else
355 // Interaction with takes: 0 or 1 elements left.
356 bool lockTaken = false;
359 m_foreignLock.Enter(ref lockTaken);
361 if (m_headIndex <= tail)
363 // Element still available. Take it.
364 int idx = tail & m_mask;
365 obj = Volatile.Read(ref m_array[idx]);
367 // Check for nulls in the array.
368 if (obj == null) continue;
370 m_array[idx] = null;
371 return true;
373 else
375 // We lost the ----, element was stolen, restore the tail.
376 m_tailIndex = tail + 1;
377 obj = null;
378 return false;
381 finally
383 if (lockTaken)
384 m_foreignLock.Exit(false);
390 public bool TrySteal(out IThreadPoolWorkItem obj, ref bool missedSteal)
392 return TrySteal(out obj, ref missedSteal, 0); // no blocking by default.
395 private bool TrySteal(out IThreadPoolWorkItem obj, ref bool missedSteal, int millisecondsTimeout)
397 obj = null;
399 while (true)
401 if (m_headIndex >= m_tailIndex)
402 return false;
404 bool taken = false;
407 m_foreignLock.TryEnter(millisecondsTimeout, ref taken);
408 if (taken)
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);
414 if (head < m_tailIndex)
416 int idx = head & m_mask;
417 obj = Volatile.Read(ref m_array[idx]);
419 // Check for nulls in the array.
420 if (obj == null) continue;
422 m_array[idx] = null;
423 return true;
425 else
427 // Failed, restore head.
428 m_headIndex = head;
429 obj = null;
430 missedSteal = true;
433 else
435 missedSteal = true;
438 finally
440 if (taken)
441 m_foreignLock.Exit(false);
444 return false;
449 internal class QueueSegment
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;
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;
460 // The next segment in the queue.
461 public volatile QueueSegment Next;
464 const int SixteenBits = 0xffff;
466 void GetIndexes(out int upper, out int lower)
468 int i = indexes;
469 upper = (i >> 16) & SixteenBits;
470 lower = i & SixteenBits;
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);
479 bool CompareExchangeIndexes(ref int prevUpper, int newUpper, ref int prevLower, int newLower)
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);
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;
498 [ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)]
499 public QueueSegment()
501 Contract.Assert(QueueSegmentLength <= SixteenBits);
502 nodes = new IThreadPoolWorkItem[QueueSegmentLength];
506 public bool IsUsedUp()
508 int upper, lower;
509 GetIndexes(out upper, out lower);
510 return (upper == nodes.Length) &&
511 (lower == nodes.Length);
514 public bool TryEnqueue(IThreadPoolWorkItem node)
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.
524 Contract.Assert(null != node);
526 int upper, lower;
527 GetIndexes(out upper, out lower);
529 while (true)
531 if (upper == nodes.Length)
532 return false;
534 if (CompareExchangeIndexes(ref upper, upper + 1, ref lower, lower))
536 Contract.Assert(Volatile.Read(ref nodes[upper]) == null);
537 Volatile.Write(ref nodes[upper], node);
538 return true;
543 [SuppressMessage("Microsoft.Concurrency", "CA8001", Justification = "Reviewed for thread safety")]
544 public bool TryDequeue(out IThreadPoolWorkItem node)
547 // If there are nodes in this segment, increment the lower count, then take the
548 // element we find there.
550 int upper, lower;
551 GetIndexes(out upper, out lower);
553 while(true)
555 if (lower == upper)
557 node = null;
558 return false;
561 if (CompareExchangeIndexes(ref upper, upper, ref lower, lower + 1))
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();
570 // Null-out the reference so the object can be GC'd earlier.
571 nodes[lower] = null;
573 return true;
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 #if !MONO
583 internal bool loggingEnabled;
584 #endif
586 internal static SparseArray<WorkStealingQueue> allThreadQueues = new SparseArray<WorkStealingQueue>(16); //
588 private volatile int numOutstandingThreadRequests = 0;
590 public ThreadPoolWorkQueue()
592 queueTail = queueHead = new QueueSegment();
593 #if !MONO
594 loggingEnabled = FrameworkEventSource.Log.IsEnabled(EventLevel.Verbose, FrameworkEventSource.Keywords.ThreadPool|FrameworkEventSource.Keywords.ThreadTransfer);
595 #endif
598 [SecurityCritical]
599 public ThreadPoolWorkQueueThreadLocals EnsureCurrentThreadHasQueue()
601 if (null == ThreadPoolWorkQueueThreadLocals.threadLocals)
602 ThreadPoolWorkQueueThreadLocals.threadLocals = new ThreadPoolWorkQueueThreadLocals(this);
603 return ThreadPoolWorkQueueThreadLocals.threadLocals;
606 [SecurityCritical]
607 internal void EnsureThreadRequested()
610 // If we have not yet requested #procs threads from the VM, then request a new thread.
611 // Note that there is a separate count in the VM which will also be incremented in this case,
612 // which is handled by RequestWorkerThread.
614 int count = numOutstandingThreadRequests;
615 while (count < ThreadPoolGlobals.processorCount)
617 int prev = Interlocked.CompareExchange(ref numOutstandingThreadRequests, count+1, count);
618 if (prev == count)
620 ThreadPool.RequestWorkerThread();
621 break;
623 count = prev;
627 [SecurityCritical]
628 internal void MarkThreadRequestSatisfied()
631 // The VM has called us, so one of our outstanding thread requests has been satisfied.
632 // Decrement the count so that future calls to EnsureThreadRequested will succeed.
633 // Note that there is a separate count in the VM which has already been decremented by the VM
634 // by the time we reach this point.
636 int count = numOutstandingThreadRequests;
637 while (count > 0)
639 int prev = Interlocked.CompareExchange(ref numOutstandingThreadRequests, count - 1, count);
640 if (prev == count)
642 break;
644 count = prev;
648 [SecurityCritical]
649 public void Enqueue(IThreadPoolWorkItem callback, bool forceGlobal)
651 ThreadPoolWorkQueueThreadLocals tl = null;
652 if (!forceGlobal)
653 tl = ThreadPoolWorkQueueThreadLocals.threadLocals;
655 #if !MONO
656 if (loggingEnabled)
657 System.Diagnostics.Tracing.FrameworkEventSource.Log.ThreadPoolEnqueueWorkObject(callback);
658 #endif
659 if (null != tl)
661 tl.workStealingQueue.LocalPush(callback);
663 else
665 QueueSegment head = queueHead;
667 while (!head.TryEnqueue(callback))
669 Interlocked.CompareExchange(ref head.Next, new QueueSegment(), null);
671 while (head.Next != null)
673 Interlocked.CompareExchange(ref queueHead, head.Next, head);
674 head = queueHead;
678 #if MONO
679 ThreadPool.NotifyWorkItemQueued();
680 #endif
681 EnsureThreadRequested();
684 [SecurityCritical]
685 internal bool LocalFindAndPop(IThreadPoolWorkItem callback)
687 ThreadPoolWorkQueueThreadLocals tl = ThreadPoolWorkQueueThreadLocals.threadLocals;
688 if (null == tl)
689 return false;
691 return tl.workStealingQueue.LocalFindAndPop(callback);
694 [SecurityCritical]
695 public void Dequeue(ThreadPoolWorkQueueThreadLocals tl, out IThreadPoolWorkItem callback, out bool missedSteal)
697 callback = null;
698 missedSteal = false;
699 WorkStealingQueue wsq = tl.workStealingQueue;
701 if (wsq.LocalPop(out callback))
702 Contract.Assert(null != callback);
704 if (null == callback)
706 QueueSegment tail = queueTail;
707 while (true)
709 if (tail.TryDequeue(out callback))
711 Contract.Assert(null != callback);
712 break;
715 if (null == tail.Next || !tail.IsUsedUp())
717 break;
719 else
721 Interlocked.CompareExchange(ref queueTail, tail.Next, tail);
722 tail = queueTail;
727 if (null == callback)
729 WorkStealingQueue[] otherQueues = allThreadQueues.Current;
730 int i = tl.random.Next(otherQueues.Length);
731 int c = otherQueues.Length;
732 while (c > 0)
734 WorkStealingQueue otherQueue = Volatile.Read(ref otherQueues[i % otherQueues.Length]);
735 if (otherQueue != null &&
736 otherQueue != wsq &&
737 otherQueue.TrySteal(out callback, ref missedSteal))
739 Contract.Assert(null != callback);
740 break;
742 i++;
743 c--;
748 [SecurityCritical]
749 static internal bool Dispatch()
751 var workQueue = ThreadPoolGlobals.workQueue;
753 // The clock is ticking! We have ThreadPoolGlobals.tpQuantum milliseconds to get some work done, and then
754 // we need to return to the VM.
756 int quantumStartTime = Environment.TickCount;
759 // Update our records to indicate that an outstanding request for a thread has now been fulfilled.
760 // From this point on, we are responsible for requesting another thread if we stop working for any
761 // reason, and we believe there might still be work in the queue.
763 // Note that if this thread is aborted before we get a chance to request another one, the VM will
764 // record a thread request on our behalf. So we don't need to worry about getting aborted right here.
766 workQueue.MarkThreadRequestSatisfied();
768 #if !MONO
769 // Has the desire for logging changed since the last time we entered?
770 workQueue.loggingEnabled = FrameworkEventSource.Log.IsEnabled(EventLevel.Verbose, FrameworkEventSource.Keywords.ThreadPool|FrameworkEventSource.Keywords.ThreadTransfer);
771 #endif
773 // Assume that we're going to need another thread if this one returns to the VM. We'll set this to
774 // false later, but only if we're absolutely certain that the queue is empty.
776 bool needAnotherThread = true;
777 IThreadPoolWorkItem workItem = null;
781 // Set up our thread-local data
783 ThreadPoolWorkQueueThreadLocals tl = workQueue.EnsureCurrentThreadHasQueue();
786 // Loop until our quantum expires.
788 while ((Environment.TickCount - quantumStartTime) < ThreadPoolGlobals.tpQuantum)
791 // Dequeue and EnsureThreadRequested must be protected from ThreadAbortException.
792 // These are fast, so this will not delay aborts/AD-unloads for very long.
794 try { }
795 finally
797 bool missedSteal = false;
798 workQueue.Dequeue(tl, out workItem, out missedSteal);
800 if (workItem == null)
803 // No work. We're going to return to the VM once we leave this protected region.
804 // If we missed a steal, though, there may be more work in the queue.
805 // Instead of looping around and trying again, we'll just request another thread. This way
806 // we won't starve other AppDomains while we spin trying to get locks, and hopefully the thread
807 // that owns the contended work-stealing queue will pick up its own workitems in the meantime,
808 // which will be more efficient than this thread doing it anyway.
810 needAnotherThread = missedSteal;
812 else
815 // If we found work, there may be more work. Ask for another thread so that the other work can be processed
816 // in parallel. Note that this will only ask for a max of #procs threads, so it's safe to call it for every dequeue.
818 workQueue.EnsureThreadRequested();
822 if (workItem == null)
824 // Tell the VM we're returning normally, not because Hill Climbing asked us to return.
825 return true;
827 else
829 #if !MONO
830 if (workQueue.loggingEnabled)
831 System.Diagnostics.Tracing.FrameworkEventSource.Log.ThreadPoolDequeueWorkObject(workItem);
832 #endif
834 // Execute the workitem outside of any finally blocks, so that it can be aborted if needed.
836 if (ThreadPoolGlobals.enableWorkerTracking)
838 bool reportedStatus = false;
841 try { }
842 finally
844 ThreadPool.ReportThreadStatus(true);
845 reportedStatus = true;
847 workItem.ExecuteWorkItem();
848 workItem = null;
850 finally
852 if (reportedStatus)
853 ThreadPool.ReportThreadStatus(false);
856 else
858 workItem.ExecuteWorkItem();
859 workItem = null;
863 // Notify the VM that we executed this workitem. This is also our opportunity to ask whether Hill Climbing wants
864 // us to return the thread to the pool or not.
866 if (!ThreadPool.NotifyWorkItemComplete())
867 return false;
870 // If we get here, it's because our quantum expired. Tell the VM we're returning normally.
871 return true;
873 catch (ThreadAbortException tae)
876 // This is here to catch the case where this thread is aborted between the time we exit the finally block in the dispatch
877 // loop, and the time we execute the work item. QueueUserWorkItemCallback uses this to update its accounting of whether
878 // it was executed or not (in debug builds only). Task uses this to communicate the ThreadAbortException to anyone
879 // who waits for the task to complete.
881 if (workItem != null)
882 workItem.MarkAborted(tae);
885 // In this case, the VM is going to request another thread on our behalf. No need to do it twice.
887 needAnotherThread = false;
888 // throw; //no need to explicitly rethrow a ThreadAbortException, and doing so causes allocations on amd64.
890 finally
893 // If we are exiting for any reason other than that the queue is definitely empty, ask for another
894 // thread to pick up where we left off.
896 if (needAnotherThread)
897 workQueue.EnsureThreadRequested();
900 // 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.
901 Contract.Assert(false);
902 return true;
906 // Holds a WorkStealingQueue, and remmoves it from the list when this object is no longer referened.
907 internal sealed class ThreadPoolWorkQueueThreadLocals
909 [ThreadStatic]
910 [SecurityCritical]
911 public static ThreadPoolWorkQueueThreadLocals threadLocals;
913 public readonly ThreadPoolWorkQueue workQueue;
914 public readonly ThreadPoolWorkQueue.WorkStealingQueue workStealingQueue;
915 public readonly Random random = new Random(Thread.CurrentThread.ManagedThreadId);
917 public ThreadPoolWorkQueueThreadLocals(ThreadPoolWorkQueue tpq)
919 workQueue = tpq;
920 workStealingQueue = new ThreadPoolWorkQueue.WorkStealingQueue();
921 ThreadPoolWorkQueue.allThreadQueues.Add(workStealingQueue);
924 [SecurityCritical]
925 private void CleanUp()
927 if (null != workStealingQueue)
929 if (null != workQueue)
931 bool done = false;
932 while (!done)
934 // Ensure that we won't be aborted between LocalPop and Enqueue.
935 try { }
936 finally
938 IThreadPoolWorkItem cb = null;
939 if (workStealingQueue.LocalPop(out cb))
941 Contract.Assert(null != cb);
942 workQueue.Enqueue(cb, true);
944 else
946 done = true;
952 ThreadPoolWorkQueue.allThreadQueues.Remove(workStealingQueue);
956 [SecuritySafeCritical]
957 ~ThreadPoolWorkQueueThreadLocals()
959 // Since the purpose of calling CleanUp is to transfer any pending workitems into the global
960 // queue so that they will be executed by another thread, there's no point in doing this cleanup
961 // if we're in the process of shutting down or unloading the AD. In those cases, the work won't
962 // execute anyway. And there are subtle ----s involved there that would lead us to do the wrong
963 // thing anyway. So we'll only clean up if this is a "normal" finalization.
964 if (!(Environment.HasShutdownStarted || AppDomain.CurrentDomain.IsFinalizingForUnload()))
965 CleanUp();
969 #if !MONO
970 internal sealed class RegisteredWaitHandleSafe : CriticalFinalizerObject
972 private static IntPtr InvalidHandle
974 [System.Security.SecuritySafeCritical] // auto-generated
977 return new IntPtr(-1);
980 private IntPtr registeredWaitHandle;
981 private WaitHandle m_internalWaitObject;
982 private bool bReleaseNeeded = false;
983 private volatile int m_lock = 0;
985 #if FEATURE_CORECLR
986 [System.Security.SecuritySafeCritical] // auto-generated
987 #endif
988 internal RegisteredWaitHandleSafe()
990 registeredWaitHandle = InvalidHandle;
993 internal IntPtr GetHandle()
995 return registeredWaitHandle;
998 internal void SetHandle(IntPtr handle)
1000 registeredWaitHandle = handle;
1003 [System.Security.SecurityCritical] // auto-generated
1004 [ResourceExposure(ResourceScope.None)]
1005 [ResourceConsumption(ResourceScope.Machine, ResourceScope.Machine)]
1006 [ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)]
1007 internal void SetWaitObject(WaitHandle waitObject)
1009 // needed for DangerousAddRef
1010 RuntimeHelpers.PrepareConstrainedRegions();
1014 finally
1016 m_internalWaitObject = waitObject;
1017 if (waitObject != null)
1019 m_internalWaitObject.SafeWaitHandle.DangerousAddRef(ref bReleaseNeeded);
1024 [System.Security.SecurityCritical] // auto-generated
1025 [ResourceExposure(ResourceScope.None)]
1026 [ResourceConsumption(ResourceScope.Machine, ResourceScope.Machine)]
1027 [ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)]
1028 internal bool Unregister(
1029 WaitHandle waitObject // object to be notified when all callbacks to delegates have completed
1032 bool result = false;
1033 // needed for DangerousRelease
1034 RuntimeHelpers.PrepareConstrainedRegions();
1038 finally
1040 // lock(this) cannot be used reliably in Cer since thin lock could be
1041 // promoted to syncblock and that is not a guaranteed operation
1042 bool bLockTaken = false;
1045 if (Interlocked.CompareExchange(ref m_lock, 1, 0) == 0)
1047 bLockTaken = true;
1050 if (ValidHandle())
1052 result = UnregisterWaitNative(GetHandle(), waitObject == null ? null : waitObject.SafeWaitHandle);
1053 if (result == true)
1055 if (bReleaseNeeded)
1057 m_internalWaitObject.SafeWaitHandle.DangerousRelease();
1058 bReleaseNeeded = false;
1060 // if result not true don't release/suppress here so finalizer can make another attempt
1061 SetHandle(InvalidHandle);
1062 m_internalWaitObject = null;
1063 GC.SuppressFinalize(this);
1067 finally
1069 m_lock = 0;
1072 Thread.SpinWait(1); // yield to processor
1074 while (!bLockTaken);
1076 return result;
1079 private bool ValidHandle()
1081 return (registeredWaitHandle != InvalidHandle && registeredWaitHandle != IntPtr.Zero);
1084 [System.Security.SecuritySafeCritical] // auto-generated
1085 [ResourceExposure(ResourceScope.None)]
1086 [ResourceConsumption(ResourceScope.Machine, ResourceScope.Machine)]
1087 ~RegisteredWaitHandleSafe()
1089 // if the app has already unregistered the wait, there is nothing to cleanup
1090 // we can detect this by checking the handle. Normally, there is no ---- here
1091 // so no need to protect reading of handle. However, if this object gets
1092 // resurrected and then someone does an unregister, it would introduce a ----
1094 // PrepareConstrainedRegions call not needed since finalizer already in Cer
1096 // lock(this) cannot be used reliably even in Cer since thin lock could be
1097 // promoted to syncblock and that is not a guaranteed operation
1099 // Note that we will not "spin" to get this lock. We make only a single attempt;
1100 // if we can't get the lock, it means some other thread is in the middle of a call
1101 // to Unregister, which will do the work of the finalizer anyway.
1103 // Further, it's actually critical that we *not* wait for the lock here, because
1104 // the other thread that's in the middle of Unregister may be suspended for shutdown.
1105 // Then, during the live-object finalization phase of shutdown, this thread would
1106 // end up spinning forever, as the other thread would never release the lock.
1107 // This will result in a "leak" of sorts (since the handle will not be cleaned up)
1108 // but the process is exiting anyway.
1110 // During AD-unload, we don’t finalize live objects until all threads have been
1111 // aborted out of the AD. Since these locked regions are CERs, we won’t abort them
1112 // while the lock is held. So there should be no leak on AD-unload.
1114 if (Interlocked.CompareExchange(ref m_lock, 1, 0) == 0)
1118 if (ValidHandle())
1120 WaitHandleCleanupNative(registeredWaitHandle);
1121 if (bReleaseNeeded)
1123 m_internalWaitObject.SafeWaitHandle.DangerousRelease();
1124 bReleaseNeeded = false;
1126 SetHandle(InvalidHandle);
1127 m_internalWaitObject = null;
1130 finally
1132 m_lock = 0;
1137 [System.Security.SecurityCritical] // auto-generated
1138 [ResourceExposure(ResourceScope.Machine)]
1139 [MethodImplAttribute(MethodImplOptions.InternalCall)]
1140 private static extern void WaitHandleCleanupNative(IntPtr handle);
1142 [System.Security.SecurityCritical] // auto-generated
1143 [ResourceExposure(ResourceScope.Machine)]
1144 [MethodImplAttribute(MethodImplOptions.InternalCall)]
1145 private static extern bool UnregisterWaitNative(IntPtr handle, SafeHandle waitObject);
1148 [System.Runtime.InteropServices.ComVisible(true)]
1149 #if FEATURE_REMOTING
1150 public sealed class RegisteredWaitHandle : MarshalByRefObject {
1151 #else // FEATURE_REMOTING
1152 public sealed class RegisteredWaitHandle {
1153 #endif // FEATURE_REMOTING
1154 private RegisteredWaitHandleSafe internalRegisteredWait;
1156 internal RegisteredWaitHandle()
1158 internalRegisteredWait = new RegisteredWaitHandleSafe();
1161 internal void SetHandle(IntPtr handle)
1163 internalRegisteredWait.SetHandle(handle);
1166 [System.Security.SecurityCritical] // auto-generated
1167 internal void SetWaitObject(WaitHandle waitObject)
1169 internalRegisteredWait.SetWaitObject(waitObject);
1172 [System.Security.SecuritySafeCritical] // auto-generated
1173 [System.Runtime.InteropServices.ComVisible(true)]
1174 // This is the only public method on this class
1175 public bool Unregister(
1176 WaitHandle waitObject // object to be notified when all callbacks to delegates have completed
1179 return internalRegisteredWait.Unregister(waitObject);
1182 #endif // !MONO
1185 // This type is necessary because VS 2010's debugger looks for a method named _ThreadPoolWaitCallbacck.PerformWaitCallback
1186 // 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
1187 // still need to maintain compatibility with VS 2010. When compat with VS 2010 is no longer an issue, this type may be
1188 // removed.
1190 internal static class _ThreadPoolWaitCallback
1192 #if FEATURE_INTERCEPTABLE_THREADPOOL_CALLBACK
1193 // This feature is used by Xamarin.iOS to use an NSAutoreleasePool
1194 // for every task done by the threadpool.
1195 static Func<Func<bool>, bool> dispatcher;
1197 internal static void SetDispatcher (Func<Func<bool>, bool> value)
1199 dispatcher = value;
1201 #endif
1203 [System.Security.SecurityCritical]
1204 static internal bool PerformWaitCallback()
1206 #if FEATURE_INTERCEPTABLE_THREADPOOL_CALLBACK
1207 // store locally first to ensure another thread doesn't clear the field between checking for null and using it.
1208 var dispatcher = _ThreadPoolWaitCallback.dispatcher;
1209 if (dispatcher != null)
1210 return dispatcher (ThreadPoolWorkQueue.Dispatch);
1211 #endif
1213 return ThreadPoolWorkQueue.Dispatch();
1217 internal sealed class QueueUserWorkItemCallback : IThreadPoolWorkItem
1219 [System.Security.SecuritySafeCritical]
1220 static QueueUserWorkItemCallback() {}
1222 private WaitCallback callback;
1223 private ExecutionContext context;
1224 private Object state;
1226 #if DEBUG
1227 volatile int executed;
1229 ~QueueUserWorkItemCallback()
1231 Contract.Assert(
1232 executed != 0 || Environment.HasShutdownStarted || AppDomain.CurrentDomain.IsFinalizingForUnload(),
1233 "A QueueUserWorkItemCallback was never called!");
1236 void MarkExecuted(bool aborted)
1238 GC.SuppressFinalize(this);
1239 Contract.Assert(
1240 0 == Interlocked.Exchange(ref executed, 1) || aborted,
1241 "A QueueUserWorkItemCallback was called twice!");
1243 #endif
1245 [SecurityCritical]
1246 internal QueueUserWorkItemCallback(WaitCallback waitCallback, Object stateObj, bool compressStack, ref StackCrawlMark stackMark)
1248 callback = waitCallback;
1249 state = stateObj;
1250 if (compressStack && !ExecutionContext.IsFlowSuppressed())
1252 // clone the exection context
1253 context = ExecutionContext.Capture(
1254 ref stackMark,
1255 ExecutionContext.CaptureOptions.IgnoreSyncCtx | ExecutionContext.CaptureOptions.OptimizeDefaultCase);
1260 // internal test hook - used by tests to exercise work-stealing, etc.
1262 internal QueueUserWorkItemCallback(WaitCallback waitCallback, Object stateObj, ExecutionContext ec)
1264 callback = waitCallback;
1265 state = stateObj;
1266 context = ec;
1269 [SecurityCritical]
1270 void IThreadPoolWorkItem.ExecuteWorkItem()
1272 #if DEBUG
1273 MarkExecuted(false);
1274 #endif
1276 // call directly if it is an unsafe call OR EC flow is suppressed
1277 if (context == null)
1279 WaitCallback cb = callback;
1280 callback = null;
1281 cb(state);
1283 else
1285 ExecutionContext.Run(context, ccb, this, true);
1289 [SecurityCritical]
1290 void IThreadPoolWorkItem.MarkAborted(ThreadAbortException tae)
1292 #if DEBUG
1293 // this workitem didn't execute because we got a ThreadAbortException prior to the call to ExecuteWorkItem.
1294 // This counts as being executed for our purposes.
1295 MarkExecuted(true);
1296 #endif
1299 [System.Security.SecurityCritical]
1300 static internal ContextCallback ccb = new ContextCallback(WaitCallback_Context);
1302 [System.Security.SecurityCritical]
1303 static private void WaitCallback_Context(Object state)
1305 QueueUserWorkItemCallback obj = (QueueUserWorkItemCallback)state;
1306 WaitCallback wc = obj.callback as WaitCallback;
1307 Contract.Assert(null != wc);
1308 wc(obj.state);
1312 internal class _ThreadPoolWaitOrTimerCallback
1314 [System.Security.SecuritySafeCritical]
1315 static _ThreadPoolWaitOrTimerCallback() {}
1317 WaitOrTimerCallback _waitOrTimerCallback;
1318 ExecutionContext _executionContext;
1319 Object _state;
1320 [System.Security.SecurityCritical]
1321 static private ContextCallback _ccbt = new ContextCallback(WaitOrTimerCallback_Context_t);
1322 [System.Security.SecurityCritical]
1323 static private ContextCallback _ccbf = new ContextCallback(WaitOrTimerCallback_Context_f);
1325 [System.Security.SecurityCritical] // auto-generated
1326 internal _ThreadPoolWaitOrTimerCallback(WaitOrTimerCallback waitOrTimerCallback, Object state, bool compressStack, ref StackCrawlMark stackMark)
1328 _waitOrTimerCallback = waitOrTimerCallback;
1329 _state = state;
1331 if (compressStack && !ExecutionContext.IsFlowSuppressed())
1333 // capture the exection context
1334 _executionContext = ExecutionContext.Capture(
1335 ref stackMark,
1336 ExecutionContext.CaptureOptions.IgnoreSyncCtx | ExecutionContext.CaptureOptions.OptimizeDefaultCase);
1340 [System.Security.SecurityCritical]
1341 static private void WaitOrTimerCallback_Context_t(Object state)
1343 WaitOrTimerCallback_Context(state, true);
1346 [System.Security.SecurityCritical]
1347 static private void WaitOrTimerCallback_Context_f(Object state)
1349 WaitOrTimerCallback_Context(state, false);
1352 static private void WaitOrTimerCallback_Context(Object state, bool timedOut)
1354 _ThreadPoolWaitOrTimerCallback helper = (_ThreadPoolWaitOrTimerCallback)state;
1355 helper._waitOrTimerCallback(helper._state, timedOut);
1358 // call back helper
1359 [System.Security.SecurityCritical] // auto-generated
1360 static internal void PerformWaitOrTimerCallback(Object state, bool timedOut)
1362 _ThreadPoolWaitOrTimerCallback helper = (_ThreadPoolWaitOrTimerCallback)state;
1363 Contract.Assert(helper != null, "Null state passed to PerformWaitOrTimerCallback!");
1364 // call directly if it is an unsafe call OR EC flow is suppressed
1365 if (helper._executionContext == null)
1367 WaitOrTimerCallback callback = helper._waitOrTimerCallback;
1368 callback(helper._state, timedOut);
1370 else
1372 using (ExecutionContext executionContext = helper._executionContext.CreateCopy())
1374 if (timedOut)
1375 ExecutionContext.Run(executionContext, _ccbt, helper, true);
1376 else
1377 ExecutionContext.Run(executionContext, _ccbf, helper, true);
1384 [HostProtection(Synchronization=true, ExternalThreading=true)]
1385 public static partial class ThreadPool
1387 #if FEATURE_CORECLR
1388 [System.Security.SecurityCritical] // auto-generated
1389 #else
1390 [System.Security.SecuritySafeCritical]
1391 #endif
1392 #pragma warning disable 618
1393 [SecurityPermissionAttribute(SecurityAction.Demand, ControlThread = true)]
1394 #pragma warning restore 618
1395 public static bool SetMaxThreads(int workerThreads, int completionPortThreads)
1397 return SetMaxThreadsNative(workerThreads, completionPortThreads);
1400 [System.Security.SecuritySafeCritical] // auto-generated
1401 public static void GetMaxThreads(out int workerThreads, out int completionPortThreads)
1403 GetMaxThreadsNative(out workerThreads, out completionPortThreads);
1406 #if FEATURE_CORECLR
1407 [System.Security.SecurityCritical] // auto-generated
1408 #else
1409 [System.Security.SecuritySafeCritical]
1410 #endif
1411 #pragma warning disable 618
1412 [SecurityPermissionAttribute(SecurityAction.Demand, ControlThread = true)]
1413 #pragma warning restore 618
1414 public static bool SetMinThreads(int workerThreads, int completionPortThreads)
1416 return SetMinThreadsNative(workerThreads, completionPortThreads);
1419 [System.Security.SecuritySafeCritical] // auto-generated
1420 public static void GetMinThreads(out int workerThreads, out int completionPortThreads)
1422 GetMinThreadsNative(out workerThreads, out completionPortThreads);
1425 [System.Security.SecuritySafeCritical] // auto-generated
1426 public static void GetAvailableThreads(out int workerThreads, out int completionPortThreads)
1428 GetAvailableThreadsNative(out workerThreads, out completionPortThreads);
1431 [System.Security.SecuritySafeCritical] // auto-generated
1432 [CLSCompliant(false)]
1433 [MethodImplAttribute(MethodImplOptions.NoInlining)] // Methods containing StackCrawlMark local var has to be marked non-inlineable
1434 public static RegisteredWaitHandle RegisterWaitForSingleObject( // throws RegisterWaitException
1435 WaitHandle waitObject,
1436 WaitOrTimerCallback callBack,
1437 Object state,
1438 uint millisecondsTimeOutInterval,
1439 bool executeOnlyOnce // NOTE: we do not allow other options that allow the callback to be queued as an APC
1442 StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller;
1443 return RegisterWaitForSingleObject(waitObject,callBack,state,millisecondsTimeOutInterval,executeOnlyOnce,ref stackMark,true);
1446 [System.Security.SecurityCritical] // auto-generated_required
1447 [CLSCompliant(false)]
1448 [MethodImplAttribute(MethodImplOptions.NoInlining)] // Methods containing StackCrawlMark local var has to be marked non-inlineable
1449 public static RegisteredWaitHandle UnsafeRegisterWaitForSingleObject( // throws RegisterWaitException
1450 WaitHandle waitObject,
1451 WaitOrTimerCallback callBack,
1452 Object state,
1453 uint millisecondsTimeOutInterval,
1454 bool executeOnlyOnce // NOTE: we do not allow other options that allow the callback to be queued as an APC
1457 StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller;
1458 return RegisterWaitForSingleObject(waitObject,callBack,state,millisecondsTimeOutInterval,executeOnlyOnce,ref stackMark,false);
1462 [System.Security.SecurityCritical] // auto-generated
1463 private static RegisteredWaitHandle RegisterWaitForSingleObject( // throws RegisterWaitException
1464 WaitHandle waitObject,
1465 WaitOrTimerCallback callBack,
1466 Object state,
1467 uint millisecondsTimeOutInterval,
1468 bool executeOnlyOnce, // NOTE: we do not allow other options that allow the callback to be queued as an APC
1469 ref StackCrawlMark stackMark,
1470 bool compressStack
1473 #if !MONO
1474 #if FEATURE_REMOTING
1475 if (RemotingServices.IsTransparentProxy(waitObject))
1476 throw new InvalidOperationException(Environment.GetResourceString("InvalidOperation_WaitOnTransparentProxy"));
1477 Contract.EndContractBlock();
1478 #endif
1480 RegisteredWaitHandle registeredWaitHandle = new RegisteredWaitHandle();
1482 if (callBack != null)
1484 _ThreadPoolWaitOrTimerCallback callBackHelper = new _ThreadPoolWaitOrTimerCallback(callBack, state, compressStack, ref stackMark);
1485 state = (Object)callBackHelper;
1486 // call SetWaitObject before native call so that waitObject won't be closed before threadpoolmgr registration
1487 // this could occur if callback were to fire before SetWaitObject does its addref
1488 registeredWaitHandle.SetWaitObject(waitObject);
1489 IntPtr nativeRegisteredWaitHandle = RegisterWaitForSingleObjectNative(waitObject,
1490 state,
1491 millisecondsTimeOutInterval,
1492 executeOnlyOnce,
1493 registeredWaitHandle,
1494 ref stackMark,
1495 compressStack);
1496 registeredWaitHandle.SetHandle(nativeRegisteredWaitHandle);
1498 else
1500 throw new ArgumentNullException("WaitOrTimerCallback");
1502 return registeredWaitHandle;
1503 #else
1504 if (waitObject == null)
1505 throw new ArgumentNullException ("waitObject");
1506 if (callBack == null)
1507 throw new ArgumentNullException ("callBack");
1508 if (millisecondsTimeOutInterval != Timeout.UnsignedInfinite && millisecondsTimeOutInterval > Int32.MaxValue)
1509 throw new NotSupportedException ("Timeout is too big. Maximum is Int32.MaxValue");
1511 RegisteredWaitHandle waiter = new RegisteredWaitHandle (waitObject, callBack, state, new TimeSpan (0, 0, 0, 0, (int) millisecondsTimeOutInterval), executeOnlyOnce);
1512 if (compressStack)
1513 QueueUserWorkItem (new WaitCallback (waiter.Wait), null);
1514 else
1515 UnsafeQueueUserWorkItem (new WaitCallback (waiter.Wait), null);
1517 return waiter;
1518 #endif
1522 [System.Security.SecuritySafeCritical] // auto-generated
1523 [MethodImplAttribute(MethodImplOptions.NoInlining)] // Methods containing StackCrawlMark local var has to be marked non-inlineable
1524 public static RegisteredWaitHandle RegisterWaitForSingleObject( // throws RegisterWaitException
1525 WaitHandle waitObject,
1526 WaitOrTimerCallback callBack,
1527 Object state,
1528 int millisecondsTimeOutInterval,
1529 bool executeOnlyOnce // NOTE: we do not allow other options that allow the callback to be queued as an APC
1532 if (millisecondsTimeOutInterval < -1)
1533 throw new ArgumentOutOfRangeException("millisecondsTimeOutInterval", Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegOrNegative1"));
1534 Contract.EndContractBlock();
1535 StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller;
1536 return RegisterWaitForSingleObject(waitObject,callBack,state,millisecondsTimeOutInterval == Timeout.Infinite ? Timeout.UnsignedInfinite : (UInt32)millisecondsTimeOutInterval,executeOnlyOnce,ref stackMark,true);
1539 [System.Security.SecurityCritical] // auto-generated_required
1540 [MethodImplAttribute(MethodImplOptions.NoInlining)] // Methods containing StackCrawlMark local var has to be marked non-inlineable
1541 public static RegisteredWaitHandle UnsafeRegisterWaitForSingleObject( // throws RegisterWaitException
1542 WaitHandle waitObject,
1543 WaitOrTimerCallback callBack,
1544 Object state,
1545 int millisecondsTimeOutInterval,
1546 bool executeOnlyOnce // NOTE: we do not allow other options that allow the callback to be queued as an APC
1549 if (millisecondsTimeOutInterval < -1)
1550 throw new ArgumentOutOfRangeException("millisecondsTimeOutInterval", Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegOrNegative1"));
1551 Contract.EndContractBlock();
1552 StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller;
1553 return RegisterWaitForSingleObject(waitObject,callBack,state,millisecondsTimeOutInterval == Timeout.Infinite ? Timeout.UnsignedInfinite : (UInt32)millisecondsTimeOutInterval,executeOnlyOnce,ref stackMark,false);
1556 [System.Security.SecuritySafeCritical] // auto-generated
1557 [MethodImplAttribute(MethodImplOptions.NoInlining)] // Methods containing StackCrawlMark local var has to be marked non-inlineable
1558 public static RegisteredWaitHandle RegisterWaitForSingleObject( // throws RegisterWaitException
1559 WaitHandle waitObject,
1560 WaitOrTimerCallback callBack,
1561 Object state,
1562 long millisecondsTimeOutInterval,
1563 bool executeOnlyOnce // NOTE: we do not allow other options that allow the callback to be queued as an APC
1566 if (millisecondsTimeOutInterval < -1)
1567 throw new ArgumentOutOfRangeException("millisecondsTimeOutInterval", Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegOrNegative1"));
1568 Contract.EndContractBlock();
1569 StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller;
1570 return RegisterWaitForSingleObject(waitObject,callBack,state,millisecondsTimeOutInterval == Timeout.Infinite ? Timeout.UnsignedInfinite : (UInt32)millisecondsTimeOutInterval,executeOnlyOnce,ref stackMark,true);
1573 [System.Security.SecurityCritical] // auto-generated_required
1574 [MethodImplAttribute(MethodImplOptions.NoInlining)] // Methods containing StackCrawlMark local var has to be marked non-inlineable
1575 public static RegisteredWaitHandle UnsafeRegisterWaitForSingleObject( // throws RegisterWaitException
1576 WaitHandle waitObject,
1577 WaitOrTimerCallback callBack,
1578 Object state,
1579 long millisecondsTimeOutInterval,
1580 bool executeOnlyOnce // NOTE: we do not allow other options that allow the callback to be queued as an APC
1583 if (millisecondsTimeOutInterval < -1)
1584 throw new ArgumentOutOfRangeException("millisecondsTimeOutInterval", Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegOrNegative1"));
1585 Contract.EndContractBlock();
1586 StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller;
1587 return RegisterWaitForSingleObject(waitObject,callBack,state,millisecondsTimeOutInterval == Timeout.Infinite ? Timeout.UnsignedInfinite : (UInt32)millisecondsTimeOutInterval,executeOnlyOnce,ref stackMark,false);
1590 [System.Security.SecuritySafeCritical] // auto-generated
1591 [MethodImplAttribute(MethodImplOptions.NoInlining)] // Methods containing StackCrawlMark local var has to be marked non-inlineable
1592 public static RegisteredWaitHandle RegisterWaitForSingleObject(
1593 WaitHandle waitObject,
1594 WaitOrTimerCallback callBack,
1595 Object state,
1596 TimeSpan timeout,
1597 bool executeOnlyOnce
1600 long tm = (long)timeout.TotalMilliseconds;
1601 if (tm < -1)
1602 throw new ArgumentOutOfRangeException("timeout", Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegOrNegative1"));
1603 if (tm > (long) Int32.MaxValue)
1604 throw new ArgumentOutOfRangeException("timeout", Environment.GetResourceString("ArgumentOutOfRange_LessEqualToIntegerMaxVal"));
1605 StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller;
1606 return RegisterWaitForSingleObject(waitObject,callBack,state,(UInt32)tm,executeOnlyOnce,ref stackMark,true);
1609 [System.Security.SecurityCritical] // auto-generated_required
1610 [MethodImplAttribute(MethodImplOptions.NoInlining)] // Methods containing StackCrawlMark local var has to be marked non-inlineable
1611 public static RegisteredWaitHandle UnsafeRegisterWaitForSingleObject(
1612 WaitHandle waitObject,
1613 WaitOrTimerCallback callBack,
1614 Object state,
1615 TimeSpan timeout,
1616 bool executeOnlyOnce
1619 long tm = (long)timeout.TotalMilliseconds;
1620 if (tm < -1)
1621 throw new ArgumentOutOfRangeException("timeout", Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegOrNegative1"));
1622 if (tm > (long) Int32.MaxValue)
1623 throw new ArgumentOutOfRangeException("timeout", Environment.GetResourceString("ArgumentOutOfRange_LessEqualToIntegerMaxVal"));
1624 StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller;
1625 return RegisterWaitForSingleObject(waitObject,callBack,state,(UInt32)tm,executeOnlyOnce,ref stackMark,false);
1628 [System.Security.SecuritySafeCritical] // auto-generated
1629 [MethodImplAttribute(MethodImplOptions.NoInlining)] // Methods containing StackCrawlMark local var has to be marked non-inlineable
1630 public static bool QueueUserWorkItem(
1631 WaitCallback callBack, // NOTE: we do not expose options that allow the callback to be queued as an APC
1632 Object state
1635 StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller;
1636 return QueueUserWorkItemHelper(callBack,state,ref stackMark,true);
1639 [System.Security.SecuritySafeCritical] // auto-generated
1640 [MethodImplAttribute(MethodImplOptions.NoInlining)] // Methods containing StackCrawlMark local var has to be marked non-inlineable
1641 public static bool QueueUserWorkItem(
1642 WaitCallback callBack // NOTE: we do not expose options that allow the callback to be queued as an APC
1645 StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller;
1646 return QueueUserWorkItemHelper(callBack,null,ref stackMark,true);
1649 [System.Security.SecurityCritical] // auto-generated_required
1650 [MethodImplAttribute(MethodImplOptions.NoInlining)] // Methods containing StackCrawlMark local var has to be marked non-inlineable
1651 public static bool UnsafeQueueUserWorkItem(
1652 WaitCallback callBack, // NOTE: we do not expose options that allow the callback to be queued as an APC
1653 Object state
1656 StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller;
1657 return QueueUserWorkItemHelper(callBack,state,ref stackMark,false);
1660 //ThreadPool has per-appdomain managed queue of work-items. The VM is
1661 //responsible for just scheduling threads into appdomains. After that
1662 //work-items are dispatched from the managed queue.
1663 [System.Security.SecurityCritical] // auto-generated
1664 private static bool QueueUserWorkItemHelper(WaitCallback callBack, Object state, ref StackCrawlMark stackMark, bool compressStack )
1666 bool success = true;
1668 if (callBack != null)
1670 //The thread pool maintains a per-appdomain managed work queue.
1671 //New thread pool entries are added in the managed queue.
1672 //The VM is responsible for the actual growing/shrinking of
1673 //threads.
1675 EnsureVMInitialized();
1678 // If we are able to create the workitem, we need to get it in the queue without being interrupted
1679 // by a ThreadAbortException.
1681 try { }
1682 finally
1684 QueueUserWorkItemCallback tpcallBack = new QueueUserWorkItemCallback(callBack, state, compressStack, ref stackMark);
1685 ThreadPoolGlobals.workQueue.Enqueue(tpcallBack, true);
1686 success = true;
1689 else
1691 throw new ArgumentNullException("WaitCallback");
1693 return success;
1696 [SecurityCritical]
1697 internal static void UnsafeQueueCustomWorkItem(IThreadPoolWorkItem workItem, bool forceGlobal)
1699 Contract.Assert(null != workItem);
1700 EnsureVMInitialized();
1703 // Enqueue needs to be protected from ThreadAbort
1705 try { }
1706 finally
1708 ThreadPoolGlobals.workQueue.Enqueue(workItem, forceGlobal);
1712 // This method tries to take the target callback out of the current thread's queue.
1713 [SecurityCritical]
1714 internal static bool TryPopCustomWorkItem(IThreadPoolWorkItem workItem)
1716 Contract.Assert(null != workItem);
1717 if (!ThreadPoolGlobals.vmTpInitialized)
1718 return false; //Not initialized, so there's no way this workitem was ever queued.
1719 return ThreadPoolGlobals.workQueue.LocalFindAndPop(workItem);
1722 // Get all workitems. Called by TaskScheduler in its debugger hooks.
1723 [SecurityCritical]
1724 internal static IEnumerable<IThreadPoolWorkItem> GetQueuedWorkItems()
1726 return EnumerateQueuedWorkItems(ThreadPoolWorkQueue.allThreadQueues.Current, ThreadPoolGlobals.workQueue.queueTail);
1729 internal static IEnumerable<IThreadPoolWorkItem> EnumerateQueuedWorkItems(ThreadPoolWorkQueue.WorkStealingQueue[] wsQueues, ThreadPoolWorkQueue.QueueSegment globalQueueTail)
1731 if (wsQueues != null)
1733 // First, enumerate all workitems in thread-local queues.
1734 foreach (ThreadPoolWorkQueue.WorkStealingQueue wsq in wsQueues)
1736 if (wsq != null && wsq.m_array != null)
1738 IThreadPoolWorkItem[] items = wsq.m_array;
1739 for (int i = 0; i < items.Length; i++)
1741 IThreadPoolWorkItem item = items[i];
1742 if (item != null)
1743 yield return item;
1749 if (globalQueueTail != null)
1751 // Now the global queue
1752 for (ThreadPoolWorkQueue.QueueSegment segment = globalQueueTail;
1753 segment != null;
1754 segment = segment.Next)
1756 IThreadPoolWorkItem[] items = segment.nodes;
1757 for (int i = 0; i < items.Length; i++)
1759 IThreadPoolWorkItem item = items[i];
1760 if (item != null)
1761 yield return item;
1767 [SecurityCritical]
1768 internal static IEnumerable<IThreadPoolWorkItem> GetLocallyQueuedWorkItems()
1770 return EnumerateQueuedWorkItems(new ThreadPoolWorkQueue.WorkStealingQueue[] { ThreadPoolWorkQueueThreadLocals.threadLocals.workStealingQueue }, null);
1773 [SecurityCritical]
1774 internal static IEnumerable<IThreadPoolWorkItem> GetGloballyQueuedWorkItems()
1776 return EnumerateQueuedWorkItems(null, ThreadPoolGlobals.workQueue.queueTail);
1779 private static object[] ToObjectArray(IEnumerable<IThreadPoolWorkItem> workitems)
1781 int i = 0;
1782 foreach (IThreadPoolWorkItem item in workitems)
1784 i++;
1787 object[] result = new object[i];
1788 i = 0;
1789 foreach (IThreadPoolWorkItem item in workitems)
1791 if (i < result.Length) //just in case someone calls us while the queues are in motion
1792 result[i] = item;
1793 i++;
1796 return result;
1799 // This is the method the debugger will actually call, if it ends up calling
1800 // into ThreadPool directly. Tests can use this to simulate a debugger, as well.
1801 [SecurityCritical]
1802 internal static object[] GetQueuedWorkItemsForDebugger()
1804 return ToObjectArray(GetQueuedWorkItems());
1807 [SecurityCritical]
1808 internal static object[] GetGloballyQueuedWorkItemsForDebugger()
1810 return ToObjectArray(GetGloballyQueuedWorkItems());
1813 [SecurityCritical]
1814 internal static object[] GetLocallyQueuedWorkItemsForDebugger()
1816 return ToObjectArray(GetLocallyQueuedWorkItems());
1819 [System.Security.SecurityCritical] // auto-generated
1820 [ResourceExposure(ResourceScope.None)]
1821 [MethodImplAttribute(MethodImplOptions.InternalCall)]
1822 internal static extern bool RequestWorkerThread();
1824 [System.Security.SecurityCritical] // auto-generated
1825 [ResourceExposure(ResourceScope.None)]
1826 [MethodImplAttribute(MethodImplOptions.InternalCall)]
1827 unsafe private static extern bool PostQueuedCompletionStatus(NativeOverlapped* overlapped);
1829 [System.Security.SecurityCritical] // auto-generated_required
1830 [CLSCompliant(false)]
1831 unsafe public static bool UnsafeQueueNativeOverlapped(NativeOverlapped* overlapped)
1833 #if FEATURE_CORECLR && !FEATURE_LEGACYNETCF
1834 if(Environment.OSVersion.Platform == PlatformID.MacOSX)
1835 throw new NotSupportedException(Environment.GetResourceString("Arg_NotSupportedException"));
1836 Contract.EndContractBlock();
1837 #endif
1839 return PostQueuedCompletionStatus(overlapped);
1842 [SecurityCritical]
1843 private static void EnsureVMInitialized()
1845 if (!ThreadPoolGlobals.vmTpInitialized)
1847 ThreadPool.InitializeVMTp(ref ThreadPoolGlobals.enableWorkerTracking);
1848 ThreadPoolGlobals.vmTpInitialized = true;
1852 // Native methods:
1854 [System.Security.SecurityCritical] // auto-generated
1855 [ResourceExposure(ResourceScope.None)]
1856 [MethodImplAttribute(MethodImplOptions.InternalCall)]
1857 private static extern bool SetMinThreadsNative(int workerThreads, int completionPortThreads);
1859 [System.Security.SecurityCritical] // auto-generated
1860 [ResourceExposure(ResourceScope.None)]
1861 [MethodImplAttribute(MethodImplOptions.InternalCall)]
1862 private static extern bool SetMaxThreadsNative(int workerThreads, int completionPortThreads);
1864 [System.Security.SecurityCritical] // auto-generated
1865 [ResourceExposure(ResourceScope.None)]
1866 [MethodImplAttribute(MethodImplOptions.InternalCall)]
1867 private static extern void GetMinThreadsNative(out int workerThreads, out int completionPortThreads);
1869 [System.Security.SecurityCritical] // auto-generated
1870 [ResourceExposure(ResourceScope.None)]
1871 [MethodImplAttribute(MethodImplOptions.InternalCall)]
1872 private static extern void GetMaxThreadsNative(out int workerThreads, out int completionPortThreads);
1874 [System.Security.SecurityCritical] // auto-generated
1875 [ResourceExposure(ResourceScope.None)]
1876 [MethodImplAttribute(MethodImplOptions.InternalCall)]
1877 private static extern void GetAvailableThreadsNative(out int workerThreads, out int completionPortThreads);
1879 [System.Security.SecurityCritical] // auto-generated
1880 [ResourceExposure(ResourceScope.None)]
1881 [MethodImplAttribute(MethodImplOptions.InternalCall)]
1882 internal static extern bool NotifyWorkItemComplete();
1884 [System.Security.SecurityCritical]
1885 [ResourceExposure(ResourceScope.None)]
1886 [MethodImplAttribute(MethodImplOptions.InternalCall)]
1887 internal static extern void ReportThreadStatus(bool isWorking);
1889 [System.Security.SecuritySafeCritical]
1890 internal static void NotifyWorkItemProgress()
1892 if (!ThreadPoolGlobals.vmTpInitialized)
1893 ThreadPool.InitializeVMTp(ref ThreadPoolGlobals.enableWorkerTracking);
1894 NotifyWorkItemProgressNative();
1897 [System.Security.SecurityCritical] // auto-generated
1898 [ResourceExposure(ResourceScope.None)]
1899 [MethodImplAttribute(MethodImplOptions.InternalCall)]
1900 internal static extern void NotifyWorkItemProgressNative();
1902 #if MONO
1903 [System.Security.SecurityCritical]
1904 [ResourceExposure(ResourceScope.None)]
1905 [MethodImplAttribute(MethodImplOptions.InternalCall)]
1906 internal static extern void NotifyWorkItemQueued();
1907 #endif
1909 [System.Security.SecurityCritical] // auto-generated
1910 [ResourceExposure(ResourceScope.None)]
1911 [MethodImplAttribute(MethodImplOptions.InternalCall)]
1912 internal static extern bool IsThreadPoolHosted();
1914 [System.Security.SecurityCritical] // auto-generated
1915 [ResourceExposure(ResourceScope.None)]
1916 [MethodImplAttribute(MethodImplOptions.InternalCall)]
1917 private static extern void InitializeVMTp(ref bool enableWorkerTracking);
1919 #if !MONO
1920 [System.Security.SecurityCritical] // auto-generated
1921 [ResourceExposure(ResourceScope.None)]
1922 [MethodImplAttribute(MethodImplOptions.InternalCall)]
1923 private static extern IntPtr RegisterWaitForSingleObjectNative(
1924 WaitHandle waitHandle,
1925 Object state,
1926 uint timeOutInterval,
1927 bool executeOnlyOnce,
1928 RegisteredWaitHandle registeredWaitHandle,
1929 ref StackCrawlMark stackMark,
1930 bool compressStack
1932 #endif
1934 #if !FEATURE_CORECLR
1935 [System.Security.SecuritySafeCritical] // auto-generated
1936 [Obsolete("ThreadPool.BindHandle(IntPtr) has been deprecated. Please use ThreadPool.BindHandle(SafeHandle) instead.", false)]
1937 [SecurityPermissionAttribute( SecurityAction.Demand, Flags = SecurityPermissionFlag.UnmanagedCode)]
1938 public static bool BindHandle(
1939 IntPtr osHandle
1942 return BindIOCompletionCallbackNative(osHandle);
1944 #endif
1946 #if FEATURE_CORECLR
1947 [System.Security.SecurityCritical] // auto-generated
1948 #else
1949 [System.Security.SecuritySafeCritical]
1950 #endif
1951 #pragma warning disable 618
1952 [SecurityPermissionAttribute(SecurityAction.Demand, Flags = SecurityPermissionFlag.UnmanagedCode)]
1953 #pragma warning restore 618
1954 public static bool BindHandle(SafeHandle osHandle)
1956 #if FEATURE_CORECLR && !FEATURE_LEGACYNETCF
1957 if(Environment.OSVersion.Platform == PlatformID.MacOSX)
1958 throw new NotSupportedException(Environment.GetResourceString("Arg_NotSupportedException"));
1959 Contract.EndContractBlock();
1960 #endif
1962 if (osHandle == null)
1963 throw new ArgumentNullException("osHandle");
1965 bool ret = false;
1966 bool mustReleaseSafeHandle = false;
1967 RuntimeHelpers.PrepareConstrainedRegions();
1968 try {
1969 osHandle.DangerousAddRef(ref mustReleaseSafeHandle);
1970 ret = BindIOCompletionCallbackNative(osHandle.DangerousGetHandle());
1972 finally {
1973 if (mustReleaseSafeHandle)
1974 osHandle.DangerousRelease();
1976 return ret;
1979 [System.Security.SecurityCritical] // auto-generated
1980 [ResourceExposure(ResourceScope.None)]
1981 [MethodImplAttribute(MethodImplOptions.InternalCall)]
1982 [ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)]
1983 private static extern bool BindIOCompletionCallbackNative(IntPtr fileHandle);