3 // Copyright (c) Microsoft Corporation. All rights reserved.
7 // <OWNER>Microsoft</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 raced 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
;
583 internal bool loggingEnabled
;
586 internal static SparseArray
<WorkStealingQueue
> allThreadQueues
= new SparseArray
<WorkStealingQueue
>(16); //
588 private volatile int numOutstandingThreadRequests
= 0;
590 public ThreadPoolWorkQueue()
592 queueTail
= queueHead
= new QueueSegment();
594 loggingEnabled
= FrameworkEventSource
.Log
.IsEnabled(EventLevel
.Verbose
, FrameworkEventSource
.Keywords
.ThreadPool
|FrameworkEventSource
.Keywords
.ThreadTransfer
);
599 public ThreadPoolWorkQueueThreadLocals
EnsureCurrentThreadHasQueue()
601 if (null == ThreadPoolWorkQueueThreadLocals
.threadLocals
)
602 ThreadPoolWorkQueueThreadLocals
.threadLocals
= new ThreadPoolWorkQueueThreadLocals(this);
603 return ThreadPoolWorkQueueThreadLocals
.threadLocals
;
607 internal void EnsureThreadRequested()
610 // If we have not yet requested #procs threads from the VM, then request a new thread.
611 // Note that there is a separate count in the VM which will also be incremented in this case,
612 // which is handled by RequestWorkerThread.
614 int count
= numOutstandingThreadRequests
;
615 while (count
< ThreadPoolGlobals
.processorCount
)
617 int prev
= Interlocked
.CompareExchange(ref numOutstandingThreadRequests
, count
+1, count
);
620 ThreadPool
.RequestWorkerThread();
628 internal void MarkThreadRequestSatisfied()
631 // The VM has called us, so one of our outstanding thread requests has been satisfied.
632 // Decrement the count so that future calls to EnsureThreadRequested will succeed.
633 // Note that there is a separate count in the VM which has already been decremented by the VM
634 // by the time we reach this point.
636 int count
= numOutstandingThreadRequests
;
639 int prev
= Interlocked
.CompareExchange(ref numOutstandingThreadRequests
, count
- 1, count
);
649 public void Enqueue(IThreadPoolWorkItem callback
, bool forceGlobal
)
651 ThreadPoolWorkQueueThreadLocals tl
= null;
653 tl
= ThreadPoolWorkQueueThreadLocals
.threadLocals
;
657 System
.Diagnostics
.Tracing
.FrameworkEventSource
.Log
.ThreadPoolEnqueueWorkObject(callback
);
661 tl
.workStealingQueue
.LocalPush(callback
);
665 QueueSegment head
= queueHead
;
667 while (!head
.TryEnqueue(callback
))
669 Interlocked
.CompareExchange(ref head
.Next
, new QueueSegment(), null);
671 while (head
.Next
!= null)
673 Interlocked
.CompareExchange(ref queueHead
, head
.Next
, head
);
679 ThreadPool
.NotifyWorkItemQueued();
681 EnsureThreadRequested();
685 internal bool LocalFindAndPop(IThreadPoolWorkItem callback
)
687 ThreadPoolWorkQueueThreadLocals tl
= ThreadPoolWorkQueueThreadLocals
.threadLocals
;
691 return tl
.workStealingQueue
.LocalFindAndPop(callback
);
695 public void Dequeue(ThreadPoolWorkQueueThreadLocals tl
, out IThreadPoolWorkItem callback
, out bool missedSteal
)
699 WorkStealingQueue wsq
= tl
.workStealingQueue
;
701 if (wsq
.LocalPop(out callback
))
702 Contract
.Assert(null != callback
);
704 if (null == callback
)
706 QueueSegment tail
= queueTail
;
709 if (tail
.TryDequeue(out callback
))
711 Contract
.Assert(null != callback
);
715 if (null == tail
.Next
|| !tail
.IsUsedUp())
721 Interlocked
.CompareExchange(ref queueTail
, tail
.Next
, tail
);
727 if (null == callback
)
729 WorkStealingQueue
[] otherQueues
= allThreadQueues
.Current
;
730 int i
= tl
.random
.Next(otherQueues
.Length
);
731 int c
= otherQueues
.Length
;
734 WorkStealingQueue otherQueue
= Volatile
.Read(ref otherQueues
[i
% otherQueues
.Length
]);
735 if (otherQueue
!= null &&
737 otherQueue
.TrySteal(out callback
, ref missedSteal
))
739 Contract
.Assert(null != callback
);
749 static internal bool Dispatch()
751 var workQueue
= ThreadPoolGlobals
.workQueue
;
753 // The clock is ticking! We have ThreadPoolGlobals.tpQuantum milliseconds to get some work done, and then
754 // we need to return to the VM.
756 int quantumStartTime
= Environment
.TickCount
;
759 // Update our records to indicate that an outstanding request for a thread has now been fulfilled.
760 // From this point on, we are responsible for requesting another thread if we stop working for any
761 // reason, and we believe there might still be work in the queue.
763 // Note that if this thread is aborted before we get a chance to request another one, the VM will
764 // record a thread request on our behalf. So we don't need to worry about getting aborted right here.
766 workQueue
.MarkThreadRequestSatisfied();
769 // Has the desire for logging changed since the last time we entered?
770 workQueue
.loggingEnabled
= FrameworkEventSource
.Log
.IsEnabled(EventLevel
.Verbose
, FrameworkEventSource
.Keywords
.ThreadPool
|FrameworkEventSource
.Keywords
.ThreadTransfer
);
773 // Assume that we're going to need another thread if this one returns to the VM. We'll set this to
774 // false later, but only if we're absolutely certain that the queue is empty.
776 bool needAnotherThread
= true;
777 IThreadPoolWorkItem workItem
= null;
781 // Set up our thread-local data
783 ThreadPoolWorkQueueThreadLocals tl
= workQueue
.EnsureCurrentThreadHasQueue();
786 // Loop until our quantum expires.
788 while ((Environment
.TickCount
- quantumStartTime
) < ThreadPoolGlobals
.tpQuantum
)
791 // Dequeue and EnsureThreadRequested must be protected from ThreadAbortException.
792 // These are fast, so this will not delay aborts/AD-unloads for very long.
797 bool missedSteal
= false;
798 workQueue
.Dequeue(tl
, out workItem
, out missedSteal
);
800 if (workItem
== null)
803 // No work. We're going to return to the VM once we leave this protected region.
804 // If we missed a steal, though, there may be more work in the queue.
805 // Instead of looping around and trying again, we'll just request another thread. This way
806 // we won't starve other AppDomains while we spin trying to get locks, and hopefully the thread
807 // that owns the contended work-stealing queue will pick up its own workitems in the meantime,
808 // which will be more efficient than this thread doing it anyway.
810 needAnotherThread
= missedSteal
;
815 // If we found work, there may be more work. Ask for another thread so that the other work can be processed
816 // in parallel. Note that this will only ask for a max of #procs threads, so it's safe to call it for every dequeue.
818 workQueue
.EnsureThreadRequested();
822 if (workItem
== null)
824 // Tell the VM we're returning normally, not because Hill Climbing asked us to return.
830 if (workQueue
.loggingEnabled
)
831 System
.Diagnostics
.Tracing
.FrameworkEventSource
.Log
.ThreadPoolDequeueWorkObject(workItem
);
834 // Execute the workitem outside of any finally blocks, so that it can be aborted if needed.
836 if (ThreadPoolGlobals
.enableWorkerTracking
)
838 bool reportedStatus
= false;
844 ThreadPool
.ReportThreadStatus(true);
845 reportedStatus
= true;
847 workItem
.ExecuteWorkItem();
853 ThreadPool
.ReportThreadStatus(false);
858 workItem
.ExecuteWorkItem();
863 // Notify the VM that we executed this workitem. This is also our opportunity to ask whether Hill Climbing wants
864 // us to return the thread to the pool or not.
866 if (!ThreadPool
.NotifyWorkItemComplete())
870 // If we get here, it's because our quantum expired. Tell the VM we're returning normally.
873 catch (ThreadAbortException tae
)
876 // This is here to catch the case where this thread is aborted between the time we exit the finally block in the dispatch
877 // loop, and the time we execute the work item. QueueUserWorkItemCallback uses this to update its accounting of whether
878 // it was executed or not (in debug builds only). Task uses this to communicate the ThreadAbortException to anyone
879 // who waits for the task to complete.
881 if (workItem
!= null)
882 workItem
.MarkAborted(tae
);
885 // In this case, the VM is going to request another thread on our behalf. No need to do it twice.
887 needAnotherThread
= false;
888 // throw; //no need to explicitly rethrow a ThreadAbortException, and doing so causes allocations on amd64.
893 // If we are exiting for any reason other than that the queue is definitely empty, ask for another
894 // thread to pick up where we left off.
896 if (needAnotherThread
)
897 workQueue
.EnsureThreadRequested();
900 // we can never reach this point, but the C# compiler doesn't know that, because it doesn't know the ThreadAbortException will be reraised above.
901 Contract
.Assert(false);
906 // Holds a WorkStealingQueue, and remmoves it from the list when this object is no longer referened.
907 internal sealed class ThreadPoolWorkQueueThreadLocals
911 public static ThreadPoolWorkQueueThreadLocals threadLocals
;
913 public readonly ThreadPoolWorkQueue workQueue
;
914 public readonly ThreadPoolWorkQueue
.WorkStealingQueue workStealingQueue
;
915 public readonly Random random
= new Random(Thread
.CurrentThread
.ManagedThreadId
);
917 public ThreadPoolWorkQueueThreadLocals(ThreadPoolWorkQueue tpq
)
920 workStealingQueue
= new ThreadPoolWorkQueue
.WorkStealingQueue();
921 ThreadPoolWorkQueue
.allThreadQueues
.Add(workStealingQueue
);
925 private void CleanUp()
927 if (null != workStealingQueue
)
929 if (null != workQueue
)
934 // Ensure that we won't be aborted between LocalPop and Enqueue.
938 IThreadPoolWorkItem cb
= null;
939 if (workStealingQueue
.LocalPop(out cb
))
941 Contract
.Assert(null != cb
);
942 workQueue
.Enqueue(cb
, true);
952 ThreadPoolWorkQueue
.allThreadQueues
.Remove(workStealingQueue
);
956 [SecuritySafeCritical
]
957 ~
ThreadPoolWorkQueueThreadLocals()
959 // Since the purpose of calling CleanUp is to transfer any pending workitems into the global
960 // queue so that they will be executed by another thread, there's no point in doing this cleanup
961 // if we're in the process of shutting down or unloading the AD. In those cases, the work won't
962 // execute anyway. And there are subtle ----s involved there that would lead us to do the wrong
963 // thing anyway. So we'll only clean up if this is a "normal" finalization.
964 if (!(Environment
.HasShutdownStarted
|| AppDomain
.CurrentDomain
.IsFinalizingForUnload()))
970 internal sealed class RegisteredWaitHandleSafe
: CriticalFinalizerObject
972 private static IntPtr InvalidHandle
974 [System
.Security
.SecuritySafeCritical
] // auto-generated
977 return new IntPtr(-1);
980 private IntPtr registeredWaitHandle
;
981 private WaitHandle m_internalWaitObject
;
982 private bool bReleaseNeeded
= false;
983 private volatile int m_lock
= 0;
986 [System
.Security
.SecuritySafeCritical
] // auto-generated
988 internal RegisteredWaitHandleSafe()
990 registeredWaitHandle
= InvalidHandle
;
993 internal IntPtr
GetHandle()
995 return registeredWaitHandle
;
998 internal void SetHandle(IntPtr handle
)
1000 registeredWaitHandle
= handle
;
1003 [System
.Security
.SecurityCritical
] // auto-generated
1004 [ResourceExposure(ResourceScope
.None
)]
1005 [ResourceConsumption(ResourceScope
.Machine
, ResourceScope
.Machine
)]
1006 [ReliabilityContract(Consistency
.WillNotCorruptState
, Cer
.MayFail
)]
1007 internal void SetWaitObject(WaitHandle waitObject
)
1009 // needed for DangerousAddRef
1010 RuntimeHelpers
.PrepareConstrainedRegions();
1016 m_internalWaitObject
= waitObject
;
1017 if (waitObject
!= null)
1019 m_internalWaitObject
.SafeWaitHandle
.DangerousAddRef(ref bReleaseNeeded
);
1024 [System
.Security
.SecurityCritical
] // auto-generated
1025 [ResourceExposure(ResourceScope
.None
)]
1026 [ResourceConsumption(ResourceScope
.Machine
, ResourceScope
.Machine
)]
1027 [ReliabilityContract(Consistency
.WillNotCorruptState
, Cer
.MayFail
)]
1028 internal bool Unregister(
1029 WaitHandle waitObject
// object to be notified when all callbacks to delegates have completed
1032 bool result
= false;
1033 // needed for DangerousRelease
1034 RuntimeHelpers
.PrepareConstrainedRegions();
1040 // lock(this) cannot be used reliably in Cer since thin lock could be
1041 // promoted to syncblock and that is not a guaranteed operation
1042 bool bLockTaken
= false;
1045 if (Interlocked
.CompareExchange(ref m_lock
, 1, 0) == 0)
1052 result
= UnregisterWaitNative(GetHandle(), waitObject
== null ? null : waitObject
.SafeWaitHandle
);
1057 m_internalWaitObject
.SafeWaitHandle
.DangerousRelease();
1058 bReleaseNeeded
= false;
1060 // if result not true don't release/suppress here so finalizer can make another attempt
1061 SetHandle(InvalidHandle
);
1062 m_internalWaitObject
= null;
1063 GC
.SuppressFinalize(this);
1072 Thread
.SpinWait(1); // yield to processor
1074 while (!bLockTaken
);
1079 private bool ValidHandle()
1081 return (registeredWaitHandle
!= InvalidHandle
&& registeredWaitHandle
!= IntPtr
.Zero
);
1084 [System
.Security
.SecuritySafeCritical
] // auto-generated
1085 [ResourceExposure(ResourceScope
.None
)]
1086 [ResourceConsumption(ResourceScope
.Machine
, ResourceScope
.Machine
)]
1087 ~
RegisteredWaitHandleSafe()
1089 // if the app has already unregistered the wait, there is nothing to cleanup
1090 // we can detect this by checking the handle. Normally, there is no ---- here
1091 // so no need to protect reading of handle. However, if this object gets
1092 // resurrected and then someone does an unregister, it would introduce a ----
1094 // PrepareConstrainedRegions call not needed since finalizer already in Cer
1096 // lock(this) cannot be used reliably even in Cer since thin lock could be
1097 // promoted to syncblock and that is not a guaranteed operation
1099 // Note that we will not "spin" to get this lock. We make only a single attempt;
1100 // if we can't get the lock, it means some other thread is in the middle of a call
1101 // to Unregister, which will do the work of the finalizer anyway.
1103 // Further, it's actually critical that we *not* wait for the lock here, because
1104 // the other thread that's in the middle of Unregister may be suspended for shutdown.
1105 // Then, during the live-object finalization phase of shutdown, this thread would
1106 // end up spinning forever, as the other thread would never release the lock.
1107 // This will result in a "leak" of sorts (since the handle will not be cleaned up)
1108 // but the process is exiting anyway.
1110 // During AD-unload, we don’t finalize live objects until all threads have been
1111 // aborted out of the AD. Since these locked regions are CERs, we won’t abort them
1112 // while the lock is held. So there should be no leak on AD-unload.
1114 if (Interlocked
.CompareExchange(ref m_lock
, 1, 0) == 0)
1120 WaitHandleCleanupNative(registeredWaitHandle
);
1123 m_internalWaitObject
.SafeWaitHandle
.DangerousRelease();
1124 bReleaseNeeded
= false;
1126 SetHandle(InvalidHandle
);
1127 m_internalWaitObject
= null;
1137 [System
.Security
.SecurityCritical
] // auto-generated
1138 [ResourceExposure(ResourceScope
.Machine
)]
1139 [MethodImplAttribute(MethodImplOptions
.InternalCall
)]
1140 private static extern void WaitHandleCleanupNative(IntPtr handle
);
1142 [System
.Security
.SecurityCritical
] // auto-generated
1143 [ResourceExposure(ResourceScope
.Machine
)]
1144 [MethodImplAttribute(MethodImplOptions
.InternalCall
)]
1145 private static extern bool UnregisterWaitNative(IntPtr handle
, SafeHandle waitObject
);
1148 [System
.Runtime
.InteropServices
.ComVisible(true)]
1149 #if FEATURE_REMOTING
1150 public sealed class RegisteredWaitHandle
: MarshalByRefObject
{
1151 #else // FEATURE_REMOTING
1152 public sealed class RegisteredWaitHandle
{
1153 #endif // FEATURE_REMOTING
1154 private RegisteredWaitHandleSafe internalRegisteredWait
;
1156 internal RegisteredWaitHandle()
1158 internalRegisteredWait
= new RegisteredWaitHandleSafe();
1161 internal void SetHandle(IntPtr handle
)
1163 internalRegisteredWait
.SetHandle(handle
);
1166 [System
.Security
.SecurityCritical
] // auto-generated
1167 internal void SetWaitObject(WaitHandle waitObject
)
1169 internalRegisteredWait
.SetWaitObject(waitObject
);
1172 [System
.Security
.SecuritySafeCritical
] // auto-generated
1173 [System
.Runtime
.InteropServices
.ComVisible(true)]
1174 // This is the only public method on this class
1175 public bool Unregister(
1176 WaitHandle waitObject
// object to be notified when all callbacks to delegates have completed
1179 return internalRegisteredWait
.Unregister(waitObject
);
1185 // This type is necessary because VS 2010's debugger looks for a method named _ThreadPoolWaitCallbacck.PerformWaitCallback
1186 // on the stack to determine if a thread is a ThreadPool thread or not. We have a better way to do this for .NET 4.5, but
1187 // still need to maintain compatibility with VS 2010. When compat with VS 2010 is no longer an issue, this type may be
1190 internal static class _ThreadPoolWaitCallback
1192 #if FEATURE_INTERCEPTABLE_THREADPOOL_CALLBACK
1193 // This feature is used by Xamarin.iOS to use an NSAutoreleasePool
1194 // for every task done by the threadpool.
1195 static Func
<Func
<bool>, bool> dispatcher
;
1197 internal static void SetDispatcher (Func
<Func
<bool>, bool> value)
1203 [System
.Security
.SecurityCritical
]
1204 static internal bool PerformWaitCallback()
1206 #if FEATURE_INTERCEPTABLE_THREADPOOL_CALLBACK
1207 // store locally first to ensure another thread doesn't clear the field between checking for null and using it.
1208 var dispatcher
= _ThreadPoolWaitCallback
.dispatcher
;
1209 if (dispatcher
!= null)
1210 return dispatcher (ThreadPoolWorkQueue
.Dispatch
);
1213 return ThreadPoolWorkQueue
.Dispatch();
1217 internal sealed class QueueUserWorkItemCallback
: IThreadPoolWorkItem
1219 [System
.Security
.SecuritySafeCritical
]
1220 static QueueUserWorkItemCallback() {}
1222 private WaitCallback callback
;
1223 private ExecutionContext context
;
1224 private Object state
;
1227 volatile int executed
;
1229 ~
QueueUserWorkItemCallback()
1232 executed
!= 0 || Environment
.HasShutdownStarted
|| AppDomain
.CurrentDomain
.IsFinalizingForUnload(),
1233 "A QueueUserWorkItemCallback was never called!");
1236 void MarkExecuted(bool aborted
)
1238 GC
.SuppressFinalize(this);
1240 0 == Interlocked
.Exchange(ref executed
, 1) || aborted
,
1241 "A QueueUserWorkItemCallback was called twice!");
1246 internal QueueUserWorkItemCallback(WaitCallback waitCallback
, Object stateObj
, bool compressStack
, ref StackCrawlMark stackMark
)
1248 callback
= waitCallback
;
1250 if (compressStack
&& !ExecutionContext
.IsFlowSuppressed())
1252 // clone the exection context
1253 context
= ExecutionContext
.Capture(
1255 ExecutionContext
.CaptureOptions
.IgnoreSyncCtx
| ExecutionContext
.CaptureOptions
.OptimizeDefaultCase
);
1260 // internal test hook - used by tests to exercise work-stealing, etc.
1262 internal QueueUserWorkItemCallback(WaitCallback waitCallback
, Object stateObj
, ExecutionContext ec
)
1264 callback
= waitCallback
;
1270 void IThreadPoolWorkItem
.ExecuteWorkItem()
1273 MarkExecuted(false);
1276 // call directly if it is an unsafe call OR EC flow is suppressed
1277 if (context
== null)
1279 WaitCallback cb
= callback
;
1285 ExecutionContext
.Run(context
, ccb
, this, true);
1290 void IThreadPoolWorkItem
.MarkAborted(ThreadAbortException tae
)
1293 // this workitem didn't execute because we got a ThreadAbortException prior to the call to ExecuteWorkItem.
1294 // This counts as being executed for our purposes.
1299 [System
.Security
.SecurityCritical
]
1300 static internal ContextCallback ccb
= new ContextCallback(WaitCallback_Context
);
1302 [System
.Security
.SecurityCritical
]
1303 static private void WaitCallback_Context(Object state
)
1305 QueueUserWorkItemCallback obj
= (QueueUserWorkItemCallback
)state
;
1306 WaitCallback wc
= obj
.callback
as WaitCallback
;
1307 Contract
.Assert(null != wc
);
1312 internal class _ThreadPoolWaitOrTimerCallback
1314 [System
.Security
.SecuritySafeCritical
]
1315 static _ThreadPoolWaitOrTimerCallback() {}
1317 WaitOrTimerCallback _waitOrTimerCallback
;
1318 ExecutionContext _executionContext
;
1320 [System
.Security
.SecurityCritical
]
1321 static private ContextCallback _ccbt
= new ContextCallback(WaitOrTimerCallback_Context_t
);
1322 [System
.Security
.SecurityCritical
]
1323 static private ContextCallback _ccbf
= new ContextCallback(WaitOrTimerCallback_Context_f
);
1325 [System
.Security
.SecurityCritical
] // auto-generated
1326 internal _ThreadPoolWaitOrTimerCallback(WaitOrTimerCallback waitOrTimerCallback
, Object state
, bool compressStack
, ref StackCrawlMark stackMark
)
1328 _waitOrTimerCallback
= waitOrTimerCallback
;
1331 if (compressStack
&& !ExecutionContext
.IsFlowSuppressed())
1333 // capture the exection context
1334 _executionContext
= ExecutionContext
.Capture(
1336 ExecutionContext
.CaptureOptions
.IgnoreSyncCtx
| ExecutionContext
.CaptureOptions
.OptimizeDefaultCase
);
1340 [System
.Security
.SecurityCritical
]
1341 static private void WaitOrTimerCallback_Context_t(Object state
)
1343 WaitOrTimerCallback_Context(state
, true);
1346 [System
.Security
.SecurityCritical
]
1347 static private void WaitOrTimerCallback_Context_f(Object state
)
1349 WaitOrTimerCallback_Context(state
, false);
1352 static private void WaitOrTimerCallback_Context(Object state
, bool timedOut
)
1354 _ThreadPoolWaitOrTimerCallback helper
= (_ThreadPoolWaitOrTimerCallback
)state
;
1355 helper
._waitOrTimerCallback(helper
._state
, timedOut
);
1359 [System
.Security
.SecurityCritical
] // auto-generated
1360 static internal void PerformWaitOrTimerCallback(Object state
, bool timedOut
)
1362 _ThreadPoolWaitOrTimerCallback helper
= (_ThreadPoolWaitOrTimerCallback
)state
;
1363 Contract
.Assert(helper
!= null, "Null state passed to PerformWaitOrTimerCallback!");
1364 // call directly if it is an unsafe call OR EC flow is suppressed
1365 if (helper
._executionContext
== null)
1367 WaitOrTimerCallback callback
= helper
._waitOrTimerCallback
;
1368 callback(helper
._state
, timedOut
);
1372 using (ExecutionContext executionContext
= helper
._executionContext
.CreateCopy())
1375 ExecutionContext
.Run(executionContext
, _ccbt
, helper
, true);
1377 ExecutionContext
.Run(executionContext
, _ccbf
, helper
, true);
1384 [HostProtection(Synchronization
=true, ExternalThreading
=true)]
1385 public static partial class ThreadPool
1388 [System
.Security
.SecurityCritical
] // auto-generated
1390 [System
.Security
.SecuritySafeCritical
]
1392 #pragma warning disable 618
1393 [SecurityPermissionAttribute(SecurityAction
.Demand
, ControlThread
= true)]
1394 #pragma warning restore 618
1395 public static bool SetMaxThreads(int workerThreads
, int completionPortThreads
)
1397 return SetMaxThreadsNative(workerThreads
, completionPortThreads
);
1400 [System
.Security
.SecuritySafeCritical
] // auto-generated
1401 public static void GetMaxThreads(out int workerThreads
, out int completionPortThreads
)
1403 GetMaxThreadsNative(out workerThreads
, out completionPortThreads
);
1407 [System
.Security
.SecurityCritical
] // auto-generated
1409 [System
.Security
.SecuritySafeCritical
]
1411 #pragma warning disable 618
1412 [SecurityPermissionAttribute(SecurityAction
.Demand
, ControlThread
= true)]
1413 #pragma warning restore 618
1414 public static bool SetMinThreads(int workerThreads
, int completionPortThreads
)
1416 return SetMinThreadsNative(workerThreads
, completionPortThreads
);
1419 [System
.Security
.SecuritySafeCritical
] // auto-generated
1420 public static void GetMinThreads(out int workerThreads
, out int completionPortThreads
)
1422 GetMinThreadsNative(out workerThreads
, out completionPortThreads
);
1425 [System
.Security
.SecuritySafeCritical
] // auto-generated
1426 public static void GetAvailableThreads(out int workerThreads
, out int completionPortThreads
)
1428 GetAvailableThreadsNative(out workerThreads
, out completionPortThreads
);
1431 [System
.Security
.SecuritySafeCritical
] // auto-generated
1432 [CLSCompliant(false)]
1433 [MethodImplAttribute(MethodImplOptions
.NoInlining
)] // Methods containing StackCrawlMark local var has to be marked non-inlineable
1434 public static RegisteredWaitHandle
RegisterWaitForSingleObject( // throws RegisterWaitException
1435 WaitHandle waitObject
,
1436 WaitOrTimerCallback callBack
,
1438 uint millisecondsTimeOutInterval
,
1439 bool executeOnlyOnce
// NOTE: we do not allow other options that allow the callback to be queued as an APC
1442 StackCrawlMark stackMark
= StackCrawlMark
.LookForMyCaller
;
1443 return RegisterWaitForSingleObject(waitObject
,callBack
,state
,millisecondsTimeOutInterval
,executeOnlyOnce
,ref stackMark
,true);
1446 [System
.Security
.SecurityCritical
] // auto-generated_required
1447 [CLSCompliant(false)]
1448 [MethodImplAttribute(MethodImplOptions
.NoInlining
)] // Methods containing StackCrawlMark local var has to be marked non-inlineable
1449 public static RegisteredWaitHandle
UnsafeRegisterWaitForSingleObject( // throws RegisterWaitException
1450 WaitHandle waitObject
,
1451 WaitOrTimerCallback callBack
,
1453 uint millisecondsTimeOutInterval
,
1454 bool executeOnlyOnce
// NOTE: we do not allow other options that allow the callback to be queued as an APC
1457 StackCrawlMark stackMark
= StackCrawlMark
.LookForMyCaller
;
1458 return RegisterWaitForSingleObject(waitObject
,callBack
,state
,millisecondsTimeOutInterval
,executeOnlyOnce
,ref stackMark
,false);
1462 [System
.Security
.SecurityCritical
] // auto-generated
1463 private static RegisteredWaitHandle
RegisterWaitForSingleObject( // throws RegisterWaitException
1464 WaitHandle waitObject
,
1465 WaitOrTimerCallback callBack
,
1467 uint millisecondsTimeOutInterval
,
1468 bool executeOnlyOnce
, // NOTE: we do not allow other options that allow the callback to be queued as an APC
1469 ref StackCrawlMark stackMark
,
1474 #if FEATURE_REMOTING
1475 if (RemotingServices
.IsTransparentProxy(waitObject
))
1476 throw new InvalidOperationException(Environment
.GetResourceString("InvalidOperation_WaitOnTransparentProxy"));
1477 Contract
.EndContractBlock();
1480 RegisteredWaitHandle registeredWaitHandle
= new RegisteredWaitHandle();
1482 if (callBack
!= null)
1484 _ThreadPoolWaitOrTimerCallback callBackHelper
= new _ThreadPoolWaitOrTimerCallback(callBack
, state
, compressStack
, ref stackMark
);
1485 state
= (Object
)callBackHelper
;
1486 // call SetWaitObject before native call so that waitObject won't be closed before threadpoolmgr registration
1487 // this could occur if callback were to fire before SetWaitObject does its addref
1488 registeredWaitHandle
.SetWaitObject(waitObject
);
1489 IntPtr nativeRegisteredWaitHandle
= RegisterWaitForSingleObjectNative(waitObject
,
1491 millisecondsTimeOutInterval
,
1493 registeredWaitHandle
,
1496 registeredWaitHandle
.SetHandle(nativeRegisteredWaitHandle
);
1500 throw new ArgumentNullException("WaitOrTimerCallback");
1502 return registeredWaitHandle
;
1504 if (waitObject
== null)
1505 throw new ArgumentNullException ("waitObject");
1506 if (callBack
== null)
1507 throw new ArgumentNullException ("callBack");
1508 if (millisecondsTimeOutInterval
!= Timeout
.UnsignedInfinite
&& millisecondsTimeOutInterval
> Int32
.MaxValue
)
1509 throw new NotSupportedException ("Timeout is too big. Maximum is Int32.MaxValue");
1511 RegisteredWaitHandle waiter
= new RegisteredWaitHandle (waitObject
, callBack
, state
, new TimeSpan (0, 0, 0, 0, (int) millisecondsTimeOutInterval
), executeOnlyOnce
);
1513 QueueUserWorkItem (new WaitCallback (waiter
.Wait
), null);
1515 UnsafeQueueUserWorkItem (new WaitCallback (waiter
.Wait
), null);
1522 [System
.Security
.SecuritySafeCritical
] // auto-generated
1523 [MethodImplAttribute(MethodImplOptions
.NoInlining
)] // Methods containing StackCrawlMark local var has to be marked non-inlineable
1524 public static RegisteredWaitHandle
RegisterWaitForSingleObject( // throws RegisterWaitException
1525 WaitHandle waitObject
,
1526 WaitOrTimerCallback callBack
,
1528 int millisecondsTimeOutInterval
,
1529 bool executeOnlyOnce
// NOTE: we do not allow other options that allow the callback to be queued as an APC
1532 if (millisecondsTimeOutInterval
< -1)
1533 throw new ArgumentOutOfRangeException("millisecondsTimeOutInterval", Environment
.GetResourceString("ArgumentOutOfRange_NeedNonNegOrNegative1"));
1534 Contract
.EndContractBlock();
1535 StackCrawlMark stackMark
= StackCrawlMark
.LookForMyCaller
;
1536 return RegisterWaitForSingleObject(waitObject
,callBack
,state
,millisecondsTimeOutInterval
== Timeout
.Infinite
? Timeout
.UnsignedInfinite
: (UInt32
)millisecondsTimeOutInterval
,executeOnlyOnce
,ref stackMark
,true);
1539 [System
.Security
.SecurityCritical
] // auto-generated_required
1540 [MethodImplAttribute(MethodImplOptions
.NoInlining
)] // Methods containing StackCrawlMark local var has to be marked non-inlineable
1541 public static RegisteredWaitHandle
UnsafeRegisterWaitForSingleObject( // throws RegisterWaitException
1542 WaitHandle waitObject
,
1543 WaitOrTimerCallback callBack
,
1545 int millisecondsTimeOutInterval
,
1546 bool executeOnlyOnce
// NOTE: we do not allow other options that allow the callback to be queued as an APC
1549 if (millisecondsTimeOutInterval
< -1)
1550 throw new ArgumentOutOfRangeException("millisecondsTimeOutInterval", Environment
.GetResourceString("ArgumentOutOfRange_NeedNonNegOrNegative1"));
1551 Contract
.EndContractBlock();
1552 StackCrawlMark stackMark
= StackCrawlMark
.LookForMyCaller
;
1553 return RegisterWaitForSingleObject(waitObject
,callBack
,state
,millisecondsTimeOutInterval
== Timeout
.Infinite
? Timeout
.UnsignedInfinite
: (UInt32
)millisecondsTimeOutInterval
,executeOnlyOnce
,ref stackMark
,false);
1556 [System
.Security
.SecuritySafeCritical
] // auto-generated
1557 [MethodImplAttribute(MethodImplOptions
.NoInlining
)] // Methods containing StackCrawlMark local var has to be marked non-inlineable
1558 public static RegisteredWaitHandle
RegisterWaitForSingleObject( // throws RegisterWaitException
1559 WaitHandle waitObject
,
1560 WaitOrTimerCallback callBack
,
1562 long millisecondsTimeOutInterval
,
1563 bool executeOnlyOnce
// NOTE: we do not allow other options that allow the callback to be queued as an APC
1566 if (millisecondsTimeOutInterval
< -1)
1567 throw new ArgumentOutOfRangeException("millisecondsTimeOutInterval", Environment
.GetResourceString("ArgumentOutOfRange_NeedNonNegOrNegative1"));
1568 Contract
.EndContractBlock();
1569 StackCrawlMark stackMark
= StackCrawlMark
.LookForMyCaller
;
1570 return RegisterWaitForSingleObject(waitObject
,callBack
,state
,millisecondsTimeOutInterval
== Timeout
.Infinite
? Timeout
.UnsignedInfinite
: (UInt32
)millisecondsTimeOutInterval
,executeOnlyOnce
,ref stackMark
,true);
1573 [System
.Security
.SecurityCritical
] // auto-generated_required
1574 [MethodImplAttribute(MethodImplOptions
.NoInlining
)] // Methods containing StackCrawlMark local var has to be marked non-inlineable
1575 public static RegisteredWaitHandle
UnsafeRegisterWaitForSingleObject( // throws RegisterWaitException
1576 WaitHandle waitObject
,
1577 WaitOrTimerCallback callBack
,
1579 long millisecondsTimeOutInterval
,
1580 bool executeOnlyOnce
// NOTE: we do not allow other options that allow the callback to be queued as an APC
1583 if (millisecondsTimeOutInterval
< -1)
1584 throw new ArgumentOutOfRangeException("millisecondsTimeOutInterval", Environment
.GetResourceString("ArgumentOutOfRange_NeedNonNegOrNegative1"));
1585 Contract
.EndContractBlock();
1586 StackCrawlMark stackMark
= StackCrawlMark
.LookForMyCaller
;
1587 return RegisterWaitForSingleObject(waitObject
,callBack
,state
,millisecondsTimeOutInterval
== Timeout
.Infinite
? Timeout
.UnsignedInfinite
: (UInt32
)millisecondsTimeOutInterval
,executeOnlyOnce
,ref stackMark
,false);
1590 [System
.Security
.SecuritySafeCritical
] // auto-generated
1591 [MethodImplAttribute(MethodImplOptions
.NoInlining
)] // Methods containing StackCrawlMark local var has to be marked non-inlineable
1592 public static RegisteredWaitHandle
RegisterWaitForSingleObject(
1593 WaitHandle waitObject
,
1594 WaitOrTimerCallback callBack
,
1597 bool executeOnlyOnce
1600 long tm
= (long)timeout
.TotalMilliseconds
;
1602 throw new ArgumentOutOfRangeException("timeout", Environment
.GetResourceString("ArgumentOutOfRange_NeedNonNegOrNegative1"));
1603 if (tm
> (long) Int32
.MaxValue
)
1604 throw new ArgumentOutOfRangeException("timeout", Environment
.GetResourceString("ArgumentOutOfRange_LessEqualToIntegerMaxVal"));
1605 StackCrawlMark stackMark
= StackCrawlMark
.LookForMyCaller
;
1606 return RegisterWaitForSingleObject(waitObject
,callBack
,state
,(UInt32
)tm
,executeOnlyOnce
,ref stackMark
,true);
1609 [System
.Security
.SecurityCritical
] // auto-generated_required
1610 [MethodImplAttribute(MethodImplOptions
.NoInlining
)] // Methods containing StackCrawlMark local var has to be marked non-inlineable
1611 public static RegisteredWaitHandle
UnsafeRegisterWaitForSingleObject(
1612 WaitHandle waitObject
,
1613 WaitOrTimerCallback callBack
,
1616 bool executeOnlyOnce
1619 long tm
= (long)timeout
.TotalMilliseconds
;
1621 throw new ArgumentOutOfRangeException("timeout", Environment
.GetResourceString("ArgumentOutOfRange_NeedNonNegOrNegative1"));
1622 if (tm
> (long) Int32
.MaxValue
)
1623 throw new ArgumentOutOfRangeException("timeout", Environment
.GetResourceString("ArgumentOutOfRange_LessEqualToIntegerMaxVal"));
1624 StackCrawlMark stackMark
= StackCrawlMark
.LookForMyCaller
;
1625 return RegisterWaitForSingleObject(waitObject
,callBack
,state
,(UInt32
)tm
,executeOnlyOnce
,ref stackMark
,false);
1628 [System
.Security
.SecuritySafeCritical
] // auto-generated
1629 [MethodImplAttribute(MethodImplOptions
.NoInlining
)] // Methods containing StackCrawlMark local var has to be marked non-inlineable
1630 public static bool QueueUserWorkItem(
1631 WaitCallback callBack
, // NOTE: we do not expose options that allow the callback to be queued as an APC
1635 StackCrawlMark stackMark
= StackCrawlMark
.LookForMyCaller
;
1636 return QueueUserWorkItemHelper(callBack
,state
,ref stackMark
,true);
1639 [System
.Security
.SecuritySafeCritical
] // auto-generated
1640 [MethodImplAttribute(MethodImplOptions
.NoInlining
)] // Methods containing StackCrawlMark local var has to be marked non-inlineable
1641 public static bool QueueUserWorkItem(
1642 WaitCallback callBack
// NOTE: we do not expose options that allow the callback to be queued as an APC
1645 StackCrawlMark stackMark
= StackCrawlMark
.LookForMyCaller
;
1646 return QueueUserWorkItemHelper(callBack
,null,ref stackMark
,true);
1649 [System
.Security
.SecurityCritical
] // auto-generated_required
1650 [MethodImplAttribute(MethodImplOptions
.NoInlining
)] // Methods containing StackCrawlMark local var has to be marked non-inlineable
1651 public static bool UnsafeQueueUserWorkItem(
1652 WaitCallback callBack
, // NOTE: we do not expose options that allow the callback to be queued as an APC
1656 StackCrawlMark stackMark
= StackCrawlMark
.LookForMyCaller
;
1657 return QueueUserWorkItemHelper(callBack
,state
,ref stackMark
,false);
1660 //ThreadPool has per-appdomain managed queue of work-items. The VM is
1661 //responsible for just scheduling threads into appdomains. After that
1662 //work-items are dispatched from the managed queue.
1663 [System
.Security
.SecurityCritical
] // auto-generated
1664 private static bool QueueUserWorkItemHelper(WaitCallback callBack
, Object state
, ref StackCrawlMark stackMark
, bool compressStack
)
1666 bool success
= true;
1668 if (callBack
!= null)
1670 //The thread pool maintains a per-appdomain managed work queue.
1671 //New thread pool entries are added in the managed queue.
1672 //The VM is responsible for the actual growing/shrinking of
1675 EnsureVMInitialized();
1678 // If we are able to create the workitem, we need to get it in the queue without being interrupted
1679 // by a ThreadAbortException.
1684 QueueUserWorkItemCallback tpcallBack
= new QueueUserWorkItemCallback(callBack
, state
, compressStack
, ref stackMark
);
1685 ThreadPoolGlobals
.workQueue
.Enqueue(tpcallBack
, true);
1691 throw new ArgumentNullException("WaitCallback");
1697 internal static void UnsafeQueueCustomWorkItem(IThreadPoolWorkItem workItem
, bool forceGlobal
)
1699 Contract
.Assert(null != workItem
);
1700 EnsureVMInitialized();
1703 // Enqueue needs to be protected from ThreadAbort
1708 ThreadPoolGlobals
.workQueue
.Enqueue(workItem
, forceGlobal
);
1712 // This method tries to take the target callback out of the current thread's queue.
1714 internal static bool TryPopCustomWorkItem(IThreadPoolWorkItem workItem
)
1716 Contract
.Assert(null != workItem
);
1717 if (!ThreadPoolGlobals
.vmTpInitialized
)
1718 return false; //Not initialized, so there's no way this workitem was ever queued.
1719 return ThreadPoolGlobals
.workQueue
.LocalFindAndPop(workItem
);
1722 // Get all workitems. Called by TaskScheduler in its debugger hooks.
1724 internal static IEnumerable
<IThreadPoolWorkItem
> GetQueuedWorkItems()
1726 return EnumerateQueuedWorkItems(ThreadPoolWorkQueue
.allThreadQueues
.Current
, ThreadPoolGlobals
.workQueue
.queueTail
);
1729 internal static IEnumerable
<IThreadPoolWorkItem
> EnumerateQueuedWorkItems(ThreadPoolWorkQueue
.WorkStealingQueue
[] wsQueues
, ThreadPoolWorkQueue
.QueueSegment globalQueueTail
)
1731 if (wsQueues
!= null)
1733 // First, enumerate all workitems in thread-local queues.
1734 foreach (ThreadPoolWorkQueue
.WorkStealingQueue wsq
in wsQueues
)
1736 if (wsq
!= null && wsq
.m_array
!= null)
1738 IThreadPoolWorkItem
[] items
= wsq
.m_array
;
1739 for (int i
= 0; i
< items
.Length
; i
++)
1741 IThreadPoolWorkItem item
= items
[i
];
1749 if (globalQueueTail
!= null)
1751 // Now the global queue
1752 for (ThreadPoolWorkQueue
.QueueSegment segment
= globalQueueTail
;
1754 segment
= segment
.Next
)
1756 IThreadPoolWorkItem
[] items
= segment
.nodes
;
1757 for (int i
= 0; i
< items
.Length
; i
++)
1759 IThreadPoolWorkItem item
= items
[i
];
1768 internal static IEnumerable
<IThreadPoolWorkItem
> GetLocallyQueuedWorkItems()
1770 return EnumerateQueuedWorkItems(new ThreadPoolWorkQueue
.WorkStealingQueue
[] { ThreadPoolWorkQueueThreadLocals.threadLocals.workStealingQueue }
, null);
1774 internal static IEnumerable
<IThreadPoolWorkItem
> GetGloballyQueuedWorkItems()
1776 return EnumerateQueuedWorkItems(null, ThreadPoolGlobals
.workQueue
.queueTail
);
1779 private static object[] ToObjectArray(IEnumerable
<IThreadPoolWorkItem
> workitems
)
1782 foreach (IThreadPoolWorkItem item
in workitems
)
1787 object[] result
= new object[i
];
1789 foreach (IThreadPoolWorkItem item
in workitems
)
1791 if (i
< result
.Length
) //just in case someone calls us while the queues are in motion
1799 // This is the method the debugger will actually call, if it ends up calling
1800 // into ThreadPool directly. Tests can use this to simulate a debugger, as well.
1802 internal static object[] GetQueuedWorkItemsForDebugger()
1804 return ToObjectArray(GetQueuedWorkItems());
1808 internal static object[] GetGloballyQueuedWorkItemsForDebugger()
1810 return ToObjectArray(GetGloballyQueuedWorkItems());
1814 internal static object[] GetLocallyQueuedWorkItemsForDebugger()
1816 return ToObjectArray(GetLocallyQueuedWorkItems());
1819 [System
.Security
.SecurityCritical
] // auto-generated
1820 [ResourceExposure(ResourceScope
.None
)]
1821 [MethodImplAttribute(MethodImplOptions
.InternalCall
)]
1822 internal static extern bool RequestWorkerThread();
1824 [System
.Security
.SecurityCritical
] // auto-generated
1825 [ResourceExposure(ResourceScope
.None
)]
1826 [MethodImplAttribute(MethodImplOptions
.InternalCall
)]
1827 unsafe private static extern bool PostQueuedCompletionStatus(NativeOverlapped
* overlapped
);
1829 [System
.Security
.SecurityCritical
] // auto-generated_required
1830 [CLSCompliant(false)]
1831 unsafe public static bool UnsafeQueueNativeOverlapped(NativeOverlapped
* overlapped
)
1833 #if FEATURE_CORECLR && !FEATURE_LEGACYNETCF
1834 if(Environment
.OSVersion
.Platform
== PlatformID
.MacOSX
)
1835 throw new NotSupportedException(Environment
.GetResourceString("Arg_NotSupportedException"));
1836 Contract
.EndContractBlock();
1839 return PostQueuedCompletionStatus(overlapped
);
1843 private static void EnsureVMInitialized()
1845 if (!ThreadPoolGlobals
.vmTpInitialized
)
1847 ThreadPool
.InitializeVMTp(ref ThreadPoolGlobals
.enableWorkerTracking
);
1848 ThreadPoolGlobals
.vmTpInitialized
= true;
1854 [System
.Security
.SecurityCritical
] // auto-generated
1855 [ResourceExposure(ResourceScope
.None
)]
1856 [MethodImplAttribute(MethodImplOptions
.InternalCall
)]
1857 private static extern bool SetMinThreadsNative(int workerThreads
, int completionPortThreads
);
1859 [System
.Security
.SecurityCritical
] // auto-generated
1860 [ResourceExposure(ResourceScope
.None
)]
1861 [MethodImplAttribute(MethodImplOptions
.InternalCall
)]
1862 private static extern bool SetMaxThreadsNative(int workerThreads
, int completionPortThreads
);
1864 [System
.Security
.SecurityCritical
] // auto-generated
1865 [ResourceExposure(ResourceScope
.None
)]
1866 [MethodImplAttribute(MethodImplOptions
.InternalCall
)]
1867 private static extern void GetMinThreadsNative(out int workerThreads
, out int completionPortThreads
);
1869 [System
.Security
.SecurityCritical
] // auto-generated
1870 [ResourceExposure(ResourceScope
.None
)]
1871 [MethodImplAttribute(MethodImplOptions
.InternalCall
)]
1872 private static extern void GetMaxThreadsNative(out int workerThreads
, out int completionPortThreads
);
1874 [System
.Security
.SecurityCritical
] // auto-generated
1875 [ResourceExposure(ResourceScope
.None
)]
1876 [MethodImplAttribute(MethodImplOptions
.InternalCall
)]
1877 private static extern void GetAvailableThreadsNative(out int workerThreads
, out int completionPortThreads
);
1879 [System
.Security
.SecurityCritical
] // auto-generated
1880 [ResourceExposure(ResourceScope
.None
)]
1881 [MethodImplAttribute(MethodImplOptions
.InternalCall
)]
1882 internal static extern bool NotifyWorkItemComplete();
1884 [System
.Security
.SecurityCritical
]
1885 [ResourceExposure(ResourceScope
.None
)]
1886 [MethodImplAttribute(MethodImplOptions
.InternalCall
)]
1887 internal static extern void ReportThreadStatus(bool isWorking
);
1889 [System
.Security
.SecuritySafeCritical
]
1890 internal static void NotifyWorkItemProgress()
1892 if (!ThreadPoolGlobals
.vmTpInitialized
)
1893 ThreadPool
.InitializeVMTp(ref ThreadPoolGlobals
.enableWorkerTracking
);
1894 NotifyWorkItemProgressNative();
1897 [System
.Security
.SecurityCritical
] // auto-generated
1898 [ResourceExposure(ResourceScope
.None
)]
1899 [MethodImplAttribute(MethodImplOptions
.InternalCall
)]
1900 internal static extern void NotifyWorkItemProgressNative();
1903 [System
.Security
.SecurityCritical
]
1904 [ResourceExposure(ResourceScope
.None
)]
1905 [MethodImplAttribute(MethodImplOptions
.InternalCall
)]
1906 internal static extern void NotifyWorkItemQueued();
1909 [System
.Security
.SecurityCritical
] // auto-generated
1910 [ResourceExposure(ResourceScope
.None
)]
1911 [MethodImplAttribute(MethodImplOptions
.InternalCall
)]
1912 internal static extern bool IsThreadPoolHosted();
1914 [System
.Security
.SecurityCritical
] // auto-generated
1915 [ResourceExposure(ResourceScope
.None
)]
1916 [MethodImplAttribute(MethodImplOptions
.InternalCall
)]
1917 private static extern void InitializeVMTp(ref bool enableWorkerTracking
);
1920 [System
.Security
.SecurityCritical
] // auto-generated
1921 [ResourceExposure(ResourceScope
.None
)]
1922 [MethodImplAttribute(MethodImplOptions
.InternalCall
)]
1923 private static extern IntPtr
RegisterWaitForSingleObjectNative(
1924 WaitHandle waitHandle
,
1926 uint timeOutInterval
,
1927 bool executeOnlyOnce
,
1928 RegisteredWaitHandle registeredWaitHandle
,
1929 ref StackCrawlMark stackMark
,
1934 #if !FEATURE_CORECLR
1935 [System
.Security
.SecuritySafeCritical
] // auto-generated
1936 [Obsolete("ThreadPool.BindHandle(IntPtr) has been deprecated. Please use ThreadPool.BindHandle(SafeHandle) instead.", false)]
1937 [SecurityPermissionAttribute( SecurityAction
.Demand
, Flags
= SecurityPermissionFlag
.UnmanagedCode
)]
1938 public static bool BindHandle(
1942 return BindIOCompletionCallbackNative(osHandle
);
1947 [System
.Security
.SecurityCritical
] // auto-generated
1949 [System
.Security
.SecuritySafeCritical
]
1951 #pragma warning disable 618
1952 [SecurityPermissionAttribute(SecurityAction
.Demand
, Flags
= SecurityPermissionFlag
.UnmanagedCode
)]
1953 #pragma warning restore 618
1954 public static bool BindHandle(SafeHandle osHandle
)
1956 #if FEATURE_CORECLR && !FEATURE_LEGACYNETCF
1957 if(Environment
.OSVersion
.Platform
== PlatformID
.MacOSX
)
1958 throw new NotSupportedException(Environment
.GetResourceString("Arg_NotSupportedException"));
1959 Contract
.EndContractBlock();
1962 if (osHandle
== null)
1963 throw new ArgumentNullException("osHandle");
1966 bool mustReleaseSafeHandle
= false;
1967 RuntimeHelpers
.PrepareConstrainedRegions();
1969 osHandle
.DangerousAddRef(ref mustReleaseSafeHandle
);
1970 ret
= BindIOCompletionCallbackNative(osHandle
.DangerousGetHandle());
1973 if (mustReleaseSafeHandle
)
1974 osHandle
.DangerousRelease();
1979 [System
.Security
.SecurityCritical
] // auto-generated
1980 [ResourceExposure(ResourceScope
.None
)]
1981 [MethodImplAttribute(MethodImplOptions
.InternalCall
)]
1982 [ReliabilityContract(Consistency
.WillNotCorruptState
, Cer
.MayFail
)]
1983 private static extern bool BindIOCompletionCallbackNative(IntPtr fileHandle
);