[netcore] Implement missing bits in ThreadPool (#13566)
[mono-project.git] / mcs / class / referencesource / mscorlib / system / threading / threadpool.cs
blobd67296a5cb645ed43d1f01d4268cec420a0e8057
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 #if !MONO
38 using System.Security.Permissions;
39 #endif
40 using System;
41 using System.Runtime.CompilerServices;
42 using System.Runtime.ConstrainedExecution;
43 using System.Runtime.InteropServices;
44 using System.Runtime.Versioning;
45 using System.Collections.Generic;
46 using System.Diagnostics.Contracts;
47 using System.Diagnostics.CodeAnalysis;
48 using System.Diagnostics.Tracing;
49 #if !MONO
50 using Microsoft.Win32;
51 #endif
53 #if !NETCORE
55 // Interface to something that can be queued to the TP. This is implemented by
56 // QueueUserWorkItemCallback, Task, and potentially other internal types.
57 // For example, SemaphoreSlim represents callbacks using its own type that
58 // implements IThreadPoolWorkItem.
60 // If we decide to expose some of the workstealing
61 // stuff, this is NOT the thing we want to expose to the public.
63 internal interface IThreadPoolWorkItem
65 [SecurityCritical]
66 void ExecuteWorkItem();
67 [SecurityCritical]
68 void MarkAborted(ThreadAbortException tae);
70 #endif
72 [System.Runtime.InteropServices.ComVisible(true)]
73 public delegate void WaitCallback(Object state);
75 [System.Runtime.InteropServices.ComVisible(true)]
76 public delegate void WaitOrTimerCallback(Object state, bool timedOut); // signalled or timed out
78 [System.Security.SecurityCritical]
79 [CLSCompliant(false)]
80 [System.Runtime.InteropServices.ComVisible(true)]
81 unsafe public delegate void IOCompletionCallback(uint errorCode, uint numBytes, NativeOverlapped* pOVERLAP);
83 internal static class ThreadPoolGlobals
85 #if MONO
86 public const uint tpQuantum = 30U;
88 public static int processorCount => Environment.ProcessorCount;
90 public static bool tpHosted => ThreadPool.IsThreadPoolHosted();
92 public static volatile bool vmTpInitialized;
93 public static bool enableWorkerTracking;
95 [SecurityCritical]
96 public static readonly ThreadPoolWorkQueue workQueue = new ThreadPoolWorkQueue();
99 #if NETCORE
100 /// <summary>Shim used to invoke <see cref="IAsyncStateMachineBox.MoveNext"/> of the supplied <see cref="IAsyncStateMachineBox"/>.</summary>
101 internal static readonly Action<object> s_invokeAsyncStateMachineBox = state =>
103 if (!(state is IAsyncStateMachineBox box))
105 ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.state);
106 return;
109 box.MoveNext();
111 #endif
113 #else
114 //Per-appDomain quantum (in ms) for which the thread keeps processing
115 //requests in the current domain.
116 public static uint tpQuantum = 30U;
118 public static int processorCount = Environment.ProcessorCount;
120 public static bool tpHosted = ThreadPool.IsThreadPoolHosted();
122 public static volatile bool vmTpInitialized;
123 public static bool enableWorkerTracking;
125 [SecurityCritical]
126 public static ThreadPoolWorkQueue workQueue = new ThreadPoolWorkQueue();
128 [System.Security.SecuritySafeCritical] // static constructors should be safe to call
129 static ThreadPoolGlobals()
132 #endif
135 internal sealed class ThreadPoolWorkQueue
137 // Simple sparsely populated array to allow lock-free reading.
138 internal class SparseArray<T> where T : class
140 private volatile T[] m_array;
142 internal SparseArray(int initialSize)
144 m_array = new T[initialSize];
147 internal T[] Current
149 get { return m_array; }
152 internal int Add(T e)
154 while (true)
156 T[] array = m_array;
157 lock (array)
159 for (int i = 0; i < array.Length; i++)
161 if (array[i] == null)
163 Volatile.Write(ref array[i], e);
164 return i;
166 else if (i == array.Length - 1)
168 // Must resize. If we raced and lost, we start over again.
169 if (array != m_array)
170 continue;
172 T[] newArray = new T[array.Length * 2];
173 Array.Copy(array, newArray, i + 1);
174 newArray[i + 1] = e;
175 m_array = newArray;
176 return i + 1;
183 internal void Remove(T e)
185 T[] array = m_array;
186 lock (array)
188 for (int i = 0; i < m_array.Length; i++)
190 if (m_array[i] == e)
192 Volatile.Write(ref m_array[i], null);
193 break;
200 internal class WorkStealingQueue
202 private const int INITIAL_SIZE = 32;
203 internal volatile IThreadPoolWorkItem[] m_array = new IThreadPoolWorkItem[INITIAL_SIZE];
204 private volatile int m_mask = INITIAL_SIZE - 1;
206 #if DEBUG
207 // in debug builds, start at the end so we exercise the index reset logic.
208 private const int START_INDEX = int.MaxValue;
209 #else
210 private const int START_INDEX = 0;
211 #endif
213 private volatile int m_headIndex = START_INDEX;
214 private volatile int m_tailIndex = START_INDEX;
216 private SpinLock m_foreignLock = new SpinLock(false);
218 public void LocalPush(IThreadPoolWorkItem obj)
220 int tail = m_tailIndex;
222 // We're going to increment the tail; if we'll overflow, then we need to reset our counts
223 if (tail == int.MaxValue)
225 bool lockTaken = false;
228 m_foreignLock.Enter(ref lockTaken);
230 if (m_tailIndex == int.MaxValue)
233 // Rather than resetting to zero, we'll just mask off the bits we don't care about.
234 // This way we don't need to rearrange the items already in the queue; they'll be found
235 // correctly exactly where they are. One subtlety here is that we need to make sure that
236 // if head is currently < tail, it remains that way. This happens to just fall out from
237 // the bit-masking, because we only do this if tail == int.MaxValue, meaning that all
238 // bits are set, so all of the bits we're keeping will also be set. Thus it's impossible
239 // for the head to end up > than the tail, since you can't set any more bits than all of
240 // them.
242 m_headIndex = m_headIndex & m_mask;
243 m_tailIndex = tail = m_tailIndex & m_mask;
244 Contract.Assert(m_headIndex <= m_tailIndex);
247 finally
249 if (lockTaken)
250 m_foreignLock.Exit(true);
254 // When there are at least 2 elements' worth of space, we can take the fast path.
255 if (tail < m_headIndex + m_mask)
257 Volatile.Write(ref m_array[tail & m_mask], obj);
258 m_tailIndex = tail + 1;
260 else
262 // We need to contend with foreign pops, so we lock.
263 bool lockTaken = false;
266 m_foreignLock.Enter(ref lockTaken);
268 int head = m_headIndex;
269 int count = m_tailIndex - m_headIndex;
271 // If there is still space (one left), just add the element.
272 if (count >= m_mask)
274 // We're full; expand the queue by doubling its size.
275 IThreadPoolWorkItem[] newArray = new IThreadPoolWorkItem[m_array.Length << 1];
276 for (int i = 0; i < m_array.Length; i++)
277 newArray[i] = m_array[(i + head) & m_mask];
279 // Reset the field values, incl. the mask.
280 m_array = newArray;
281 m_headIndex = 0;
282 m_tailIndex = tail = count;
283 m_mask = (m_mask << 1) | 1;
286 Volatile.Write(ref m_array[tail & m_mask], obj);
287 m_tailIndex = tail + 1;
289 finally
291 if (lockTaken)
292 m_foreignLock.Exit(false);
297 [SuppressMessage("Microsoft.Concurrency", "CA8001", Justification = "Reviewed for thread safety")]
298 public bool LocalFindAndPop(IThreadPoolWorkItem obj)
300 // Fast path: check the tail. If equal, we can skip the lock.
301 if (m_array[(m_tailIndex - 1) & m_mask] == obj)
303 IThreadPoolWorkItem unused;
304 if (LocalPop(out unused))
306 Contract.Assert(unused == obj);
307 return true;
309 return false;
312 // Else, do an O(N) search for the work item. The theory of work stealing and our
313 // inlining logic is that most waits will happen on recently queued work. And
314 // since recently queued work will be close to the tail end (which is where we
315 // begin our search), we will likely find it quickly. In the worst case, we
316 // will traverse the whole local queue; this is typically not going to be a
317 // problem (although degenerate cases are clearly an issue) because local work
318 // queues tend to be somewhat shallow in length, and because if we fail to find
319 // the work item, we are about to block anyway (which is very expensive).
320 for (int i = m_tailIndex - 2; i >= m_headIndex; i--)
322 if (m_array[i & m_mask] == obj)
324 // If we found the element, block out steals to avoid interference.
325 // @
326 bool lockTaken = false;
329 m_foreignLock.Enter(ref lockTaken);
331 // If we lost the ----, bail.
332 if (m_array[i & m_mask] == null)
333 return false;
335 // Otherwise, null out the element.
336 Volatile.Write(ref m_array[i & m_mask], null);
338 // And then check to see if we can fix up the indexes (if we're at
339 // the edge). If we can't, we just leave nulls in the array and they'll
340 // get filtered out eventually (but may lead to superflous resizing).
341 if (i == m_tailIndex)
342 m_tailIndex -= 1;
343 else if (i == m_headIndex)
344 m_headIndex += 1;
346 return true;
348 finally
350 if (lockTaken)
351 m_foreignLock.Exit(false);
356 return false;
359 [SuppressMessage("Microsoft.Concurrency", "CA8001", Justification = "Reviewed for thread safety")]
360 public bool LocalPop(out IThreadPoolWorkItem obj)
362 while (true)
364 // Decrement the tail using a fence to ensure subsequent read doesn't come before.
365 int tail = m_tailIndex;
366 if (m_headIndex >= tail)
368 obj = null;
369 return false;
372 tail -= 1;
373 Interlocked.Exchange(ref m_tailIndex, tail);
375 // If there is no interaction with a take, we can head down the fast path.
376 if (m_headIndex <= tail)
378 int idx = tail & m_mask;
379 obj = Volatile.Read(ref m_array[idx]);
381 // Check for nulls in the array.
382 if (obj == null) continue;
384 m_array[idx] = null;
385 return true;
387 else
389 // Interaction with takes: 0 or 1 elements left.
390 bool lockTaken = false;
393 m_foreignLock.Enter(ref lockTaken);
395 if (m_headIndex <= tail)
397 // Element still available. Take it.
398 int idx = tail & m_mask;
399 obj = Volatile.Read(ref m_array[idx]);
401 // Check for nulls in the array.
402 if (obj == null) continue;
404 m_array[idx] = null;
405 return true;
407 else
409 // We lost the ----, element was stolen, restore the tail.
410 m_tailIndex = tail + 1;
411 obj = null;
412 return false;
415 finally
417 if (lockTaken)
418 m_foreignLock.Exit(false);
424 public bool TrySteal(out IThreadPoolWorkItem obj, ref bool missedSteal)
426 return TrySteal(out obj, ref missedSteal, 0); // no blocking by default.
429 private bool TrySteal(out IThreadPoolWorkItem obj, ref bool missedSteal, int millisecondsTimeout)
431 obj = null;
433 while (true)
435 if (m_headIndex >= m_tailIndex)
436 return false;
438 bool taken = false;
441 m_foreignLock.TryEnter(millisecondsTimeout, ref taken);
442 if (taken)
444 // Increment head, and ensure read of tail doesn't move before it (fence).
445 int head = m_headIndex;
446 Interlocked.Exchange(ref m_headIndex, head + 1);
448 if (head < m_tailIndex)
450 int idx = head & m_mask;
451 obj = Volatile.Read(ref m_array[idx]);
453 // Check for nulls in the array.
454 if (obj == null) continue;
456 m_array[idx] = null;
457 return true;
459 else
461 // Failed, restore head.
462 m_headIndex = head;
463 obj = null;
464 missedSteal = true;
467 else
469 missedSteal = true;
472 finally
474 if (taken)
475 m_foreignLock.Exit(false);
478 return false;
483 internal class QueueSegment
485 // Holds a segment of the queue. Enqueues/Dequeues start at element 0, and work their way up.
486 internal readonly IThreadPoolWorkItem[] nodes;
487 private const int QueueSegmentLength = 256;
489 // Holds the indexes of the lowest and highest valid elements of the nodes array.
490 // The low index is in the lower 16 bits, high index is in the upper 16 bits.
491 // Use GetIndexes and CompareExchangeIndexes to manipulate this.
492 private volatile int indexes;
494 // The next segment in the queue.
495 public volatile QueueSegment Next;
498 const int SixteenBits = 0xffff;
500 void GetIndexes(out int upper, out int lower)
502 int i = indexes;
503 upper = (i >> 16) & SixteenBits;
504 lower = i & SixteenBits;
506 Contract.Assert(upper >= lower);
507 Contract.Assert(upper <= nodes.Length);
508 Contract.Assert(lower <= nodes.Length);
509 Contract.Assert(upper >= 0);
510 Contract.Assert(lower >= 0);
513 bool CompareExchangeIndexes(ref int prevUpper, int newUpper, ref int prevLower, int newLower)
515 Contract.Assert(newUpper >= newLower);
516 Contract.Assert(newUpper <= nodes.Length);
517 Contract.Assert(newLower <= nodes.Length);
518 Contract.Assert(newUpper >= 0);
519 Contract.Assert(newLower >= 0);
520 Contract.Assert(newUpper >= prevUpper);
521 Contract.Assert(newLower >= prevLower);
522 Contract.Assert(newUpper == prevUpper ^ newLower == prevLower);
524 int oldIndexes = (prevUpper << 16) | (prevLower & SixteenBits);
525 int newIndexes = (newUpper << 16) | (newLower & SixteenBits);
526 int prevIndexes = Interlocked.CompareExchange(ref indexes, newIndexes, oldIndexes);
527 prevUpper = (prevIndexes >> 16) & SixteenBits;
528 prevLower = prevIndexes & SixteenBits;
529 return prevIndexes == oldIndexes;
532 [ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)]
533 public QueueSegment()
535 Contract.Assert(QueueSegmentLength <= SixteenBits);
536 nodes = new IThreadPoolWorkItem[QueueSegmentLength];
540 public bool IsUsedUp()
542 int upper, lower;
543 GetIndexes(out upper, out lower);
544 return (upper == nodes.Length) &&
545 (lower == nodes.Length);
548 public bool TryEnqueue(IThreadPoolWorkItem node)
551 // If there's room in this segment, atomically increment the upper count (to reserve
552 // space for this node), then store the node.
553 // Note that this leaves a window where it will look like there is data in that
554 // array slot, but it hasn't been written yet. This is taken care of in TryDequeue
555 // with a busy-wait loop, waiting for the element to become non-null. This implies
556 // that we can never store null nodes in this data structure.
558 Contract.Assert(null != node);
560 int upper, lower;
561 GetIndexes(out upper, out lower);
563 while (true)
565 if (upper == nodes.Length)
566 return false;
568 if (CompareExchangeIndexes(ref upper, upper + 1, ref lower, lower))
570 Contract.Assert(Volatile.Read(ref nodes[upper]) == null);
571 Volatile.Write(ref nodes[upper], node);
572 return true;
577 [SuppressMessage("Microsoft.Concurrency", "CA8001", Justification = "Reviewed for thread safety")]
578 public bool TryDequeue(out IThreadPoolWorkItem node)
581 // If there are nodes in this segment, increment the lower count, then take the
582 // element we find there.
584 int upper, lower;
585 GetIndexes(out upper, out lower);
587 while(true)
589 if (lower == upper)
591 node = null;
592 return false;
595 if (CompareExchangeIndexes(ref upper, upper, ref lower, lower + 1))
597 // It's possible that a concurrent call to Enqueue hasn't yet
598 // written the node reference to the array. We need to spin until
599 // it shows up.
600 SpinWait spinner = new SpinWait();
601 while ((node = Volatile.Read(ref nodes[lower])) == null)
602 spinner.SpinOnce();
604 // Null-out the reference so the object can be GC'd earlier.
605 nodes[lower] = null;
607 return true;
613 // The head and tail of the queue. We enqueue to the head, and dequeue from the tail.
614 internal volatile QueueSegment queueHead;
615 internal volatile QueueSegment queueTail;
616 #if !MONO
617 internal bool loggingEnabled;
618 #endif
620 internal static SparseArray<WorkStealingQueue> allThreadQueues = new SparseArray<WorkStealingQueue>(16); //
622 private volatile int numOutstandingThreadRequests = 0;
624 public ThreadPoolWorkQueue()
626 queueTail = queueHead = new QueueSegment();
627 #if !MONO
628 loggingEnabled = FrameworkEventSource.Log.IsEnabled(EventLevel.Verbose, FrameworkEventSource.Keywords.ThreadPool|FrameworkEventSource.Keywords.ThreadTransfer);
629 #endif
632 [SecurityCritical]
633 public ThreadPoolWorkQueueThreadLocals EnsureCurrentThreadHasQueue()
635 if (null == ThreadPoolWorkQueueThreadLocals.threadLocals)
636 ThreadPoolWorkQueueThreadLocals.threadLocals = new ThreadPoolWorkQueueThreadLocals(this);
637 return ThreadPoolWorkQueueThreadLocals.threadLocals;
640 [SecurityCritical]
641 internal void EnsureThreadRequested()
644 // If we have not yet requested #procs threads from the VM, then request a new thread.
645 // Note that there is a separate count in the VM which will also be incremented in this case,
646 // which is handled by RequestWorkerThread.
648 int count = numOutstandingThreadRequests;
649 while (count < ThreadPoolGlobals.processorCount)
651 int prev = Interlocked.CompareExchange(ref numOutstandingThreadRequests, count+1, count);
652 if (prev == count)
654 ThreadPool.RequestWorkerThread();
655 break;
657 count = prev;
661 [SecurityCritical]
662 internal void MarkThreadRequestSatisfied()
665 // The VM has called us, so one of our outstanding thread requests has been satisfied.
666 // Decrement the count so that future calls to EnsureThreadRequested will succeed.
667 // Note that there is a separate count in the VM which has already been decremented by the VM
668 // by the time we reach this point.
670 int count = numOutstandingThreadRequests;
671 while (count > 0)
673 int prev = Interlocked.CompareExchange(ref numOutstandingThreadRequests, count - 1, count);
674 if (prev == count)
676 break;
678 count = prev;
682 [SecurityCritical]
683 public void Enqueue(IThreadPoolWorkItem callback, bool forceGlobal)
685 ThreadPoolWorkQueueThreadLocals tl = null;
686 if (!forceGlobal)
687 tl = ThreadPoolWorkQueueThreadLocals.threadLocals;
689 #if !MONO
690 if (loggingEnabled)
691 System.Diagnostics.Tracing.FrameworkEventSource.Log.ThreadPoolEnqueueWorkObject(callback);
692 #endif
693 if (null != tl)
695 tl.workStealingQueue.LocalPush(callback);
697 else
699 QueueSegment head = queueHead;
701 while (!head.TryEnqueue(callback))
703 Interlocked.CompareExchange(ref head.Next, new QueueSegment(), null);
705 while (head.Next != null)
707 Interlocked.CompareExchange(ref queueHead, head.Next, head);
708 head = queueHead;
712 #if MONO
713 ThreadPool.NotifyWorkItemQueued();
714 #endif
715 EnsureThreadRequested();
718 [SecurityCritical]
719 internal bool LocalFindAndPop(IThreadPoolWorkItem callback)
721 ThreadPoolWorkQueueThreadLocals tl = ThreadPoolWorkQueueThreadLocals.threadLocals;
722 if (null == tl)
723 return false;
725 return tl.workStealingQueue.LocalFindAndPop(callback);
728 [SecurityCritical]
729 public void Dequeue(ThreadPoolWorkQueueThreadLocals tl, out IThreadPoolWorkItem callback, out bool missedSteal)
731 callback = null;
732 missedSteal = false;
733 WorkStealingQueue wsq = tl.workStealingQueue;
735 if (wsq.LocalPop(out callback))
736 Contract.Assert(null != callback);
738 if (null == callback)
740 QueueSegment tail = queueTail;
741 while (true)
743 if (tail.TryDequeue(out callback))
745 Contract.Assert(null != callback);
746 break;
749 if (null == tail.Next || !tail.IsUsedUp())
751 break;
753 else
755 Interlocked.CompareExchange(ref queueTail, tail.Next, tail);
756 tail = queueTail;
761 if (null == callback)
763 WorkStealingQueue[] otherQueues = allThreadQueues.Current;
764 int i = tl.random.Next(otherQueues.Length);
765 int c = otherQueues.Length;
766 while (c > 0)
768 WorkStealingQueue otherQueue = Volatile.Read(ref otherQueues[i % otherQueues.Length]);
769 if (otherQueue != null &&
770 otherQueue != wsq &&
771 otherQueue.TrySteal(out callback, ref missedSteal))
773 Contract.Assert(null != callback);
774 break;
776 i++;
777 c--;
782 [SecurityCritical]
783 static internal bool Dispatch()
785 var workQueue = ThreadPoolGlobals.workQueue;
787 // The clock is ticking! We have ThreadPoolGlobals.tpQuantum milliseconds to get some work done, and then
788 // we need to return to the VM.
790 int quantumStartTime = Environment.TickCount;
793 // Update our records to indicate that an outstanding request for a thread has now been fulfilled.
794 // From this point on, we are responsible for requesting another thread if we stop working for any
795 // reason, and we believe there might still be work in the queue.
797 // Note that if this thread is aborted before we get a chance to request another one, the VM will
798 // record a thread request on our behalf. So we don't need to worry about getting aborted right here.
800 workQueue.MarkThreadRequestSatisfied();
802 #if !MONO
803 // Has the desire for logging changed since the last time we entered?
804 workQueue.loggingEnabled = FrameworkEventSource.Log.IsEnabled(EventLevel.Verbose, FrameworkEventSource.Keywords.ThreadPool|FrameworkEventSource.Keywords.ThreadTransfer);
805 #endif
807 // Assume that we're going to need another thread if this one returns to the VM. We'll set this to
808 // false later, but only if we're absolutely certain that the queue is empty.
810 bool needAnotherThread = true;
811 IThreadPoolWorkItem workItem = null;
815 // Set up our thread-local data
817 ThreadPoolWorkQueueThreadLocals tl = workQueue.EnsureCurrentThreadHasQueue();
820 // Loop until our quantum expires.
822 while ((Environment.TickCount - quantumStartTime) < ThreadPoolGlobals.tpQuantum)
825 // Dequeue and EnsureThreadRequested must be protected from ThreadAbortException.
826 // These are fast, so this will not delay aborts/AD-unloads for very long.
828 try { }
829 finally
831 bool missedSteal = false;
832 workQueue.Dequeue(tl, out workItem, out missedSteal);
834 if (workItem == null)
837 // No work. We're going to return to the VM once we leave this protected region.
838 // If we missed a steal, though, there may be more work in the queue.
839 // Instead of looping around and trying again, we'll just request another thread. This way
840 // we won't starve other AppDomains while we spin trying to get locks, and hopefully the thread
841 // that owns the contended work-stealing queue will pick up its own workitems in the meantime,
842 // which will be more efficient than this thread doing it anyway.
844 needAnotherThread = missedSteal;
846 else
849 // If we found work, there may be more work. Ask for another thread so that the other work can be processed
850 // in parallel. Note that this will only ask for a max of #procs threads, so it's safe to call it for every dequeue.
852 workQueue.EnsureThreadRequested();
856 if (workItem == null)
858 // Tell the VM we're returning normally, not because Hill Climbing asked us to return.
859 return true;
861 else
863 #if !MONO
864 if (workQueue.loggingEnabled)
865 System.Diagnostics.Tracing.FrameworkEventSource.Log.ThreadPoolDequeueWorkObject(workItem);
866 #endif
868 // Execute the workitem outside of any finally blocks, so that it can be aborted if needed.
870 if (ThreadPoolGlobals.enableWorkerTracking)
872 bool reportedStatus = false;
875 try { }
876 finally
878 ThreadPool.ReportThreadStatus(true);
879 reportedStatus = true;
881 #if NETCORE
882 workItem.Execute();
883 #else
884 workItem.ExecuteWorkItem();
885 #endif
886 workItem = null;
888 finally
890 if (reportedStatus)
891 ThreadPool.ReportThreadStatus(false);
894 else
896 #if NETCORE
897 workItem.Execute();
898 #else
899 workItem.ExecuteWorkItem();
900 #endif
901 workItem = null;
905 // Notify the VM that we executed this workitem. This is also our opportunity to ask whether Hill Climbing wants
906 // us to return the thread to the pool or not.
908 if (!ThreadPool.NotifyWorkItemComplete())
909 return false;
912 // If we get here, it's because our quantum expired. Tell the VM we're returning normally.
913 return true;
915 catch (ThreadAbortException tae)
918 // This is here to catch the case where this thread is aborted between the time we exit the finally block in the dispatch
919 // loop, and the time we execute the work item. QueueUserWorkItemCallback uses this to update its accounting of whether
920 // it was executed or not (in debug builds only). Task uses this to communicate the ThreadAbortException to anyone
921 // who waits for the task to complete.
923 #if !NETCORE
924 if (workItem != null)
925 workItem.MarkAborted(tae);
926 #endif
929 // In this case, the VM is going to request another thread on our behalf. No need to do it twice.
931 needAnotherThread = false;
932 // throw; //no need to explicitly rethrow a ThreadAbortException, and doing so causes allocations on amd64.
934 finally
937 // If we are exiting for any reason other than that the queue is definitely empty, ask for another
938 // thread to pick up where we left off.
940 if (needAnotherThread)
941 workQueue.EnsureThreadRequested();
944 // 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.
945 Contract.Assert(false);
946 return true;
950 // Holds a WorkStealingQueue, and remmoves it from the list when this object is no longer referened.
951 internal sealed class ThreadPoolWorkQueueThreadLocals
953 [ThreadStatic]
954 [SecurityCritical]
955 public static ThreadPoolWorkQueueThreadLocals threadLocals;
957 public readonly ThreadPoolWorkQueue workQueue;
958 public readonly ThreadPoolWorkQueue.WorkStealingQueue workStealingQueue;
959 public readonly Random random = new Random(Thread.CurrentThread.ManagedThreadId);
961 public ThreadPoolWorkQueueThreadLocals(ThreadPoolWorkQueue tpq)
963 workQueue = tpq;
964 workStealingQueue = new ThreadPoolWorkQueue.WorkStealingQueue();
965 ThreadPoolWorkQueue.allThreadQueues.Add(workStealingQueue);
968 [SecurityCritical]
969 private void CleanUp()
971 if (null != workStealingQueue)
973 if (null != workQueue)
975 bool done = false;
976 while (!done)
978 // Ensure that we won't be aborted between LocalPop and Enqueue.
979 try { }
980 finally
982 IThreadPoolWorkItem cb = null;
983 if (workStealingQueue.LocalPop(out cb))
985 Contract.Assert(null != cb);
986 workQueue.Enqueue(cb, true);
988 else
990 done = true;
996 ThreadPoolWorkQueue.allThreadQueues.Remove(workStealingQueue);
1000 [SecuritySafeCritical]
1001 ~ThreadPoolWorkQueueThreadLocals()
1003 // Since the purpose of calling CleanUp is to transfer any pending workitems into the global
1004 // queue so that they will be executed by another thread, there's no point in doing this cleanup
1005 // if we're in the process of shutting down or unloading the AD. In those cases, the work won't
1006 // execute anyway. And there are subtle ----s involved there that would lead us to do the wrong
1007 // thing anyway. So we'll only clean up if this is a "normal" finalization.
1008 if (!(Environment.HasShutdownStarted
1009 #if !NETCORE
1010 || AppDomain.CurrentDomain.IsFinalizingForUnload()
1011 #endif
1013 CleanUp();
1017 #if !MONO
1018 internal sealed class RegisteredWaitHandleSafe : CriticalFinalizerObject
1020 private static IntPtr InvalidHandle
1022 [System.Security.SecuritySafeCritical] // auto-generated
1025 return new IntPtr(-1);
1028 private IntPtr registeredWaitHandle;
1029 private WaitHandle m_internalWaitObject;
1030 private bool bReleaseNeeded = false;
1031 private volatile int m_lock = 0;
1033 #if FEATURE_CORECLR
1034 [System.Security.SecuritySafeCritical] // auto-generated
1035 #endif
1036 internal RegisteredWaitHandleSafe()
1038 registeredWaitHandle = InvalidHandle;
1041 internal IntPtr GetHandle()
1043 return registeredWaitHandle;
1046 internal void SetHandle(IntPtr handle)
1048 registeredWaitHandle = handle;
1051 [System.Security.SecurityCritical] // auto-generated
1052 // [ResourceExposure(ResourceScope.None)]
1053 // [ResourceConsumption(ResourceScope.Machine, ResourceScope.Machine)]
1054 [ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)]
1055 internal void SetWaitObject(WaitHandle waitObject)
1057 // needed for DangerousAddRef
1058 RuntimeHelpers.PrepareConstrainedRegions();
1062 finally
1064 m_internalWaitObject = waitObject;
1065 if (waitObject != null)
1067 m_internalWaitObject.SafeWaitHandle.DangerousAddRef(ref bReleaseNeeded);
1072 [System.Security.SecurityCritical] // auto-generated
1073 [ResourceExposure(ResourceScope.None)]
1074 [ResourceConsumption(ResourceScope.Machine, ResourceScope.Machine)]
1075 [ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)]
1076 internal bool Unregister(
1077 WaitHandle waitObject // object to be notified when all callbacks to delegates have completed
1080 bool result = false;
1081 // needed for DangerousRelease
1082 RuntimeHelpers.PrepareConstrainedRegions();
1086 finally
1088 // lock(this) cannot be used reliably in Cer since thin lock could be
1089 // promoted to syncblock and that is not a guaranteed operation
1090 bool bLockTaken = false;
1093 if (Interlocked.CompareExchange(ref m_lock, 1, 0) == 0)
1095 bLockTaken = true;
1098 if (ValidHandle())
1100 result = UnregisterWaitNative(GetHandle(), waitObject == null ? null : waitObject.SafeWaitHandle);
1101 if (result == true)
1103 if (bReleaseNeeded)
1105 m_internalWaitObject.SafeWaitHandle.DangerousRelease();
1106 bReleaseNeeded = false;
1108 // if result not true don't release/suppress here so finalizer can make another attempt
1109 SetHandle(InvalidHandle);
1110 m_internalWaitObject = null;
1111 GC.SuppressFinalize(this);
1115 finally
1117 m_lock = 0;
1120 Thread.SpinWait(1); // yield to processor
1122 while (!bLockTaken);
1124 return result;
1127 private bool ValidHandle()
1129 return (registeredWaitHandle != InvalidHandle && registeredWaitHandle != IntPtr.Zero);
1132 [System.Security.SecuritySafeCritical] // auto-generated
1133 [ResourceExposure(ResourceScope.None)]
1134 [ResourceConsumption(ResourceScope.Machine, ResourceScope.Machine)]
1135 ~RegisteredWaitHandleSafe()
1137 // if the app has already unregistered the wait, there is nothing to cleanup
1138 // we can detect this by checking the handle. Normally, there is no ---- here
1139 // so no need to protect reading of handle. However, if this object gets
1140 // resurrected and then someone does an unregister, it would introduce a ----
1142 // PrepareConstrainedRegions call not needed since finalizer already in Cer
1144 // lock(this) cannot be used reliably even in Cer since thin lock could be
1145 // promoted to syncblock and that is not a guaranteed operation
1147 // Note that we will not "spin" to get this lock. We make only a single attempt;
1148 // if we can't get the lock, it means some other thread is in the middle of a call
1149 // to Unregister, which will do the work of the finalizer anyway.
1151 // Further, it's actually critical that we *not* wait for the lock here, because
1152 // the other thread that's in the middle of Unregister may be suspended for shutdown.
1153 // Then, during the live-object finalization phase of shutdown, this thread would
1154 // end up spinning forever, as the other thread would never release the lock.
1155 // This will result in a "leak" of sorts (since the handle will not be cleaned up)
1156 // but the process is exiting anyway.
1158 // During AD-unload, we don�t finalize live objects until all threads have been
1159 // aborted out of the AD. Since these locked regions are CERs, we won�t abort them
1160 // while the lock is held. So there should be no leak on AD-unload.
1162 if (Interlocked.CompareExchange(ref m_lock, 1, 0) == 0)
1166 if (ValidHandle())
1168 WaitHandleCleanupNative(registeredWaitHandle);
1169 if (bReleaseNeeded)
1171 m_internalWaitObject.SafeWaitHandle.DangerousRelease();
1172 bReleaseNeeded = false;
1174 SetHandle(InvalidHandle);
1175 m_internalWaitObject = null;
1178 finally
1180 m_lock = 0;
1185 [System.Security.SecurityCritical] // auto-generated
1186 [ResourceExposure(ResourceScope.Machine)]
1187 [MethodImplAttribute(MethodImplOptions.InternalCall)]
1188 private static extern void WaitHandleCleanupNative(IntPtr handle);
1190 [System.Security.SecurityCritical] // auto-generated
1191 [ResourceExposure(ResourceScope.Machine)]
1192 [MethodImplAttribute(MethodImplOptions.InternalCall)]
1193 private static extern bool UnregisterWaitNative(IntPtr handle, SafeHandle waitObject);
1196 [System.Runtime.InteropServices.ComVisible(true)]
1197 #if FEATURE_REMOTING
1198 public sealed class RegisteredWaitHandle : MarshalByRefObject {
1199 #else // FEATURE_REMOTING
1200 public sealed class RegisteredWaitHandle {
1201 #endif // FEATURE_REMOTING
1202 private RegisteredWaitHandleSafe internalRegisteredWait;
1204 internal RegisteredWaitHandle()
1206 internalRegisteredWait = new RegisteredWaitHandleSafe();
1209 internal void SetHandle(IntPtr handle)
1211 internalRegisteredWait.SetHandle(handle);
1214 [System.Security.SecurityCritical] // auto-generated
1215 internal void SetWaitObject(WaitHandle waitObject)
1217 internalRegisteredWait.SetWaitObject(waitObject);
1220 [System.Security.SecuritySafeCritical] // auto-generated
1221 [System.Runtime.InteropServices.ComVisible(true)]
1222 // This is the only public method on this class
1223 public bool Unregister(
1224 WaitHandle waitObject // object to be notified when all callbacks to delegates have completed
1227 return internalRegisteredWait.Unregister(waitObject);
1230 #endif // !MONO
1233 // This type is necessary because VS 2010's debugger looks for a method named _ThreadPoolWaitCallbacck.PerformWaitCallback
1234 // 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
1235 // still need to maintain compatibility with VS 2010. When compat with VS 2010 is no longer an issue, this type may be
1236 // removed.
1238 internal static class _ThreadPoolWaitCallback
1240 #if FEATURE_INTERCEPTABLE_THREADPOOL_CALLBACK
1241 // This feature is used by Xamarin.iOS to use an NSAutoreleasePool
1242 // for every task done by the threadpool.
1243 static Func<Func<bool>, bool> dispatcher;
1245 internal static void SetDispatcher (Func<Func<bool>, bool> value)
1247 dispatcher = value;
1249 #endif
1251 [System.Security.SecurityCritical]
1252 static internal bool PerformWaitCallback()
1254 #if FEATURE_INTERCEPTABLE_THREADPOOL_CALLBACK
1255 // store locally first to ensure another thread doesn't clear the field between checking for null and using it.
1256 var dispatcher = _ThreadPoolWaitCallback.dispatcher;
1257 if (dispatcher != null)
1258 return dispatcher (ThreadPoolWorkQueue.Dispatch);
1259 #endif
1261 return ThreadPoolWorkQueue.Dispatch();
1265 internal sealed class QueueUserWorkItemCallback : IThreadPoolWorkItem
1267 #if !MONO
1268 [System.Security.SecuritySafeCritical]
1269 static QueueUserWorkItemCallback() {}
1270 #endif
1272 private WaitCallback callback;
1273 private ExecutionContext context;
1274 private Object state;
1276 #if DEBUG
1277 volatile int executed;
1279 ~QueueUserWorkItemCallback()
1281 Contract.Assert(
1282 executed != 0 || Environment.HasShutdownStarted || AppDomain.CurrentDomain.IsFinalizingForUnload(),
1283 "A QueueUserWorkItemCallback was never called!");
1286 void MarkExecuted(bool aborted)
1288 GC.SuppressFinalize(this);
1289 Contract.Assert(
1290 0 == Interlocked.Exchange(ref executed, 1) || aborted,
1291 "A QueueUserWorkItemCallback was called twice!");
1293 #endif
1295 [SecurityCritical]
1296 internal QueueUserWorkItemCallback(WaitCallback waitCallback, Object stateObj, bool compressStack, ref StackCrawlMark stackMark)
1298 callback = waitCallback;
1299 state = stateObj;
1300 if (compressStack && !ExecutionContext.IsFlowSuppressed())
1302 #if !NETCORE
1303 // clone the exection context
1304 context = ExecutionContext.Capture(
1305 ref stackMark,
1306 ExecutionContext.CaptureOptions.IgnoreSyncCtx | ExecutionContext.CaptureOptions.OptimizeDefaultCase);
1307 #endif
1312 // internal test hook - used by tests to exercise work-stealing, etc.
1314 internal QueueUserWorkItemCallback(WaitCallback waitCallback, Object stateObj, ExecutionContext ec)
1316 callback = waitCallback;
1317 state = stateObj;
1318 context = ec;
1321 [SecurityCritical]
1322 #if NETCORE
1323 void IThreadPoolWorkItem.Execute()
1324 #else
1325 void IThreadPoolWorkItem.ExecuteWorkItem()
1326 #endif
1328 #if DEBUG
1329 MarkExecuted(false);
1330 #endif
1332 // call directly if it is an unsafe call OR EC flow is suppressed
1333 if (context == null)
1335 WaitCallback cb = callback;
1336 callback = null;
1337 cb(state);
1339 else
1341 ExecutionContext.Run(context, ccb, this
1342 #if !NETCORE
1343 , true
1344 #endif
1349 #if !NETCORE
1350 [SecurityCritical]
1351 void IThreadPoolWorkItem.MarkAborted(ThreadAbortException tae)
1353 #if DEBUG
1354 // this workitem didn't execute because we got a ThreadAbortException prior to the call to ExecuteWorkItem.
1355 // This counts as being executed for our purposes.
1356 MarkExecuted(true);
1357 #endif
1359 #endif
1361 [System.Security.SecurityCritical]
1362 static internal ContextCallback ccb = new ContextCallback(WaitCallback_Context);
1364 [System.Security.SecurityCritical]
1365 static private void WaitCallback_Context(Object state)
1367 QueueUserWorkItemCallback obj = (QueueUserWorkItemCallback)state;
1368 WaitCallback wc = obj.callback as WaitCallback;
1369 Contract.Assert(null != wc);
1370 wc(obj.state);
1374 internal class _ThreadPoolWaitOrTimerCallback
1376 [System.Security.SecuritySafeCritical]
1377 static _ThreadPoolWaitOrTimerCallback() {}
1379 WaitOrTimerCallback _waitOrTimerCallback;
1380 ExecutionContext _executionContext;
1381 Object _state;
1382 [System.Security.SecurityCritical]
1383 static private ContextCallback _ccbt = new ContextCallback(WaitOrTimerCallback_Context_t);
1384 [System.Security.SecurityCritical]
1385 static private ContextCallback _ccbf = new ContextCallback(WaitOrTimerCallback_Context_f);
1387 [System.Security.SecurityCritical] // auto-generated
1388 internal _ThreadPoolWaitOrTimerCallback(WaitOrTimerCallback waitOrTimerCallback, Object state, bool compressStack, ref StackCrawlMark stackMark)
1390 _waitOrTimerCallback = waitOrTimerCallback;
1391 _state = state;
1393 if (compressStack && !ExecutionContext.IsFlowSuppressed())
1395 #if !NETCORE
1396 // capture the exection context
1397 _executionContext = ExecutionContext.Capture(
1398 ref stackMark,
1399 ExecutionContext.CaptureOptions.IgnoreSyncCtx | ExecutionContext.CaptureOptions.OptimizeDefaultCase);
1400 #endif
1404 [System.Security.SecurityCritical]
1405 static private void WaitOrTimerCallback_Context_t(Object state)
1407 WaitOrTimerCallback_Context(state, true);
1410 [System.Security.SecurityCritical]
1411 static private void WaitOrTimerCallback_Context_f(Object state)
1413 WaitOrTimerCallback_Context(state, false);
1416 static private void WaitOrTimerCallback_Context(Object state, bool timedOut)
1418 _ThreadPoolWaitOrTimerCallback helper = (_ThreadPoolWaitOrTimerCallback)state;
1419 helper._waitOrTimerCallback(helper._state, timedOut);
1422 // call back helper
1423 [System.Security.SecurityCritical] // auto-generated
1424 static internal void PerformWaitOrTimerCallback(Object state, bool timedOut)
1426 _ThreadPoolWaitOrTimerCallback helper = (_ThreadPoolWaitOrTimerCallback)state;
1427 Contract.Assert(helper != null, "Null state passed to PerformWaitOrTimerCallback!");
1428 // call directly if it is an unsafe call OR EC flow is suppressed
1429 if (helper._executionContext == null)
1431 WaitOrTimerCallback callback = helper._waitOrTimerCallback;
1432 callback(helper._state, timedOut);
1434 else
1436 using (ExecutionContext executionContext = helper._executionContext.CreateCopy())
1438 if (timedOut)
1439 ExecutionContext.Run(executionContext, _ccbt, helper
1440 #if !NETCORE
1441 , true
1442 #endif
1444 else
1445 ExecutionContext.Run(executionContext, _ccbf, helper
1446 #if !NETCORE
1447 , true
1448 #endif
1456 // [HostProtection(Synchronization=true, ExternalThreading=true)]
1457 public static partial class ThreadPool
1459 #if FEATURE_CORECLR
1460 [System.Security.SecurityCritical] // auto-generated
1461 #else
1462 [System.Security.SecuritySafeCritical]
1463 #endif
1464 //#pragma warning disable 618
1465 // [SecurityPermissionAttribute(SecurityAction.Demand, ControlThread = true)]
1466 //#pragma warning restore 618
1467 public static bool SetMaxThreads(int workerThreads, int completionPortThreads)
1469 return SetMaxThreadsNative(workerThreads, completionPortThreads);
1472 [System.Security.SecuritySafeCritical] // auto-generated
1473 public static void GetMaxThreads(out int workerThreads, out int completionPortThreads)
1475 GetMaxThreadsNative(out workerThreads, out completionPortThreads);
1478 #if FEATURE_CORECLR
1479 [System.Security.SecurityCritical] // auto-generated
1480 #else
1481 [System.Security.SecuritySafeCritical]
1482 #endif
1483 //#pragma warning disable 618
1484 // [SecurityPermissionAttribute(SecurityAction.Demand, ControlThread = true)]
1485 //#pragma warning restore 618
1486 public static bool SetMinThreads(int workerThreads, int completionPortThreads)
1488 return SetMinThreadsNative(workerThreads, completionPortThreads);
1491 [System.Security.SecuritySafeCritical] // auto-generated
1492 public static void GetMinThreads(out int workerThreads, out int completionPortThreads)
1494 GetMinThreadsNative(out workerThreads, out completionPortThreads);
1497 [System.Security.SecuritySafeCritical] // auto-generated
1498 public static void GetAvailableThreads(out int workerThreads, out int completionPortThreads)
1500 GetAvailableThreadsNative(out workerThreads, out completionPortThreads);
1503 [System.Security.SecuritySafeCritical] // auto-generated
1504 [CLSCompliant(false)]
1505 [MethodImplAttribute(MethodImplOptions.NoInlining)] // Methods containing StackCrawlMark local var has to be marked non-inlineable
1506 public static RegisteredWaitHandle RegisterWaitForSingleObject( // throws RegisterWaitException
1507 WaitHandle waitObject,
1508 WaitOrTimerCallback callBack,
1509 Object state,
1510 uint millisecondsTimeOutInterval,
1511 bool executeOnlyOnce // NOTE: we do not allow other options that allow the callback to be queued as an APC
1514 StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller;
1515 return RegisterWaitForSingleObject(waitObject,callBack,state,millisecondsTimeOutInterval,executeOnlyOnce,ref stackMark,true);
1518 [System.Security.SecurityCritical] // auto-generated_required
1519 [CLSCompliant(false)]
1520 [MethodImplAttribute(MethodImplOptions.NoInlining)] // Methods containing StackCrawlMark local var has to be marked non-inlineable
1521 public static RegisteredWaitHandle UnsafeRegisterWaitForSingleObject( // throws RegisterWaitException
1522 WaitHandle waitObject,
1523 WaitOrTimerCallback callBack,
1524 Object state,
1525 uint millisecondsTimeOutInterval,
1526 bool executeOnlyOnce // NOTE: we do not allow other options that allow the callback to be queued as an APC
1529 StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller;
1530 return RegisterWaitForSingleObject(waitObject,callBack,state,millisecondsTimeOutInterval,executeOnlyOnce,ref stackMark,false);
1534 [System.Security.SecurityCritical] // auto-generated
1535 private static RegisteredWaitHandle RegisterWaitForSingleObject( // throws RegisterWaitException
1536 WaitHandle waitObject,
1537 WaitOrTimerCallback callBack,
1538 Object state,
1539 uint millisecondsTimeOutInterval,
1540 bool executeOnlyOnce, // NOTE: we do not allow other options that allow the callback to be queued as an APC
1541 ref StackCrawlMark stackMark,
1542 bool compressStack
1545 #if !MONO
1546 #if FEATURE_REMOTING
1547 if (RemotingServices.IsTransparentProxy(waitObject))
1548 throw new InvalidOperationException(Environment.GetResourceString("InvalidOperation_WaitOnTransparentProxy"));
1549 Contract.EndContractBlock();
1550 #endif
1552 RegisteredWaitHandle registeredWaitHandle = new RegisteredWaitHandle();
1554 if (callBack != null)
1556 _ThreadPoolWaitOrTimerCallback callBackHelper = new _ThreadPoolWaitOrTimerCallback(callBack, state, compressStack, ref stackMark);
1557 state = (Object)callBackHelper;
1558 // call SetWaitObject before native call so that waitObject won't be closed before threadpoolmgr registration
1559 // this could occur if callback were to fire before SetWaitObject does its addref
1560 registeredWaitHandle.SetWaitObject(waitObject);
1561 IntPtr nativeRegisteredWaitHandle = RegisterWaitForSingleObjectNative(waitObject,
1562 state,
1563 millisecondsTimeOutInterval,
1564 executeOnlyOnce,
1565 registeredWaitHandle,
1566 ref stackMark,
1567 compressStack);
1568 registeredWaitHandle.SetHandle(nativeRegisteredWaitHandle);
1570 else
1572 throw new ArgumentNullException("WaitOrTimerCallback");
1574 return registeredWaitHandle;
1575 #else
1576 if (waitObject == null)
1577 throw new ArgumentNullException ("waitObject");
1578 if (callBack == null)
1579 throw new ArgumentNullException ("callBack");
1580 if (millisecondsTimeOutInterval != Timeout.UnsignedInfinite && millisecondsTimeOutInterval > Int32.MaxValue)
1581 throw new NotSupportedException ("Timeout is too big. Maximum is Int32.MaxValue");
1583 RegisteredWaitHandle waiter = new RegisteredWaitHandle (waitObject, callBack, state, new TimeSpan (0, 0, 0, 0, (int) millisecondsTimeOutInterval), executeOnlyOnce);
1584 if (compressStack)
1585 QueueUserWorkItem (new WaitCallback (waiter.Wait), null);
1586 else
1587 UnsafeQueueUserWorkItem (new WaitCallback (waiter.Wait), null);
1589 return waiter;
1590 #endif
1594 [System.Security.SecuritySafeCritical] // auto-generated
1595 [MethodImplAttribute(MethodImplOptions.NoInlining)] // Methods containing StackCrawlMark local var has to be marked non-inlineable
1596 public static RegisteredWaitHandle RegisterWaitForSingleObject( // throws RegisterWaitException
1597 WaitHandle waitObject,
1598 WaitOrTimerCallback callBack,
1599 Object state,
1600 int millisecondsTimeOutInterval,
1601 bool executeOnlyOnce // NOTE: we do not allow other options that allow the callback to be queued as an APC
1604 if (millisecondsTimeOutInterval < -1)
1605 throw new ArgumentOutOfRangeException("millisecondsTimeOutInterval", Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegOrNegative1"));
1606 Contract.EndContractBlock();
1607 StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller;
1608 return RegisterWaitForSingleObject(waitObject,callBack,state,millisecondsTimeOutInterval == Timeout.Infinite ? Timeout.UnsignedInfinite : (UInt32)millisecondsTimeOutInterval,executeOnlyOnce,ref stackMark,true);
1611 [System.Security.SecurityCritical] // auto-generated_required
1612 [MethodImplAttribute(MethodImplOptions.NoInlining)] // Methods containing StackCrawlMark local var has to be marked non-inlineable
1613 public static RegisteredWaitHandle UnsafeRegisterWaitForSingleObject( // throws RegisterWaitException
1614 WaitHandle waitObject,
1615 WaitOrTimerCallback callBack,
1616 Object state,
1617 int millisecondsTimeOutInterval,
1618 bool executeOnlyOnce // NOTE: we do not allow other options that allow the callback to be queued as an APC
1621 if (millisecondsTimeOutInterval < -1)
1622 throw new ArgumentOutOfRangeException("millisecondsTimeOutInterval", Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegOrNegative1"));
1623 Contract.EndContractBlock();
1624 StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller;
1625 return RegisterWaitForSingleObject(waitObject,callBack,state,millisecondsTimeOutInterval == Timeout.Infinite ? Timeout.UnsignedInfinite : (UInt32)millisecondsTimeOutInterval,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 RegisteredWaitHandle RegisterWaitForSingleObject( // throws RegisterWaitException
1631 WaitHandle waitObject,
1632 WaitOrTimerCallback callBack,
1633 Object state,
1634 long millisecondsTimeOutInterval,
1635 bool executeOnlyOnce // NOTE: we do not allow other options that allow the callback to be queued as an APC
1638 if (millisecondsTimeOutInterval < -1)
1639 throw new ArgumentOutOfRangeException("millisecondsTimeOutInterval", Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegOrNegative1"));
1640 Contract.EndContractBlock();
1641 StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller;
1642 return RegisterWaitForSingleObject(waitObject,callBack,state,millisecondsTimeOutInterval == Timeout.Infinite ? Timeout.UnsignedInfinite : (UInt32)millisecondsTimeOutInterval,executeOnlyOnce,ref stackMark,true);
1645 [System.Security.SecurityCritical] // auto-generated_required
1646 [MethodImplAttribute(MethodImplOptions.NoInlining)] // Methods containing StackCrawlMark local var has to be marked non-inlineable
1647 public static RegisteredWaitHandle UnsafeRegisterWaitForSingleObject( // throws RegisterWaitException
1648 WaitHandle waitObject,
1649 WaitOrTimerCallback callBack,
1650 Object state,
1651 long millisecondsTimeOutInterval,
1652 bool executeOnlyOnce // NOTE: we do not allow other options that allow the callback to be queued as an APC
1655 if (millisecondsTimeOutInterval < -1)
1656 throw new ArgumentOutOfRangeException("millisecondsTimeOutInterval", Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegOrNegative1"));
1657 Contract.EndContractBlock();
1658 StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller;
1659 return RegisterWaitForSingleObject(waitObject,callBack,state,millisecondsTimeOutInterval == Timeout.Infinite ? Timeout.UnsignedInfinite : (UInt32)millisecondsTimeOutInterval,executeOnlyOnce,ref stackMark,false);
1662 [System.Security.SecuritySafeCritical] // auto-generated
1663 [MethodImplAttribute(MethodImplOptions.NoInlining)] // Methods containing StackCrawlMark local var has to be marked non-inlineable
1664 public static RegisteredWaitHandle RegisterWaitForSingleObject(
1665 WaitHandle waitObject,
1666 WaitOrTimerCallback callBack,
1667 Object state,
1668 TimeSpan timeout,
1669 bool executeOnlyOnce
1672 long tm = (long)timeout.TotalMilliseconds;
1673 if (tm < -1)
1674 throw new ArgumentOutOfRangeException("timeout", Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegOrNegative1"));
1675 if (tm > (long) Int32.MaxValue)
1676 throw new ArgumentOutOfRangeException("timeout", Environment.GetResourceString("ArgumentOutOfRange_LessEqualToIntegerMaxVal"));
1677 StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller;
1678 return RegisterWaitForSingleObject(waitObject,callBack,state,(UInt32)tm,executeOnlyOnce,ref stackMark,true);
1681 [System.Security.SecurityCritical] // auto-generated_required
1682 [MethodImplAttribute(MethodImplOptions.NoInlining)] // Methods containing StackCrawlMark local var has to be marked non-inlineable
1683 public static RegisteredWaitHandle UnsafeRegisterWaitForSingleObject(
1684 WaitHandle waitObject,
1685 WaitOrTimerCallback callBack,
1686 Object state,
1687 TimeSpan timeout,
1688 bool executeOnlyOnce
1691 long tm = (long)timeout.TotalMilliseconds;
1692 if (tm < -1)
1693 throw new ArgumentOutOfRangeException("timeout", Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegOrNegative1"));
1694 if (tm > (long) Int32.MaxValue)
1695 throw new ArgumentOutOfRangeException("timeout", Environment.GetResourceString("ArgumentOutOfRange_LessEqualToIntegerMaxVal"));
1696 StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller;
1697 return RegisterWaitForSingleObject(waitObject,callBack,state,(UInt32)tm,executeOnlyOnce,ref stackMark,false);
1700 [System.Security.SecuritySafeCritical] // auto-generated
1701 [MethodImplAttribute(MethodImplOptions.NoInlining)] // Methods containing StackCrawlMark local var has to be marked non-inlineable
1702 public static bool QueueUserWorkItem(
1703 WaitCallback callBack, // NOTE: we do not expose options that allow the callback to be queued as an APC
1704 Object state
1707 StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller;
1708 return QueueUserWorkItemHelper(callBack,state,ref stackMark,true);
1711 [System.Security.SecuritySafeCritical] // auto-generated
1712 [MethodImplAttribute(MethodImplOptions.NoInlining)] // Methods containing StackCrawlMark local var has to be marked non-inlineable
1713 public static bool QueueUserWorkItem(
1714 WaitCallback callBack // NOTE: we do not expose options that allow the callback to be queued as an APC
1717 StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller;
1718 return QueueUserWorkItemHelper(callBack,null,ref stackMark,true);
1721 [System.Security.SecurityCritical] // auto-generated_required
1722 [MethodImplAttribute(MethodImplOptions.NoInlining)] // Methods containing StackCrawlMark local var has to be marked non-inlineable
1723 public static bool UnsafeQueueUserWorkItem(
1724 WaitCallback callBack, // NOTE: we do not expose options that allow the callback to be queued as an APC
1725 Object state
1728 StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller;
1729 return QueueUserWorkItemHelper(callBack,state,ref stackMark,false);
1732 public static bool QueueUserWorkItem<TState>(Action<TState> callBack, TState state, bool preferLocal)
1734 if (callBack == null)
1736 throw new ArgumentNullException(nameof(callBack));
1739 StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller;
1740 WaitCallback waitCallback = x => callBack((TState)x);
1741 return QueueUserWorkItemHelper(waitCallback,state,ref stackMark,true,!preferLocal);
1744 public static bool UnsafeQueueUserWorkItem<TState>(Action<TState> callBack, TState state, bool preferLocal)
1746 if (callBack == null)
1748 throw new ArgumentNullException(nameof(callBack));
1751 StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller;
1752 WaitCallback waitCallback = x => callBack((TState)x);
1753 return QueueUserWorkItemHelper(waitCallback,state,ref stackMark,false,!preferLocal);
1757 //ThreadPool has per-appdomain managed queue of work-items. The VM is
1758 //responsible for just scheduling threads into appdomains. After that
1759 //work-items are dispatched from the managed queue.
1760 [System.Security.SecurityCritical] // auto-generated
1761 private static bool QueueUserWorkItemHelper(WaitCallback callBack, Object state, ref StackCrawlMark stackMark, bool compressStack, bool forceGlobal = true)
1763 bool success = true;
1765 if (callBack != null)
1767 //The thread pool maintains a per-appdomain managed work queue.
1768 //New thread pool entries are added in the managed queue.
1769 //The VM is responsible for the actual growing/shrinking of
1770 //threads.
1772 EnsureVMInitialized();
1775 // If we are able to create the workitem, we need to get it in the queue without being interrupted
1776 // by a ThreadAbortException.
1778 try { }
1779 finally
1781 QueueUserWorkItemCallback tpcallBack = new QueueUserWorkItemCallback(callBack, state, compressStack, ref stackMark);
1782 ThreadPoolGlobals.workQueue.Enqueue(tpcallBack, forceGlobal);
1783 success = true;
1786 else
1788 throw new ArgumentNullException("WaitCallback");
1790 return success;
1793 [SecurityCritical]
1794 internal static void UnsafeQueueCustomWorkItem(IThreadPoolWorkItem workItem, bool forceGlobal)
1796 Contract.Assert(null != workItem);
1797 EnsureVMInitialized();
1800 // Enqueue needs to be protected from ThreadAbort
1802 try { }
1803 finally
1805 ThreadPoolGlobals.workQueue.Enqueue(workItem, forceGlobal);
1809 // This method tries to take the target callback out of the current thread's queue.
1810 [SecurityCritical]
1811 internal static bool TryPopCustomWorkItem(IThreadPoolWorkItem workItem)
1813 Contract.Assert(null != workItem);
1814 if (!ThreadPoolGlobals.vmTpInitialized)
1815 return false; //Not initialized, so there's no way this workitem was ever queued.
1816 return ThreadPoolGlobals.workQueue.LocalFindAndPop(workItem);
1819 // Get all workitems. Called by TaskScheduler in its debugger hooks.
1820 [SecurityCritical]
1821 internal static IEnumerable<IThreadPoolWorkItem> GetQueuedWorkItems()
1823 return EnumerateQueuedWorkItems(ThreadPoolWorkQueue.allThreadQueues.Current, ThreadPoolGlobals.workQueue.queueTail);
1826 internal static IEnumerable<IThreadPoolWorkItem> EnumerateQueuedWorkItems(ThreadPoolWorkQueue.WorkStealingQueue[] wsQueues, ThreadPoolWorkQueue.QueueSegment globalQueueTail)
1828 if (wsQueues != null)
1830 // First, enumerate all workitems in thread-local queues.
1831 foreach (ThreadPoolWorkQueue.WorkStealingQueue wsq in wsQueues)
1833 if (wsq != null && wsq.m_array != null)
1835 IThreadPoolWorkItem[] items = wsq.m_array;
1836 for (int i = 0; i < items.Length; i++)
1838 IThreadPoolWorkItem item = items[i];
1839 if (item != null)
1840 yield return item;
1846 if (globalQueueTail != null)
1848 // Now the global queue
1849 for (ThreadPoolWorkQueue.QueueSegment segment = globalQueueTail;
1850 segment != null;
1851 segment = segment.Next)
1853 IThreadPoolWorkItem[] items = segment.nodes;
1854 for (int i = 0; i < items.Length; i++)
1856 IThreadPoolWorkItem item = items[i];
1857 if (item != null)
1858 yield return item;
1864 [SecurityCritical]
1865 internal static IEnumerable<IThreadPoolWorkItem> GetLocallyQueuedWorkItems()
1867 return EnumerateQueuedWorkItems(new ThreadPoolWorkQueue.WorkStealingQueue[] { ThreadPoolWorkQueueThreadLocals.threadLocals.workStealingQueue }, null);
1870 [SecurityCritical]
1871 internal static IEnumerable<IThreadPoolWorkItem> GetGloballyQueuedWorkItems()
1873 return EnumerateQueuedWorkItems(null, ThreadPoolGlobals.workQueue.queueTail);
1876 private static object[] ToObjectArray(IEnumerable<IThreadPoolWorkItem> workitems)
1878 int i = 0;
1879 foreach (IThreadPoolWorkItem item in workitems)
1881 i++;
1884 object[] result = new object[i];
1885 i = 0;
1886 foreach (IThreadPoolWorkItem item in workitems)
1888 if (i < result.Length) //just in case someone calls us while the queues are in motion
1889 result[i] = item;
1890 i++;
1893 return result;
1896 // This is the method the debugger will actually call, if it ends up calling
1897 // into ThreadPool directly. Tests can use this to simulate a debugger, as well.
1898 [SecurityCritical]
1899 internal static object[] GetQueuedWorkItemsForDebugger()
1901 return ToObjectArray(GetQueuedWorkItems());
1904 [SecurityCritical]
1905 internal static object[] GetGloballyQueuedWorkItemsForDebugger()
1907 return ToObjectArray(GetGloballyQueuedWorkItems());
1910 [SecurityCritical]
1911 internal static object[] GetLocallyQueuedWorkItemsForDebugger()
1913 return ToObjectArray(GetLocallyQueuedWorkItems());
1916 [System.Security.SecurityCritical] // auto-generated
1917 // [ResourceExposure(ResourceScope.None)]
1918 [MethodImplAttribute(MethodImplOptions.InternalCall)]
1919 internal static extern bool RequestWorkerThread();
1921 [System.Security.SecurityCritical] // auto-generated
1922 // [ResourceExposure(ResourceScope.None)]
1923 [MethodImplAttribute(MethodImplOptions.InternalCall)]
1924 unsafe private static extern bool PostQueuedCompletionStatus(NativeOverlapped* overlapped);
1926 [System.Security.SecurityCritical] // auto-generated_required
1927 [CLSCompliant(false)]
1928 unsafe public static bool UnsafeQueueNativeOverlapped(NativeOverlapped* overlapped)
1930 #if FEATURE_CORECLR && !FEATURE_LEGACYNETCF
1931 if(Environment.OSVersion.Platform == PlatformID.MacOSX)
1932 throw new NotSupportedException(Environment.GetResourceString("Arg_NotSupportedException"));
1933 Contract.EndContractBlock();
1934 #endif
1936 return PostQueuedCompletionStatus(overlapped);
1939 [SecurityCritical]
1940 private static void EnsureVMInitialized()
1942 if (!ThreadPoolGlobals.vmTpInitialized)
1944 ThreadPool.InitializeVMTp(ref ThreadPoolGlobals.enableWorkerTracking);
1945 ThreadPoolGlobals.vmTpInitialized = true;
1949 // Native methods:
1951 [System.Security.SecurityCritical] // auto-generated
1952 // [ResourceExposure(ResourceScope.None)]
1953 [MethodImplAttribute(MethodImplOptions.InternalCall)]
1954 private static extern bool SetMinThreadsNative(int workerThreads, int completionPortThreads);
1956 [System.Security.SecurityCritical] // auto-generated
1957 // [ResourceExposure(ResourceScope.None)]
1958 [MethodImplAttribute(MethodImplOptions.InternalCall)]
1959 private static extern bool SetMaxThreadsNative(int workerThreads, int completionPortThreads);
1961 [System.Security.SecurityCritical] // auto-generated
1962 // [ResourceExposure(ResourceScope.None)]
1963 [MethodImplAttribute(MethodImplOptions.InternalCall)]
1964 private static extern void GetMinThreadsNative(out int workerThreads, out int completionPortThreads);
1966 [System.Security.SecurityCritical] // auto-generated
1967 // [ResourceExposure(ResourceScope.None)]
1968 [MethodImplAttribute(MethodImplOptions.InternalCall)]
1969 private static extern void GetMaxThreadsNative(out int workerThreads, out int completionPortThreads);
1971 [System.Security.SecurityCritical] // auto-generated
1972 // [ResourceExposure(ResourceScope.None)]
1973 [MethodImplAttribute(MethodImplOptions.InternalCall)]
1974 private static extern void GetAvailableThreadsNative(out int workerThreads, out int completionPortThreads);
1976 [System.Security.SecurityCritical] // auto-generated
1977 // [ResourceExposure(ResourceScope.None)]
1978 [MethodImplAttribute(MethodImplOptions.InternalCall)]
1979 internal static extern bool NotifyWorkItemComplete();
1981 [System.Security.SecurityCritical]
1982 // [ResourceExposure(ResourceScope.None)]
1983 [MethodImplAttribute(MethodImplOptions.InternalCall)]
1984 internal static extern void ReportThreadStatus(bool isWorking);
1986 [System.Security.SecuritySafeCritical]
1987 internal static void NotifyWorkItemProgress()
1989 EnsureVMInitialized();
1990 NotifyWorkItemProgressNative();
1993 [System.Security.SecurityCritical] // auto-generated
1994 // [ResourceExposure(ResourceScope.None)]
1995 [MethodImplAttribute(MethodImplOptions.InternalCall)]
1996 internal static extern void NotifyWorkItemProgressNative();
1998 #if MONO
1999 [System.Security.SecurityCritical]
2000 // [ResourceExposure(ResourceScope.None)]
2001 [MethodImplAttribute(MethodImplOptions.InternalCall)]
2002 internal static extern void NotifyWorkItemQueued();
2003 #endif
2005 [System.Security.SecurityCritical] // auto-generated
2006 // [ResourceExposure(ResourceScope.None)]
2007 [MethodImplAttribute(MethodImplOptions.InternalCall)]
2008 internal static extern bool IsThreadPoolHosted();
2010 [System.Security.SecurityCritical] // auto-generated
2011 // [ResourceExposure(ResourceScope.None)]
2012 [MethodImplAttribute(MethodImplOptions.InternalCall)]
2013 private static extern void InitializeVMTp(ref bool enableWorkerTracking);
2015 #if !MONO
2016 [System.Security.SecurityCritical] // auto-generated
2017 [ResourceExposure(ResourceScope.None)]
2018 [MethodImplAttribute(MethodImplOptions.InternalCall)]
2019 private static extern IntPtr RegisterWaitForSingleObjectNative(
2020 WaitHandle waitHandle,
2021 Object state,
2022 uint timeOutInterval,
2023 bool executeOnlyOnce,
2024 RegisteredWaitHandle registeredWaitHandle,
2025 ref StackCrawlMark stackMark,
2026 bool compressStack
2028 #endif
2030 #if !FEATURE_CORECLR
2031 [System.Security.SecuritySafeCritical] // auto-generated
2032 [Obsolete("ThreadPool.BindHandle(IntPtr) has been deprecated. Please use ThreadPool.BindHandle(SafeHandle) instead.", false)]
2033 // [SecurityPermissionAttribute( SecurityAction.Demand, Flags = SecurityPermissionFlag.UnmanagedCode)]
2034 public static bool BindHandle(
2035 IntPtr osHandle
2038 return BindIOCompletionCallbackNative(osHandle);
2040 #endif
2042 #if FEATURE_CORECLR
2043 [System.Security.SecurityCritical] // auto-generated
2044 #else
2045 [System.Security.SecuritySafeCritical]
2046 #endif
2047 //#pragma warning disable 618
2048 // [SecurityPermissionAttribute(SecurityAction.Demand, Flags = SecurityPermissionFlag.UnmanagedCode)]
2049 //#pragma warning restore 618
2050 public static bool BindHandle(SafeHandle osHandle)
2052 #if FEATURE_CORECLR && !FEATURE_LEGACYNETCF
2053 if(Environment.OSVersion.Platform == PlatformID.MacOSX)
2054 throw new NotSupportedException(Environment.GetResourceString("Arg_NotSupportedException"));
2055 Contract.EndContractBlock();
2056 #endif
2058 if (osHandle == null)
2059 throw new ArgumentNullException("osHandle");
2061 bool ret = false;
2062 bool mustReleaseSafeHandle = false;
2063 RuntimeHelpers.PrepareConstrainedRegions();
2064 try {
2065 osHandle.DangerousAddRef(ref mustReleaseSafeHandle);
2066 ret = BindIOCompletionCallbackNative(osHandle.DangerousGetHandle());
2068 finally {
2069 if (mustReleaseSafeHandle)
2070 osHandle.DangerousRelease();
2072 return ret;
2075 [System.Security.SecurityCritical] // auto-generated
2076 // [ResourceExposure(ResourceScope.None)]
2077 [MethodImplAttribute(MethodImplOptions.InternalCall)]
2078 [ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)]
2079 private static extern bool BindIOCompletionCallbackNative(IntPtr fileHandle);