3 // Copyright (c) Microsoft Corporation. All rights reserved.
7 // <OWNER>[....]</OWNER>
8 /*=============================================================================
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;
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;
48 using Microsoft.Win32;
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
63 void ExecuteWorkItem();
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]
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;
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];
115 get { return m_array; }
118 internal int Add(T e)
125 for (int i = 0; i < array.Length; i++)
127 if (array[i] == null)
129 Volatile.Write(ref array[i], e);
132 else if (i == array.Length - 1)
134 // Must resize. If we ----d and lost, we start over again.
135 if (array != m_array)
138 T[] newArray = new T[array.Length * 2];
139 Array.Copy(array, newArray, i + 1);
149 internal void Remove(T e)
154 for (int i = 0; i < m_array.Length; i++)
158 Volatile.Write(ref m_array[i], null);
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;
173 // in debug builds, start at the end so we exercise the index reset logic.
174 private const int START_INDEX = int.MaxValue;
176 private const int START_INDEX = 0;
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
208 m_headIndex = m_headIndex & m_mask;
209 m_tailIndex = tail = m_tailIndex & m_mask;
210 Contract.Assert(m_headIndex <= m_tailIndex);
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;
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.
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.
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;
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);
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.
292 bool lockTaken = false;
295 m_foreignLock.Enter(ref lockTaken);
297 // If we lost the ----, bail.
298 if (m_array[i & m_mask] == null)
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)
309 else if (i == m_headIndex)
317 m_foreignLock.Exit(false);
325 [SuppressMessage("Microsoft.Concurrency", "CA8001", Justification = "Reviewed for thread safety")]
326 public bool LocalPop(out IThreadPoolWorkItem obj)
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)
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;
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;
375 // We lost the ----, element was stolen, restore the tail.
376 m_tailIndex = tail + 1;
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)
401 if (m_headIndex >= m_tailIndex)
407 m_foreignLock.TryEnter(millisecondsTimeout, ref 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;
427 // Failed, restore head.
441 m_foreignLock.Exit(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)
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()
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);
527 GetIndexes(out upper, out lower);
531 if (upper == nodes.Length)
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);
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.
551 GetIndexes(out upper, out lower);
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
566 SpinWait spinner = new SpinWait();
567 while ((node = Volatile.Read(ref nodes[lower])) == null)
570 // Null-out the reference so the object can be GC'd earlier.
579 // The head and tail of the queue. We enqueue to the head, and dequeue from the tail.
580 internal volatile QueueSegment queueHead;
581 internal volatile QueueSegment queueTail;
582 internal bool loggingEnabled;
584 internal static SparseArray<WorkStealingQueue> allThreadQueues = new SparseArray<WorkStealingQueue>(16); //
586 private volatile int numOutstandingThreadRequests = 0;
588 public ThreadPoolWorkQueue()
590 queueTail = queueHead = new QueueSegment();
592 loggingEnabled = FrameworkEventSource.Log.IsEnabled(EventLevel.Verbose, FrameworkEventSource.Keywords.ThreadPool|FrameworkEventSource.Keywords.ThreadTransfer);
597 public ThreadPoolWorkQueueThreadLocals EnsureCurrentThreadHasQueue()
599 if (null == ThreadPoolWorkQueueThreadLocals.threadLocals)
600 ThreadPoolWorkQueueThreadLocals.threadLocals = new ThreadPoolWorkQueueThreadLocals(this);
601 return ThreadPoolWorkQueueThreadLocals.threadLocals;
605 internal void EnsureThreadRequested()
608 // If we have not yet requested #procs threads from the VM, then request a new thread.
609 // Note that there is a separate count in the VM which will also be incremented in this case,
610 // which is handled by RequestWorkerThread.
612 int count = numOutstandingThreadRequests;
613 while (count < ThreadPoolGlobals.processorCount)
615 int prev = Interlocked.CompareExchange(ref numOutstandingThreadRequests, count+1, count);
618 ThreadPool.RequestWorkerThread();
626 internal void MarkThreadRequestSatisfied()
629 // The VM has called us, so one of our outstanding thread requests has been satisfied.
630 // Decrement the count so that future calls to EnsureThreadRequested will succeed.
631 // Note that there is a separate count in the VM which has already been decremented by the VM
632 // by the time we reach this point.
634 int count = numOutstandingThreadRequests;
637 int prev = Interlocked.CompareExchange(ref numOutstandingThreadRequests, count - 1, count);
647 public void Enqueue(IThreadPoolWorkItem callback, bool forceGlobal)
649 ThreadPoolWorkQueueThreadLocals tl = null;
651 tl = ThreadPoolWorkQueueThreadLocals.threadLocals;
655 System.Diagnostics.Tracing.FrameworkEventSource.Log.ThreadPoolEnqueueWorkObject(callback);
659 tl.workStealingQueue.LocalPush(callback);
663 QueueSegment head = queueHead;
665 while (!head.TryEnqueue(callback))
667 Interlocked.CompareExchange(ref head.Next, new QueueSegment(), null);
669 while (head.Next != null)
671 Interlocked.CompareExchange(ref queueHead, head.Next, head);
677 EnsureThreadRequested();
681 internal bool LocalFindAndPop(IThreadPoolWorkItem callback)
683 ThreadPoolWorkQueueThreadLocals tl = ThreadPoolWorkQueueThreadLocals.threadLocals;
687 return tl.workStealingQueue.LocalFindAndPop(callback);
691 public void Dequeue(ThreadPoolWorkQueueThreadLocals tl, out IThreadPoolWorkItem callback, out bool missedSteal)
695 WorkStealingQueue wsq = tl.workStealingQueue;
697 if (wsq.LocalPop(out callback))
698 Contract.Assert(null != callback);
700 if (null == callback)
702 QueueSegment tail = queueTail;
705 if (tail.TryDequeue(out callback))
707 Contract.Assert(null != callback);
711 if (null == tail.Next || !tail.IsUsedUp())
717 Interlocked.CompareExchange(ref queueTail, tail.Next, tail);
723 if (null == callback)
725 WorkStealingQueue[] otherQueues = allThreadQueues.Current;
726 int i = tl.random.Next(otherQueues.Length);
727 int c = otherQueues.Length;
730 WorkStealingQueue otherQueue = Volatile.Read(ref otherQueues[i % otherQueues.Length]);
731 if (otherQueue != null &&
733 otherQueue.TrySteal(out callback, ref missedSteal))
735 Contract.Assert(null != callback);
745 static internal bool Dispatch()
747 var workQueue = ThreadPoolGlobals.workQueue;
749 // The clock is ticking! We have ThreadPoolGlobals.tpQuantum milliseconds to get some work done, and then
750 // we need to return to the VM.
752 int quantumStartTime = Environment.TickCount;
755 // Update our records to indicate that an outstanding request for a thread has now been fulfilled.
756 // From this point on, we are responsible for requesting another thread if we stop working for any
757 // reason, and we believe there might still be work in the queue.
759 // Note that if this thread is aborted before we get a chance to request another one, the VM will
760 // record a thread request on our behalf. So we don't need to worry about getting aborted right here.
762 workQueue.MarkThreadRequestSatisfied();
765 // Has the desire for logging changed since the last time we entered?
766 workQueue.loggingEnabled = FrameworkEventSource.Log.IsEnabled(EventLevel.Verbose, FrameworkEventSource.Keywords.ThreadPool|FrameworkEventSource.Keywords.ThreadTransfer);
769 // Assume that we're going to need another thread if this one returns to the VM. We'll set this to
770 // false later, but only if we're absolutely certain that the queue is empty.
772 bool needAnotherThread = true;
773 IThreadPoolWorkItem workItem = null;
777 // Set up our thread-local data
779 ThreadPoolWorkQueueThreadLocals tl = workQueue.EnsureCurrentThreadHasQueue();
782 // Loop until our quantum expires.
784 while ((Environment.TickCount - quantumStartTime) < ThreadPoolGlobals.tpQuantum)
787 // Dequeue and EnsureThreadRequested must be protected from ThreadAbortException.
788 // These are fast, so this will not delay aborts/AD-unloads for very long.
793 bool missedSteal = false;
794 workQueue.Dequeue(tl, out workItem, out missedSteal);
796 if (workItem == null)
799 // No work. We're going to return to the VM once we leave this protected region.
800 // If we missed a steal, though, there may be more work in the queue.
801 // Instead of looping around and trying again, we'll just request another thread. This way
802 // we won't starve other AppDomains while we spin trying to get locks, and hopefully the thread
803 // that owns the contended work-stealing queue will pick up its own workitems in the meantime,
804 // which will be more efficient than this thread doing it anyway.
806 needAnotherThread = missedSteal;
811 // If we found work, there may be more work. Ask for another thread so that the other work can be processed
812 // in parallel. Note that this will only ask for a max of #procs threads, so it's safe to call it for every dequeue.
814 workQueue.EnsureThreadRequested();
818 if (workItem == null)
820 // Tell the VM we're returning normally, not because Hill Climbing asked us to return.
826 if (workQueue.loggingEnabled)
827 System.Diagnostics.Tracing.FrameworkEventSource.Log.ThreadPoolDequeueWorkObject(workItem);
830 // Execute the workitem outside of any finally blocks, so that it can be aborted if needed.
832 if (ThreadPoolGlobals.enableWorkerTracking)
834 bool reportedStatus = false;
840 ThreadPool.ReportThreadStatus(true);
841 reportedStatus = true;
843 workItem.ExecuteWorkItem();
849 ThreadPool.ReportThreadStatus(false);
854 workItem.ExecuteWorkItem();
859 // Notify the VM that we executed this workitem. This is also our opportunity to ask whether Hill Climbing wants
860 // us to return the thread to the pool or not.
862 if (!ThreadPool.NotifyWorkItemComplete())
866 // If we get here, it's because our quantum expired. Tell the VM we're returning normally.
869 catch (ThreadAbortException tae)
872 // This is here to catch the case where this thread is aborted between the time we exit the finally block in the dispatch
873 // loop, and the time we execute the work item. QueueUserWorkItemCallback uses this to update its accounting of whether
874 // it was executed or not (in debug builds only). Task uses this to communicate the ThreadAbortException to anyone
875 // who waits for the task to complete.
877 if (workItem != null)
878 workItem.MarkAborted(tae);
881 // In this case, the VM is going to request another thread on our behalf. No need to do it twice.
883 needAnotherThread = false;
884 // throw; //no need to explicitly rethrow a ThreadAbortException, and doing so causes allocations on amd64.
889 // If we are exiting for any reason other than that the queue is definitely empty, ask for another
890 // thread to pick up where we left off.
892 if (needAnotherThread)
893 workQueue.EnsureThreadRequested();
896 // we can never reach this point, but the C# compiler doesn't know that, because it doesn't know the ThreadAbortException will be reraised above.
897 Contract.Assert(false);
902 // Holds a WorkStealingQueue, and remmoves it from the list when this object is no longer referened.
903 internal sealed class ThreadPoolWorkQueueThreadLocals
907 public static ThreadPoolWorkQueueThreadLocals threadLocals;
909 public readonly ThreadPoolWorkQueue workQueue;
910 public readonly ThreadPoolWorkQueue.WorkStealingQueue workStealingQueue;
911 public readonly Random random = new Random(Thread.CurrentThread.ManagedThreadId);
913 public ThreadPoolWorkQueueThreadLocals(ThreadPoolWorkQueue tpq)
916 workStealingQueue = new ThreadPoolWorkQueue.WorkStealingQueue();
917 ThreadPoolWorkQueue.allThreadQueues.Add(workStealingQueue);
921 private void CleanUp()
923 if (null != workStealingQueue)
925 if (null != workQueue)
930 // Ensure that we won't be aborted between LocalPop and Enqueue.
934 IThreadPoolWorkItem cb = null;
935 if (workStealingQueue.LocalPop(out cb))
937 Contract.Assert(null != cb);
938 workQueue.Enqueue(cb, true);
948 ThreadPoolWorkQueue.allThreadQueues.Remove(workStealingQueue);
952 [SecuritySafeCritical]
953 ~ThreadPoolWorkQueueThreadLocals()
955 // Since the purpose of calling CleanUp is to transfer any pending workitems into the global
956 // queue so that they will be executed by another thread, there's no point in doing this cleanup
957 // if we're in the process of shutting down or unloading the AD. In those cases, the work won't
958 // execute anyway. And there are subtle ----s involved there that would lead us to do the wrong
959 // thing anyway. So we'll only clean up if this is a "normal" finalization.
960 if (!(Environment.HasShutdownStarted || AppDomain.CurrentDomain.IsFinalizingForUnload()))
966 internal sealed class RegisteredWaitHandleSafe : CriticalFinalizerObject
968 private static IntPtr InvalidHandle
970 [System.Security.SecuritySafeCritical] // auto-generated
973 return new IntPtr(-1);
976 private IntPtr registeredWaitHandle;
977 private WaitHandle m_internalWaitObject;
978 private bool bReleaseNeeded = false;
979 private volatile int m_lock = 0;
982 [System.Security.SecuritySafeCritical] // auto-generated
984 internal RegisteredWaitHandleSafe()
986 registeredWaitHandle = InvalidHandle;
989 internal IntPtr GetHandle()
991 return registeredWaitHandle;
994 internal void SetHandle(IntPtr handle)
996 registeredWaitHandle = handle;
999 [System.Security.SecurityCritical] // auto-generated
1000 [ResourceExposure(ResourceScope.None)]
1001 [ResourceConsumption(ResourceScope.Machine, ResourceScope.Machine)]
1002 [ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)]
1003 internal void SetWaitObject(WaitHandle waitObject)
1005 // needed for DangerousAddRef
1006 RuntimeHelpers.PrepareConstrainedRegions();
1012 m_internalWaitObject = waitObject;
1013 if (waitObject != null)
1015 m_internalWaitObject.SafeWaitHandle.DangerousAddRef(ref bReleaseNeeded);
1020 [System.Security.SecurityCritical] // auto-generated
1021 [ResourceExposure(ResourceScope.None)]
1022 [ResourceConsumption(ResourceScope.Machine, ResourceScope.Machine)]
1023 [ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)]
1024 internal bool Unregister(
1025 WaitHandle waitObject // object to be notified when all callbacks to delegates have completed
1028 bool result = false;
1029 // needed for DangerousRelease
1030 RuntimeHelpers.PrepareConstrainedRegions();
1036 // lock(this) cannot be used reliably in Cer since thin lock could be
1037 // promoted to syncblock and that is not a guaranteed operation
1038 bool bLockTaken = false;
1041 if (Interlocked.CompareExchange(ref m_lock, 1, 0) == 0)
1048 result = UnregisterWaitNative(GetHandle(), waitObject == null ? null : waitObject.SafeWaitHandle);
1053 m_internalWaitObject.SafeWaitHandle.DangerousRelease();
1054 bReleaseNeeded = false;
1056 // if result not true don't release/suppress here so finalizer can make another attempt
1057 SetHandle(InvalidHandle);
1058 m_internalWaitObject = null;
1059 GC.SuppressFinalize(this);
1068 Thread.SpinWait(1); // yield to processor
1070 while (!bLockTaken);
1075 private bool ValidHandle()
1077 return (registeredWaitHandle != InvalidHandle && registeredWaitHandle != IntPtr.Zero);
1080 [System.Security.SecuritySafeCritical] // auto-generated
1081 [ResourceExposure(ResourceScope.None)]
1082 [ResourceConsumption(ResourceScope.Machine, ResourceScope.Machine)]
1083 ~RegisteredWaitHandleSafe()
1085 // if the app has already unregistered the wait, there is nothing to cleanup
1086 // we can detect this by checking the handle. Normally, there is no ---- here
1087 // so no need to protect reading of handle. However, if this object gets
1088 // resurrected and then someone does an unregister, it would introduce a ----
1090 // PrepareConstrainedRegions call not needed since finalizer already in Cer
1092 // lock(this) cannot be used reliably even in Cer since thin lock could be
1093 // promoted to syncblock and that is not a guaranteed operation
1095 // Note that we will not "spin" to get this lock. We make only a single attempt;
1096 // if we can't get the lock, it means some other thread is in the middle of a call
1097 // to Unregister, which will do the work of the finalizer anyway.
1099 // Further, it's actually critical that we *not* wait for the lock here, because
1100 // the other thread that's in the middle of Unregister may be suspended for shutdown.
1101 // Then, during the live-object finalization phase of shutdown, this thread would
1102 // end up spinning forever, as the other thread would never release the lock.
1103 // This will result in a "leak" of sorts (since the handle will not be cleaned up)
1104 // but the process is exiting anyway.
1106 // During AD-unload, we don
\92t finalize live objects until all threads have been
1107 // aborted out of the AD. Since these locked regions are CERs, we won
\92t abort them
1108 // while the lock is held. So there should be no leak on AD-unload.
1110 if (Interlocked.CompareExchange(ref m_lock, 1, 0) == 0)
1116 WaitHandleCleanupNative(registeredWaitHandle);
1119 m_internalWaitObject.SafeWaitHandle.DangerousRelease();
1120 bReleaseNeeded = false;
1122 SetHandle(InvalidHandle);
1123 m_internalWaitObject = null;
1133 [System.Security.SecurityCritical] // auto-generated
1134 [ResourceExposure(ResourceScope.Machine)]
1135 [MethodImplAttribute(MethodImplOptions.InternalCall)]
1136 private static extern void WaitHandleCleanupNative(IntPtr handle);
1138 [System.Security.SecurityCritical] // auto-generated
1139 [ResourceExposure(ResourceScope.Machine)]
1140 [MethodImplAttribute(MethodImplOptions.InternalCall)]
1141 private static extern bool UnregisterWaitNative(IntPtr handle, SafeHandle waitObject);
1144 [System.Runtime.InteropServices.ComVisible(true)]
1145 #if FEATURE_REMOTING
1146 public sealed class RegisteredWaitHandle : MarshalByRefObject {
1147 #else // FEATURE_REMOTING
1148 public sealed class RegisteredWaitHandle {
1149 #endif // FEATURE_REMOTING
1150 private RegisteredWaitHandleSafe internalRegisteredWait;
1152 internal RegisteredWaitHandle()
1154 internalRegisteredWait = new RegisteredWaitHandleSafe();
1157 internal void SetHandle(IntPtr handle)
1159 internalRegisteredWait.SetHandle(handle);
1162 [System.Security.SecurityCritical] // auto-generated
1163 internal void SetWaitObject(WaitHandle waitObject)
1165 internalRegisteredWait.SetWaitObject(waitObject);
1168 [System.Security.SecuritySafeCritical] // auto-generated
1169 [System.Runtime.InteropServices.ComVisible(true)]
1170 // This is the only public method on this class
1171 public bool Unregister(
1172 WaitHandle waitObject // object to be notified when all callbacks to delegates have completed
1175 return internalRegisteredWait.Unregister(waitObject);
1181 // This type is necessary because VS 2010's debugger looks for a method named _ThreadPoolWaitCallbacck.PerformWaitCallback
1182 // on the stack to determine if a thread is a ThreadPool thread or not. We have a better way to do this for .NET 4.5, but
1183 // still need to maintain compatibility with VS 2010. When compat with VS 2010 is no longer an issue, this type may be
1186 internal static class _ThreadPoolWaitCallback
1188 [System.Security.SecurityCritical]
1189 static internal bool PerformWaitCallback()
1191 return ThreadPoolWorkQueue.Dispatch();
1195 internal sealed class QueueUserWorkItemCallback : IThreadPoolWorkItem
1197 [System.Security.SecuritySafeCritical]
1198 static QueueUserWorkItemCallback() {}
1200 private WaitCallback callback;
1201 private ExecutionContext context;
1202 private Object state;
1205 volatile int executed;
1207 ~QueueUserWorkItemCallback()
1210 executed != 0 || Environment.HasShutdownStarted || AppDomain.CurrentDomain.IsFinalizingForUnload(),
1211 "A QueueUserWorkItemCallback was never called!");
1214 void MarkExecuted(bool aborted)
1216 GC.SuppressFinalize(this);
1218 0 == Interlocked.Exchange(ref executed, 1) || aborted,
1219 "A QueueUserWorkItemCallback was called twice!");
1224 internal QueueUserWorkItemCallback(WaitCallback waitCallback, Object stateObj, bool compressStack, ref StackCrawlMark stackMark)
1226 callback = waitCallback;
1228 if (compressStack && !ExecutionContext.IsFlowSuppressed())
1230 // clone the exection context
1231 context = ExecutionContext.Capture(
1233 ExecutionContext.CaptureOptions.IgnoreSyncCtx | ExecutionContext.CaptureOptions.OptimizeDefaultCase);
1238 // internal test hook - used by tests to exercise work-stealing, etc.
1240 internal QueueUserWorkItemCallback(WaitCallback waitCallback, Object stateObj, ExecutionContext ec)
1242 callback = waitCallback;
1248 void IThreadPoolWorkItem.ExecuteWorkItem()
1251 MarkExecuted(false);
1254 // call directly if it is an unsafe call OR EC flow is suppressed
1255 if (context == null)
1257 WaitCallback cb = callback;
1263 ExecutionContext.Run(context, ccb, this, true);
1268 void IThreadPoolWorkItem.MarkAborted(ThreadAbortException tae)
1271 // this workitem didn't execute because we got a ThreadAbortException prior to the call to ExecuteWorkItem.
1272 // This counts as being executed for our purposes.
1277 [System.Security.SecurityCritical]
1278 static internal ContextCallback ccb = new ContextCallback(WaitCallback_Context);
1280 [System.Security.SecurityCritical]
1281 static private void WaitCallback_Context(Object state)
1283 QueueUserWorkItemCallback obj = (QueueUserWorkItemCallback)state;
1284 WaitCallback wc = obj.callback as WaitCallback;
1285 Contract.Assert(null != wc);
1290 internal class _ThreadPoolWaitOrTimerCallback
1292 [System.Security.SecuritySafeCritical]
1293 static _ThreadPoolWaitOrTimerCallback() {}
1295 WaitOrTimerCallback _waitOrTimerCallback;
1296 ExecutionContext _executionContext;
1298 [System.Security.SecurityCritical]
1299 static private ContextCallback _ccbt = new ContextCallback(WaitOrTimerCallback_Context_t);
1300 [System.Security.SecurityCritical]
1301 static private ContextCallback _ccbf = new ContextCallback(WaitOrTimerCallback_Context_f);
1303 [System.Security.SecurityCritical] // auto-generated
1304 internal _ThreadPoolWaitOrTimerCallback(WaitOrTimerCallback waitOrTimerCallback, Object state, bool compressStack, ref StackCrawlMark stackMark)
1306 _waitOrTimerCallback = waitOrTimerCallback;
1309 if (compressStack && !ExecutionContext.IsFlowSuppressed())
1311 // capture the exection context
1312 _executionContext = ExecutionContext.Capture(
1314 ExecutionContext.CaptureOptions.IgnoreSyncCtx | ExecutionContext.CaptureOptions.OptimizeDefaultCase);
1318 [System.Security.SecurityCritical]
1319 static private void WaitOrTimerCallback_Context_t(Object state)
1321 WaitOrTimerCallback_Context(state, true);
1324 [System.Security.SecurityCritical]
1325 static private void WaitOrTimerCallback_Context_f(Object state)
1327 WaitOrTimerCallback_Context(state, false);
1330 static private void WaitOrTimerCallback_Context(Object state, bool timedOut)
1332 _ThreadPoolWaitOrTimerCallback helper = (_ThreadPoolWaitOrTimerCallback)state;
1333 helper._waitOrTimerCallback(helper._state, timedOut);
1337 [System.Security.SecurityCritical] // auto-generated
1338 static internal void PerformWaitOrTimerCallback(Object state, bool timedOut)
1340 _ThreadPoolWaitOrTimerCallback helper = (_ThreadPoolWaitOrTimerCallback)state;
1341 Contract.Assert(helper != null, "Null state passed to PerformWaitOrTimerCallback!");
1342 // call directly if it is an unsafe call OR EC flow is suppressed
1343 if (helper._executionContext == null)
1345 WaitOrTimerCallback callback = helper._waitOrTimerCallback;
1346 callback(helper._state, timedOut);
1350 using (ExecutionContext executionContext = helper._executionContext.CreateCopy())
1353 ExecutionContext.Run(executionContext, _ccbt, helper, true);
1355 ExecutionContext.Run(executionContext, _ccbf, helper, true);
1362 [HostProtection(Synchronization=true, ExternalThreading=true)]
1363 public static class ThreadPool
1366 [System.Security.SecurityCritical] // auto-generated
1368 [System.Security.SecuritySafeCritical]
1370 #pragma warning disable 618
1371 [SecurityPermissionAttribute(SecurityAction.Demand, ControlThread = true)]
1372 #pragma warning restore 618
1373 public static bool SetMaxThreads(int workerThreads, int completionPortThreads)
1375 return SetMaxThreadsNative(workerThreads, completionPortThreads);
1378 [System.Security.SecuritySafeCritical] // auto-generated
1379 public static void GetMaxThreads(out int workerThreads, out int completionPortThreads)
1381 GetMaxThreadsNative(out workerThreads, out completionPortThreads);
1385 [System.Security.SecurityCritical] // auto-generated
1387 [System.Security.SecuritySafeCritical]
1389 #pragma warning disable 618
1390 [SecurityPermissionAttribute(SecurityAction.Demand, ControlThread = true)]
1391 #pragma warning restore 618
1392 public static bool SetMinThreads(int workerThreads, int completionPortThreads)
1394 return SetMinThreadsNative(workerThreads, completionPortThreads);
1397 [System.Security.SecuritySafeCritical] // auto-generated
1398 public static void GetMinThreads(out int workerThreads, out int completionPortThreads)
1400 GetMinThreadsNative(out workerThreads, out completionPortThreads);
1403 [System.Security.SecuritySafeCritical] // auto-generated
1404 public static void GetAvailableThreads(out int workerThreads, out int completionPortThreads)
1406 GetAvailableThreadsNative(out workerThreads, out completionPortThreads);
1409 [System.Security.SecuritySafeCritical] // auto-generated
1410 [CLSCompliant(false)]
1411 [MethodImplAttribute(MethodImplOptions.NoInlining)] // Methods containing StackCrawlMark local var has to be marked non-inlineable
1412 public static RegisteredWaitHandle RegisterWaitForSingleObject( // throws RegisterWaitException
1413 WaitHandle waitObject,
1414 WaitOrTimerCallback callBack,
1416 uint millisecondsTimeOutInterval,
1417 bool executeOnlyOnce // NOTE: we do not allow other options that allow the callback to be queued as an APC
1420 StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller;
1421 return RegisterWaitForSingleObject(waitObject,callBack,state,millisecondsTimeOutInterval,executeOnlyOnce,ref stackMark,true);
1424 [System.Security.SecurityCritical] // auto-generated_required
1425 [CLSCompliant(false)]
1426 [MethodImplAttribute(MethodImplOptions.NoInlining)] // Methods containing StackCrawlMark local var has to be marked non-inlineable
1427 public static RegisteredWaitHandle UnsafeRegisterWaitForSingleObject( // throws RegisterWaitException
1428 WaitHandle waitObject,
1429 WaitOrTimerCallback callBack,
1431 uint millisecondsTimeOutInterval,
1432 bool executeOnlyOnce // NOTE: we do not allow other options that allow the callback to be queued as an APC
1435 StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller;
1436 return RegisterWaitForSingleObject(waitObject,callBack,state,millisecondsTimeOutInterval,executeOnlyOnce,ref stackMark,false);
1440 [System.Security.SecurityCritical] // auto-generated
1441 private static RegisteredWaitHandle RegisterWaitForSingleObject( // throws RegisterWaitException
1442 WaitHandle waitObject,
1443 WaitOrTimerCallback callBack,
1445 uint millisecondsTimeOutInterval,
1446 bool executeOnlyOnce, // NOTE: we do not allow other options that allow the callback to be queued as an APC
1447 ref StackCrawlMark stackMark,
1452 #if FEATURE_REMOTING
1453 if (RemotingServices.IsTransparentProxy(waitObject))
1454 throw new InvalidOperationException(Environment.GetResourceString("InvalidOperation_WaitOnTransparentProxy"));
1455 Contract.EndContractBlock();
1458 RegisteredWaitHandle registeredWaitHandle = new RegisteredWaitHandle();
1460 if (callBack != null)
1462 _ThreadPoolWaitOrTimerCallback callBackHelper = new _ThreadPoolWaitOrTimerCallback(callBack, state, compressStack, ref stackMark);
1463 state = (Object)callBackHelper;
1464 // call SetWaitObject before native call so that waitObject won't be closed before threadpoolmgr registration
1465 // this could occur if callback were to fire before SetWaitObject does its addref
1466 registeredWaitHandle.SetWaitObject(waitObject);
1467 IntPtr nativeRegisteredWaitHandle = RegisterWaitForSingleObjectNative(waitObject,
1469 millisecondsTimeOutInterval,
1471 registeredWaitHandle,
1474 registeredWaitHandle.SetHandle(nativeRegisteredWaitHandle);
1478 throw new ArgumentNullException("WaitOrTimerCallback");
1480 return registeredWaitHandle;
1482 if (waitObject == null)
1483 throw new ArgumentNullException ("waitObject");
1484 if (callBack == null)
1485 throw new ArgumentNullException ("callBack");
1486 if (millisecondsTimeOutInterval != Timeout.UnsignedInfinite && millisecondsTimeOutInterval > Int32.MaxValue)
1487 throw new NotSupportedException ("Timeout is too big. Maximum is Int32.MaxValue");
1489 RegisteredWaitHandle waiter = new RegisteredWaitHandle (waitObject, callBack, state, new TimeSpan (0, 0, 0, 0, (int) millisecondsTimeOutInterval), executeOnlyOnce);
1490 QueueUserWorkItem (new WaitCallback (waiter.Wait), null);
1497 [System.Security.SecuritySafeCritical] // auto-generated
1498 [MethodImplAttribute(MethodImplOptions.NoInlining)] // Methods containing StackCrawlMark local var has to be marked non-inlineable
1499 public static RegisteredWaitHandle RegisterWaitForSingleObject( // throws RegisterWaitException
1500 WaitHandle waitObject,
1501 WaitOrTimerCallback callBack,
1503 int millisecondsTimeOutInterval,
1504 bool executeOnlyOnce // NOTE: we do not allow other options that allow the callback to be queued as an APC
1507 if (millisecondsTimeOutInterval < -1)
1508 throw new ArgumentOutOfRangeException("millisecondsTimeOutInterval", Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegOrNegative1"));
1509 Contract.EndContractBlock();
1510 StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller;
1511 return RegisterWaitForSingleObject(waitObject,callBack,state,millisecondsTimeOutInterval == Timeout.Infinite ? Timeout.UnsignedInfinite : (UInt32)millisecondsTimeOutInterval,executeOnlyOnce,ref stackMark,true);
1514 [System.Security.SecurityCritical] // auto-generated_required
1515 [MethodImplAttribute(MethodImplOptions.NoInlining)] // Methods containing StackCrawlMark local var has to be marked non-inlineable
1516 public static RegisteredWaitHandle UnsafeRegisterWaitForSingleObject( // throws RegisterWaitException
1517 WaitHandle waitObject,
1518 WaitOrTimerCallback callBack,
1520 int millisecondsTimeOutInterval,
1521 bool executeOnlyOnce // NOTE: we do not allow other options that allow the callback to be queued as an APC
1524 if (millisecondsTimeOutInterval < -1)
1525 throw new ArgumentOutOfRangeException("millisecondsTimeOutInterval", Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegOrNegative1"));
1526 Contract.EndContractBlock();
1527 StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller;
1528 return RegisterWaitForSingleObject(waitObject,callBack,state,millisecondsTimeOutInterval == Timeout.Infinite ? Timeout.UnsignedInfinite : (UInt32)millisecondsTimeOutInterval,executeOnlyOnce,ref stackMark,false);
1531 [System.Security.SecuritySafeCritical] // auto-generated
1532 [MethodImplAttribute(MethodImplOptions.NoInlining)] // Methods containing StackCrawlMark local var has to be marked non-inlineable
1533 public static RegisteredWaitHandle RegisterWaitForSingleObject( // throws RegisterWaitException
1534 WaitHandle waitObject,
1535 WaitOrTimerCallback callBack,
1537 long millisecondsTimeOutInterval,
1538 bool executeOnlyOnce // NOTE: we do not allow other options that allow the callback to be queued as an APC
1541 if (millisecondsTimeOutInterval < -1)
1542 throw new ArgumentOutOfRangeException("millisecondsTimeOutInterval", Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegOrNegative1"));
1543 Contract.EndContractBlock();
1544 StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller;
1545 return RegisterWaitForSingleObject(waitObject,callBack,state,millisecondsTimeOutInterval == Timeout.Infinite ? Timeout.UnsignedInfinite : (UInt32)millisecondsTimeOutInterval,executeOnlyOnce,ref stackMark,true);
1548 [System.Security.SecurityCritical] // auto-generated_required
1549 [MethodImplAttribute(MethodImplOptions.NoInlining)] // Methods containing StackCrawlMark local var has to be marked non-inlineable
1550 public static RegisteredWaitHandle UnsafeRegisterWaitForSingleObject( // throws RegisterWaitException
1551 WaitHandle waitObject,
1552 WaitOrTimerCallback callBack,
1554 long millisecondsTimeOutInterval,
1555 bool executeOnlyOnce // NOTE: we do not allow other options that allow the callback to be queued as an APC
1558 if (millisecondsTimeOutInterval < -1)
1559 throw new ArgumentOutOfRangeException("millisecondsTimeOutInterval", Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegOrNegative1"));
1560 Contract.EndContractBlock();
1561 StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller;
1562 return RegisterWaitForSingleObject(waitObject,callBack,state,millisecondsTimeOutInterval == Timeout.Infinite ? Timeout.UnsignedInfinite : (UInt32)millisecondsTimeOutInterval,executeOnlyOnce,ref stackMark,false);
1565 [System.Security.SecuritySafeCritical] // auto-generated
1566 [MethodImplAttribute(MethodImplOptions.NoInlining)] // Methods containing StackCrawlMark local var has to be marked non-inlineable
1567 public static RegisteredWaitHandle RegisterWaitForSingleObject(
1568 WaitHandle waitObject,
1569 WaitOrTimerCallback callBack,
1572 bool executeOnlyOnce
1575 long tm = (long)timeout.TotalMilliseconds;
1577 throw new ArgumentOutOfRangeException("timeout", Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegOrNegative1"));
1578 if (tm > (long) Int32.MaxValue)
1579 throw new ArgumentOutOfRangeException("timeout", Environment.GetResourceString("ArgumentOutOfRange_LessEqualToIntegerMaxVal"));
1580 StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller;
1581 return RegisterWaitForSingleObject(waitObject,callBack,state,(UInt32)tm,executeOnlyOnce,ref stackMark,true);
1584 [System.Security.SecurityCritical] // auto-generated_required
1585 [MethodImplAttribute(MethodImplOptions.NoInlining)] // Methods containing StackCrawlMark local var has to be marked non-inlineable
1586 public static RegisteredWaitHandle UnsafeRegisterWaitForSingleObject(
1587 WaitHandle waitObject,
1588 WaitOrTimerCallback callBack,
1591 bool executeOnlyOnce
1594 long tm = (long)timeout.TotalMilliseconds;
1596 throw new ArgumentOutOfRangeException("timeout", Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegOrNegative1"));
1597 if (tm > (long) Int32.MaxValue)
1598 throw new ArgumentOutOfRangeException("timeout", Environment.GetResourceString("ArgumentOutOfRange_LessEqualToIntegerMaxVal"));
1599 StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller;
1600 return RegisterWaitForSingleObject(waitObject,callBack,state,(UInt32)tm,executeOnlyOnce,ref stackMark,false);
1603 [System.Security.SecuritySafeCritical] // auto-generated
1604 [MethodImplAttribute(MethodImplOptions.NoInlining)] // Methods containing StackCrawlMark local var has to be marked non-inlineable
1605 public static bool QueueUserWorkItem(
1606 WaitCallback callBack, // NOTE: we do not expose options that allow the callback to be queued as an APC
1610 StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller;
1611 return QueueUserWorkItemHelper(callBack,state,ref stackMark,true);
1614 [System.Security.SecuritySafeCritical] // auto-generated
1615 [MethodImplAttribute(MethodImplOptions.NoInlining)] // Methods containing StackCrawlMark local var has to be marked non-inlineable
1616 public static bool QueueUserWorkItem(
1617 WaitCallback callBack // NOTE: we do not expose options that allow the callback to be queued as an APC
1620 StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller;
1621 return QueueUserWorkItemHelper(callBack,null,ref stackMark,true);
1624 [System.Security.SecurityCritical] // auto-generated_required
1625 [MethodImplAttribute(MethodImplOptions.NoInlining)] // Methods containing StackCrawlMark local var has to be marked non-inlineable
1626 public static bool UnsafeQueueUserWorkItem(
1627 WaitCallback callBack, // NOTE: we do not expose options that allow the callback to be queued as an APC
1631 StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller;
1632 return QueueUserWorkItemHelper(callBack,state,ref stackMark,false);
1635 //ThreadPool has per-appdomain managed queue of work-items. The VM is
1636 //responsible for just scheduling threads into appdomains. After that
1637 //work-items are dispatched from the managed queue.
1638 [System.Security.SecurityCritical] // auto-generated
1639 private static bool QueueUserWorkItemHelper(WaitCallback callBack, Object state, ref StackCrawlMark stackMark, bool compressStack )
1641 bool success = true;
1643 if (callBack != null)
1645 //The thread pool maintains a per-appdomain managed work queue.
1646 //New thread pool entries are added in the managed queue.
1647 //The VM is responsible for the actual growing/shrinking of
1650 EnsureVMInitialized();
1653 // If we are able to create the workitem, we need to get it in the queue without being interrupted
1654 // by a ThreadAbortException.
1659 QueueUserWorkItemCallback tpcallBack = new QueueUserWorkItemCallback(callBack, state, compressStack, ref stackMark);
1660 ThreadPoolGlobals.workQueue.Enqueue(tpcallBack, true);
1666 throw new ArgumentNullException("WaitCallback");
1672 internal static void UnsafeQueueCustomWorkItem(IThreadPoolWorkItem workItem, bool forceGlobal)
1674 Contract.Assert(null != workItem);
1675 EnsureVMInitialized();
1678 // Enqueue needs to be protected from ThreadAbort
1683 ThreadPoolGlobals.workQueue.Enqueue(workItem, forceGlobal);
1687 // This method tries to take the target callback out of the current thread's queue.
1689 internal static bool TryPopCustomWorkItem(IThreadPoolWorkItem workItem)
1691 Contract.Assert(null != workItem);
1692 if (!ThreadPoolGlobals.vmTpInitialized)
1693 return false; //Not initialized, so there's no way this workitem was ever queued.
1694 return ThreadPoolGlobals.workQueue.LocalFindAndPop(workItem);
1697 // Get all workitems. Called by TaskScheduler in its debugger hooks.
1699 internal static IEnumerable<IThreadPoolWorkItem> GetQueuedWorkItems()
1701 return EnumerateQueuedWorkItems(ThreadPoolWorkQueue.allThreadQueues.Current, ThreadPoolGlobals.workQueue.queueTail);
1704 internal static IEnumerable<IThreadPoolWorkItem> EnumerateQueuedWorkItems(ThreadPoolWorkQueue.WorkStealingQueue[] wsQueues, ThreadPoolWorkQueue.QueueSegment globalQueueTail)
1706 if (wsQueues != null)
1708 // First, enumerate all workitems in thread-local queues.
1709 foreach (ThreadPoolWorkQueue.WorkStealingQueue wsq in wsQueues)
1711 if (wsq != null && wsq.m_array != null)
1713 IThreadPoolWorkItem[] items = wsq.m_array;
1714 for (int i = 0; i < items.Length; i++)
1716 IThreadPoolWorkItem item = items[i];
1724 if (globalQueueTail != null)
1726 // Now the global queue
1727 for (ThreadPoolWorkQueue.QueueSegment segment = globalQueueTail;
1729 segment = segment.Next)
1731 IThreadPoolWorkItem[] items = segment.nodes;
1732 for (int i = 0; i < items.Length; i++)
1734 IThreadPoolWorkItem item = items[i];
1743 internal static IEnumerable<IThreadPoolWorkItem> GetLocallyQueuedWorkItems()
1745 return EnumerateQueuedWorkItems(new ThreadPoolWorkQueue.WorkStealingQueue[] { ThreadPoolWorkQueueThreadLocals.threadLocals.workStealingQueue }, null);
1749 internal static IEnumerable<IThreadPoolWorkItem> GetGloballyQueuedWorkItems()
1751 return EnumerateQueuedWorkItems(null, ThreadPoolGlobals.workQueue.queueTail);
1754 private static object[] ToObjectArray(IEnumerable<IThreadPoolWorkItem> workitems)
1757 foreach (IThreadPoolWorkItem item in workitems)
1762 object[] result = new object[i];
1764 foreach (IThreadPoolWorkItem item in workitems)
1766 if (i < result.Length) //just in case someone calls us while the queues are in motion
1774 // This is the method the debugger will actually call, if it ends up calling
1775 // into ThreadPool directly. Tests can use this to simulate a debugger, as well.
1777 internal static object[] GetQueuedWorkItemsForDebugger()
1779 return ToObjectArray(GetQueuedWorkItems());
1783 internal static object[] GetGloballyQueuedWorkItemsForDebugger()
1785 return ToObjectArray(GetGloballyQueuedWorkItems());
1789 internal static object[] GetLocallyQueuedWorkItemsForDebugger()
1791 return ToObjectArray(GetLocallyQueuedWorkItems());
1794 [System.Security.SecurityCritical] // auto-generated
1795 [ResourceExposure(ResourceScope.None)]
1796 [MethodImplAttribute(MethodImplOptions.InternalCall)]
1797 internal static extern bool RequestWorkerThread();
1799 [System.Security.SecurityCritical] // auto-generated
1800 [ResourceExposure(ResourceScope.None)]
1801 [MethodImplAttribute(MethodImplOptions.InternalCall)]
1802 unsafe private static extern bool PostQueuedCompletionStatus(NativeOverlapped* overlapped);
1804 [System.Security.SecurityCritical] // auto-generated_required
1805 [CLSCompliant(false)]
1806 unsafe public static bool UnsafeQueueNativeOverlapped(NativeOverlapped* overlapped)
1808 #if FEATURE_CORECLR && !FEATURE_LEGACYNETCF
1809 if(Environment.OSVersion.Platform == PlatformID.MacOSX)
1810 throw new NotSupportedException(Environment.GetResourceString("Arg_NotSupportedException"));
1811 Contract.EndContractBlock();
1814 return PostQueuedCompletionStatus(overlapped);
1818 private static void EnsureVMInitialized()
1820 if (!ThreadPoolGlobals.vmTpInitialized)
1822 ThreadPool.InitializeVMTp(ref ThreadPoolGlobals.enableWorkerTracking);
1823 ThreadPoolGlobals.vmTpInitialized = true;
1829 [System.Security.SecurityCritical] // auto-generated
1830 [ResourceExposure(ResourceScope.None)]
1831 [MethodImplAttribute(MethodImplOptions.InternalCall)]
1832 private static extern bool SetMinThreadsNative(int workerThreads, int completionPortThreads);
1834 [System.Security.SecurityCritical] // auto-generated
1835 [ResourceExposure(ResourceScope.None)]
1836 [MethodImplAttribute(MethodImplOptions.InternalCall)]
1837 private static extern bool SetMaxThreadsNative(int workerThreads, int completionPortThreads);
1839 [System.Security.SecurityCritical] // auto-generated
1840 [ResourceExposure(ResourceScope.None)]
1841 [MethodImplAttribute(MethodImplOptions.InternalCall)]
1842 private static extern void GetMinThreadsNative(out int workerThreads, out int completionPortThreads);
1844 [System.Security.SecurityCritical] // auto-generated
1845 [ResourceExposure(ResourceScope.None)]
1846 [MethodImplAttribute(MethodImplOptions.InternalCall)]
1847 private static extern void GetMaxThreadsNative(out int workerThreads, out int completionPortThreads);
1849 [System.Security.SecurityCritical] // auto-generated
1850 [ResourceExposure(ResourceScope.None)]
1851 [MethodImplAttribute(MethodImplOptions.InternalCall)]
1852 private static extern void GetAvailableThreadsNative(out int workerThreads, out int completionPortThreads);
1854 [System.Security.SecurityCritical] // auto-generated
1855 [ResourceExposure(ResourceScope.None)]
1856 [MethodImplAttribute(MethodImplOptions.InternalCall)]
1857 internal static extern bool NotifyWorkItemComplete();
1859 [System.Security.SecurityCritical]
1860 [ResourceExposure(ResourceScope.None)]
1861 [MethodImplAttribute(MethodImplOptions.InternalCall)]
1862 internal static extern void ReportThreadStatus(bool isWorking);
1864 [System.Security.SecuritySafeCritical]
1865 internal static void NotifyWorkItemProgress()
1867 if (!ThreadPoolGlobals.vmTpInitialized)
1868 ThreadPool.InitializeVMTp(ref ThreadPoolGlobals.enableWorkerTracking);
1869 NotifyWorkItemProgressNative();
1872 [System.Security.SecurityCritical] // auto-generated
1873 [ResourceExposure(ResourceScope.None)]
1874 [MethodImplAttribute(MethodImplOptions.InternalCall)]
1875 internal static extern void NotifyWorkItemProgressNative();
1877 [System.Security.SecurityCritical] // auto-generated
1878 [ResourceExposure(ResourceScope.None)]
1879 [MethodImplAttribute(MethodImplOptions.InternalCall)]
1880 internal static extern bool IsThreadPoolHosted();
1882 [System.Security.SecurityCritical] // auto-generated
1883 [ResourceExposure(ResourceScope.None)]
1884 [MethodImplAttribute(MethodImplOptions.InternalCall)]
1885 private static extern void InitializeVMTp(ref bool enableWorkerTracking);
1888 [System.Security.SecurityCritical] // auto-generated
1889 [ResourceExposure(ResourceScope.None)]
1890 [MethodImplAttribute(MethodImplOptions.InternalCall)]
1891 private static extern IntPtr RegisterWaitForSingleObjectNative(
1892 WaitHandle waitHandle,
1894 uint timeOutInterval,
1895 bool executeOnlyOnce,
1896 RegisteredWaitHandle registeredWaitHandle,
1897 ref StackCrawlMark stackMark,
1902 #if !FEATURE_CORECLR
1903 [System.Security.SecuritySafeCritical] // auto-generated
1904 [Obsolete("ThreadPool.BindHandle(IntPtr) has been deprecated. Please use ThreadPool.BindHandle(SafeHandle) instead.", false)]
1905 [SecurityPermissionAttribute( SecurityAction.Demand, Flags = SecurityPermissionFlag.UnmanagedCode)]
1906 public static bool BindHandle(
1910 return BindIOCompletionCallbackNative(osHandle);
1915 [System.Security.SecurityCritical] // auto-generated
1917 [System.Security.SecuritySafeCritical]
1919 #pragma warning disable 618
1920 [SecurityPermissionAttribute(SecurityAction.Demand, Flags = SecurityPermissionFlag.UnmanagedCode)]
1921 #pragma warning restore 618
1922 public static bool BindHandle(SafeHandle osHandle)
1924 #if FEATURE_CORECLR && !FEATURE_LEGACYNETCF
1925 if(Environment.OSVersion.Platform == PlatformID.MacOSX)
1926 throw new NotSupportedException(Environment.GetResourceString("Arg_NotSupportedException"));
1927 Contract.EndContractBlock();
1930 if (osHandle == null)
1931 throw new ArgumentNullException("osHandle");
1934 bool mustReleaseSafeHandle = false;
1935 RuntimeHelpers.PrepareConstrainedRegions();
1937 osHandle.DangerousAddRef(ref mustReleaseSafeHandle);
1938 ret = BindIOCompletionCallbackNative(osHandle.DangerousGetHandle());
1941 if (mustReleaseSafeHandle)
1942 osHandle.DangerousRelease();
1947 [System.Security.SecurityCritical] // auto-generated
1948 [ResourceExposure(ResourceScope.None)]
1949 [MethodImplAttribute(MethodImplOptions.InternalCall)]
1950 [ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)]
1951 private static extern bool BindIOCompletionCallbackNative(IntPtr fileHandle);