internal static bool PerformWaitCallback() { if (ThreadPoolGlobals.useNewWorkerPool) { return(ThreadPoolWorkQueue.Dispatch()); } int num = 0; _ThreadPoolWaitCallback callback = null; int tickCount = Environment.TickCount; do { int num3 = ThreadPoolGlobals.tpQueue.DeQueue(ref callback); if (callback == null) { break; } ThreadPool.CompleteThreadPoolRequest((uint)num3); PerformWaitCallbackInternal(callback); num = Environment.TickCount - tickCount; }while ((num <= ThreadPoolGlobals.tpQuantum) || !ThreadPool.ShouldReturnToVm()); return(true); }
private static void WorkerThreadStart() { Thread.CurrentThread.SetThreadPoolWorkerThreadName(); PortableThreadPool threadPoolInstance = ThreadPoolInstance; if (NativeRuntimeEventSource.Log.IsEnabled()) { NativeRuntimeEventSource.Log.ThreadPoolWorkerThreadStart( (uint)threadPoolInstance._separated.counts.VolatileRead().NumExistingThreads); } LowLevelLock threadAdjustmentLock = threadPoolInstance._threadAdjustmentLock; LowLevelLifoSemaphore semaphore = s_semaphore; while (true) { bool spinWait = true; while (semaphore.Wait(ThreadPoolThreadTimeoutMs, spinWait)) { bool alreadyRemovedWorkingWorker = false; while (TakeActiveRequest(threadPoolInstance)) { threadPoolInstance._separated.lastDequeueTime = Environment.TickCount; if (!ThreadPoolWorkQueue.Dispatch()) { // ShouldStopProcessingWorkNow() caused the thread to stop processing work, and it would have // already removed this working worker in the counts. This typically happens when hill climbing // decreases the worker thread count goal. alreadyRemovedWorkingWorker = true; break; } if (threadPoolInstance._separated.numRequestedWorkers <= 0) { break; } // In highly bursty cases with short bursts of work, especially in the portable thread pool // implementation, worker threads are being released and entering Dispatch very quickly, not finding // much work in Dispatch, and soon afterwards going back to Dispatch, causing extra thrashing on // data and some interlocked operations, and similarly when the thread pool runs out of work. Since // there is a pending request for work, introduce a slight delay before serving the next request. // The spin-wait is mainly for when the sleep is not effective due to there being no other threads // to schedule. Thread.UninterruptibleSleep0(); if (!Environment.IsSingleProcessor) { Thread.SpinWait(1); } } // Don't spin-wait on the semaphore next time if the thread was actively stopped from processing work, // as it's unlikely that the worker thread count goal would be increased again so soon afterwards that // the semaphore would be released within the spin-wait window spinWait = !alreadyRemovedWorkingWorker; if (!alreadyRemovedWorkingWorker) { // If we woke up but couldn't find a request, or ran out of work items to process, we need to update // the number of working workers to reflect that we are done working for now RemoveWorkingWorker(threadPoolInstance); } } threadAdjustmentLock.Acquire(); try { // At this point, the thread's wait timed out. We are shutting down this thread. // We are going to decrement the number of existing threads to no longer include this one // and then change the max number of threads in the thread pool to reflect that we don't need as many // as we had. Finally, we are going to tell hill climbing that we changed the max number of threads. ThreadCounts counts = threadPoolInstance._separated.counts; while (true) { // Since this thread is currently registered as an existing thread, if more work comes in meanwhile, // this thread would be expected to satisfy the new work. Ensure that NumExistingThreads is not // decreased below NumProcessingWork, as that would be indicative of such a case. if (counts.NumExistingThreads <= counts.NumProcessingWork) { // In this case, enough work came in that this thread should not time out and should go back to work. break; } ThreadCounts newCounts = counts; short newNumExistingThreads = --newCounts.NumExistingThreads; short newNumThreadsGoal = Math.Max( threadPoolInstance.MinThreadsGoal, Math.Min(newNumExistingThreads, counts.NumThreadsGoal)); newCounts.NumThreadsGoal = newNumThreadsGoal; ThreadCounts oldCounts = threadPoolInstance._separated.counts.InterlockedCompareExchange(newCounts, counts); if (oldCounts == counts) { HillClimbing.ThreadPoolHillClimber.ForceChange( newNumThreadsGoal, HillClimbing.StateOrTransition.ThreadTimedOut); if (NativeRuntimeEventSource.Log.IsEnabled()) { NativeRuntimeEventSource.Log.ThreadPoolWorkerThreadStop((uint)newNumExistingThreads); } return; } counts = oldCounts; } } finally { threadAdjustmentLock.Release(); } } }
private static void DispatchCallback(IntPtr instance, IntPtr context, IntPtr work) { RuntimeThread.InitializeThreadPoolThread(); Debug.Assert(s_work == work); ThreadPoolWorkQueue.Dispatch(); }
internal static bool PerformWaitCallback() => ThreadPoolWorkQueue.Dispatch();
internal static bool Dispatch() { ThreadPoolWorkQueue threadPoolWorkQueue = ThreadPoolGlobals.workQueue; int tickCount = Environment.TickCount; threadPoolWorkQueue.MarkThreadRequestSatisfied(); threadPoolWorkQueue.loggingEnabled = FrameworkEventSource.Log.IsEnabled(EventLevel.Verbose, (EventKeywords)18); bool flag1 = true; IThreadPoolWorkItem callback = (IThreadPoolWorkItem)null; try { ThreadPoolWorkQueueThreadLocals tl = threadPoolWorkQueue.EnsureCurrentThreadHasQueue(); while ((long)(Environment.TickCount - tickCount) < (long)ThreadPoolGlobals.tpQuantum) { try { } finally { bool missedSteal = false; threadPoolWorkQueue.Dequeue(tl, out callback, out missedSteal); if (callback == null) { flag1 = missedSteal; } else { threadPoolWorkQueue.EnsureThreadRequested(); } } if (callback == null) { return(true); } if (threadPoolWorkQueue.loggingEnabled) { FrameworkEventSource.Log.ThreadPoolDequeueWorkObject((object)callback); } if (ThreadPoolGlobals.enableWorkerTracking) { bool flag2 = false; try { try { } finally { ThreadPool.ReportThreadStatus(true); flag2 = true; } callback.ExecuteWorkItem(); callback = (IThreadPoolWorkItem)null; } finally { if (flag2) { ThreadPool.ReportThreadStatus(false); } } } else { callback.ExecuteWorkItem(); callback = (IThreadPoolWorkItem)null; } if (!ThreadPool.NotifyWorkItemComplete()) { return(false); } } return(true); } catch (ThreadAbortException ex) { if (callback != null) { callback.MarkAborted(ex); } flag1 = false; } finally { if (flag1) { threadPoolWorkQueue.EnsureThreadRequested(); } } return(true); }
public FastRandom random = new FastRandom(Environment.CurrentManagedThreadId); // mutable struct, do not copy or make readonly public ThreadPoolWorkQueueThreadLocals(ThreadPoolWorkQueue tpq) { workQueue = tpq; workStealingQueue = new ThreadPoolWorkQueue.WorkStealingQueue(); ThreadPoolWorkQueue.WorkStealingQueueList.Add(workStealingQueue); }
private static void Dispatch() { ThreadPoolWorkQueue.Dispatch(); }
private static void Callback() { _callbackQueued = false; ThreadPoolWorkQueue.Dispatch(); }
public ThreadPoolWorkQueueThreadLocals(ThreadPoolWorkQueue tpq) { this.workQueue = tpq; this.workStealingQueue = new ThreadPoolWorkQueue.WorkStealingQueue(); ThreadPoolWorkQueue.allThreadQueues.Add(this.workStealingQueue); }
internal static bool Dispatch() { ThreadPoolWorkQueue workQueue = ThreadPoolGlobals.workQueue; int tickCount = Environment.TickCount; workQueue.MarkThreadRequestSatisfied(); workQueue.loggingEnabled = FrameworkEventSource.Log.IsEnabled(EventLevel.Verbose, (EventKeywords)18L); bool flag = true; IThreadPoolWorkItem threadPoolWorkItem = null; try { ThreadPoolWorkQueueThreadLocals tl = workQueue.EnsureCurrentThreadHasQueue(); while ((long)(Environment.TickCount - tickCount) < (long)((ulong)ThreadPoolGlobals.tpQuantum)) { try { } finally { bool flag2 = false; workQueue.Dequeue(tl, out threadPoolWorkItem, out flag2); if (threadPoolWorkItem == null) { flag = flag2; } else { workQueue.EnsureThreadRequested(); } } if (threadPoolWorkItem == null) { return(true); } if (workQueue.loggingEnabled) { FrameworkEventSource.Log.ThreadPoolDequeueWorkObject(threadPoolWorkItem); } if (ThreadPoolGlobals.enableWorkerTracking) { bool flag3 = false; try { try { } finally { ThreadPool.ReportThreadStatus(true); flag3 = true; } threadPoolWorkItem.ExecuteWorkItem(); threadPoolWorkItem = null; goto IL_A6; } finally { if (flag3) { ThreadPool.ReportThreadStatus(false); } } goto IL_9E; } goto IL_9E; IL_A6: if (!ThreadPool.NotifyWorkItemComplete()) { return(false); } continue; IL_9E: threadPoolWorkItem.ExecuteWorkItem(); threadPoolWorkItem = null; goto IL_A6; } return(true); } catch (ThreadAbortException tae) { if (threadPoolWorkItem != null) { threadPoolWorkItem.MarkAborted(tae); } flag = false; } finally { if (flag) { workQueue.EnsureThreadRequested(); } } return(true); }
private static void DispatchCallback(IntPtr instance, IntPtr context, IntPtr work) { Debug.Assert(s_work == work); ThreadPoolWorkQueue.Dispatch(); }
private static void WorkerThreadStart() { // TODO: Event: Worker Thread Start event RuntimeThread currentThread = RuntimeThread.CurrentThread; while (true) { // TODO: Event: Worker thread wait event while (s_semaphore.Wait(TimeoutMs)) { if (TakeActiveRequest()) { Volatile.Write(ref ThreadPoolInstance._separated.lastDequeueTime, Environment.TickCount); if (ThreadPoolWorkQueue.Dispatch()) { // If the queue runs out of work for us, we need to update the number of working workers to reflect that we are done working for now RemoveWorkingWorker(); } // Reset thread-local state that we control. if (currentThread.Priority != ThreadPriority.Normal) { currentThread.Priority = ThreadPriority.Normal; } CultureInfo.CurrentCulture = CultureInfo.InstalledUICulture; CultureInfo.CurrentUICulture = CultureInfo.InstalledUICulture; } else { // If we woke up but couldn't find a request, we need to update the number of working workers to reflect that we are done working for now RemoveWorkingWorker(); } } ThreadPoolInstance._hillClimbingThreadAdjustmentLock.Acquire(); try { // At this point, the thread's wait timed out. We are shutting down this thread. // We are going to decrement the number of exisiting threads to no longer include this one // and then change the max number of threads in the thread pool to reflect that we don't need as many // as we had. Finally, we are going to tell hill climbing that we changed the max number of threads. ThreadCounts counts = ThreadCounts.VolatileReadCounts(ref ThreadPoolInstance._separated.counts); while (true) { if (counts.numExistingThreads == counts.numProcessingWork) { // In this case, enough work came in that this thread should not time out and should go back to work. break; } ThreadCounts newCounts = counts; newCounts.numExistingThreads--; newCounts.numThreadsGoal = Math.Max(ThreadPoolInstance._minThreads, Math.Min(newCounts.numExistingThreads, newCounts.numThreadsGoal)); ThreadCounts oldCounts = ThreadCounts.CompareExchangeCounts(ref ThreadPoolInstance._separated.counts, newCounts, counts); if (oldCounts == counts) { HillClimbing.ThreadPoolHillClimber.ForceChange(newCounts.numThreadsGoal, HillClimbing.StateOrTransition.ThreadTimedOut); // TODO: Event: Worker Thread stop event return; } counts = oldCounts; } } finally { ThreadPoolInstance._hillClimbingThreadAdjustmentLock.Release(); } } }
internal static bool PerformWaitCallback() { return(ThreadPoolWorkQueue.Dispatch()); }
private static void WorkerThreadStart() { PortableThreadPoolEventSource log = PortableThreadPoolEventSource.Log; if (log.IsEnabled()) { log.WorkerThreadStart(ThreadCounts.VolatileReadCounts(ref ThreadPoolInstance._separated.counts).numExistingThreads); } while (true) { while (WaitForRequest()) { if (TakeActiveRequest()) { Volatile.Write(ref ThreadPoolInstance._separated.lastDequeueTime, Environment.TickCount); if (ThreadPoolWorkQueue.Dispatch()) { // If the queue runs out of work for us, we need to update the number of working workers to reflect that we are done working for now RemoveWorkingWorker(); } } else { // If we woke up but couldn't find a request, we need to update the number of working workers to reflect that we are done working for now RemoveWorkingWorker(); } } ThreadPoolInstance._hillClimbingThreadAdjustmentLock.Acquire(); try { // At this point, the thread's wait timed out. We are shutting down this thread. // We are going to decrement the number of exisiting threads to no longer include this one // and then change the max number of threads in the thread pool to reflect that we don't need as many // as we had. Finally, we are going to tell hill climbing that we changed the max number of threads. ThreadCounts counts = ThreadCounts.VolatileReadCounts(ref ThreadPoolInstance._separated.counts); while (true) { if (counts.numExistingThreads == counts.numProcessingWork) { // In this case, enough work came in that this thread should not time out and should go back to work. break; } ThreadCounts newCounts = counts; newCounts.numExistingThreads--; newCounts.numThreadsGoal = Math.Max(ThreadPoolInstance._minThreads, Math.Min(newCounts.numExistingThreads, newCounts.numThreadsGoal)); ThreadCounts oldCounts = ThreadCounts.CompareExchangeCounts(ref ThreadPoolInstance._separated.counts, newCounts, counts); if (oldCounts == counts) { HillClimbing.ThreadPoolHillClimber.ForceChange(newCounts.numThreadsGoal, HillClimbing.StateOrTransition.ThreadTimedOut); if (log.IsEnabled()) { log.WorkerThreadStop(newCounts.numExistingThreads); } return; } } } finally { ThreadPoolInstance._hillClimbingThreadAdjustmentLock.Release(); } } }
private static void WorkerThreadStart() { Thread.CurrentThread.SetThreadPoolWorkerThreadName(); PortableThreadPool threadPoolInstance = ThreadPoolInstance; if (PortableThreadPoolEventSource.Log.IsEnabled(EventLevel.Informational, PortableThreadPoolEventSource.Keywords.ThreadingKeyword)) { PortableThreadPoolEventSource.Log.ThreadPoolWorkerThreadStart( (uint)threadPoolInstance._separated.counts.VolatileRead().NumExistingThreads); } LowLevelLock hillClimbingThreadAdjustmentLock = threadPoolInstance._hillClimbingThreadAdjustmentLock; LowLevelLifoSemaphore semaphore = s_semaphore; while (true) { bool spinWait = true; while (semaphore.Wait(ThreadPoolThreadTimeoutMs, spinWait)) { bool alreadyRemovedWorkingWorker = false; while (TakeActiveRequest(threadPoolInstance)) { Volatile.Write(ref threadPoolInstance._separated.lastDequeueTime, Environment.TickCount); if (!ThreadPoolWorkQueue.Dispatch()) { // ShouldStopProcessingWorkNow() caused the thread to stop processing work, and it would have // already removed this working worker in the counts. This typically happens when hill climbing // decreases the worker thread count goal. alreadyRemovedWorkingWorker = true; break; } } // Don't spin-wait on the semaphore next time if the thread was actively stopped from processing work, // as it's unlikely that the worker thread count goal would be increased again so soon afterwards that // the semaphore would be released within the spin-wait window spinWait = !alreadyRemovedWorkingWorker; if (!alreadyRemovedWorkingWorker) { // If we woke up but couldn't find a request, or ran out of work items to process, we need to update // the number of working workers to reflect that we are done working for now RemoveWorkingWorker(threadPoolInstance); } } hillClimbingThreadAdjustmentLock.Acquire(); try { // At this point, the thread's wait timed out. We are shutting down this thread. // We are going to decrement the number of exisiting threads to no longer include this one // and then change the max number of threads in the thread pool to reflect that we don't need as many // as we had. Finally, we are going to tell hill climbing that we changed the max number of threads. ThreadCounts counts = threadPoolInstance._separated.counts.VolatileRead(); while (true) { // Since this thread is currently registered as an existing thread, if more work comes in meanwhile, // this thread would be expected to satisfy the new work. Ensure that NumExistingThreads is not // decreased below NumProcessingWork, as that would be indicative of such a case. short numExistingThreads = counts.NumExistingThreads; if (numExistingThreads <= counts.NumProcessingWork) { // In this case, enough work came in that this thread should not time out and should go back to work. break; } ThreadCounts newCounts = counts; newCounts.SubtractNumExistingThreads(1); short newNumExistingThreads = (short)(numExistingThreads - 1); short newNumThreadsGoal = Math.Max(threadPoolInstance._minThreads, Math.Min(newNumExistingThreads, newCounts.NumThreadsGoal)); newCounts.NumThreadsGoal = newNumThreadsGoal; ThreadCounts oldCounts = threadPoolInstance._separated.counts.InterlockedCompareExchange(newCounts, counts); if (oldCounts == counts) { HillClimbing.ThreadPoolHillClimber.ForceChange(newNumThreadsGoal, HillClimbing.StateOrTransition.ThreadTimedOut); if (PortableThreadPoolEventSource.Log.IsEnabled(EventLevel.Informational, PortableThreadPoolEventSource.Keywords.ThreadingKeyword)) { PortableThreadPoolEventSource.Log.ThreadPoolWorkerThreadStop((uint)newNumExistingThreads); } return; } counts = oldCounts; } } finally { hillClimbingThreadAdjustmentLock.Release(); } } }
/// <summary> /// Dispatches work items to this thread. /// </summary> /// <returns> /// <c>true</c> if this thread did as much work as was available or its quantum expired. /// <c>false</c> if this thread stopped working early. /// </returns> internal static bool Dispatch() { ThreadPoolWorkQueue outerWorkQueue = ThreadPoolGlobals.workQueue; // // Save the start time // int startTickCount = Environment.TickCount; // // Update our records to indicate that an outstanding request for a thread has now been fulfilled. // From this point on, we are responsible for requesting another thread if we stop working for any // reason, and we believe there might still be work in the queue. // outerWorkQueue.MarkThreadRequestSatisfied(); Interlocked.Increment(ref outerWorkQueue.numWorkingThreads); // // Assume that we're going to need another thread if this one returns to the VM. We'll set this to // false later, but only if we're absolutely certain that the queue is empty. // bool needAnotherThread = true; object outerWorkItem = null; try { // // Set up our thread-local data // // Use operate on workQueue local to try block so it can be enregistered ThreadPoolWorkQueue workQueue = outerWorkQueue; ThreadPoolWorkQueueThreadLocals tl = workQueue.GetOrCreateThreadLocals(); Thread currentThread = tl.currentThread; // Start on clean ExecutionContext and SynchronizationContext currentThread.ExecutionContext = null; currentThread.SynchronizationContext = null; // // Loop until our quantum expires or there is no work. // while (ThreadPool.KeepDispatching(startTickCount)) { bool missedSteal = false; // Use operate on workItem local to try block so it can be enregistered object workItem = outerWorkItem = workQueue.Dequeue(tl, ref missedSteal); if (workItem == null) { // // No work. // If we missed a steal, though, there may be more work in the queue. // Instead of looping around and trying again, we'll just request another thread. Hopefully the thread // that owns the contended work-stealing queue will pick up its own workitems in the meantime, // which will be more efficient than this thread doing it anyway. // needAnotherThread = missedSteal; // Tell the VM we're returning normally, not because Hill Climbing asked us to return. return(true); } // // If we found work, there may be more work. Ask for another thread so that the other work can be processed // in parallel. Note that this will only ask for a max of #procs threads, so it's safe to call it for every dequeue. // workQueue.EnsureThreadRequested(); if (workItem is Task task) { task.ExecuteFromThreadPool(currentThread); } else { Debug.Assert(workItem is IThreadPoolWorkItem); Unsafe.As <IThreadPoolWorkItem>(workItem).Execute(); } RuntimeThread.CurrentThread.ResetThreadPoolThread(); // Release refs outerWorkItem = workItem = null; // Return to clean ExecutionContext and SynchronizationContext ExecutionContext.ResetThreadPoolThread(currentThread); if (!ThreadPool.NotifyWorkItemComplete()) { return(false); } } // If we get here, it's because our quantum expired. return(true); } catch (Exception e) { // Work items should not allow exceptions to escape. For example, Task catches and stores any exceptions. Environment.FailFast("Unhandled exception in ThreadPool dispatch loop", e); return(true); // Will never actually be executed because Environment.FailFast doesn't return } finally { int numWorkers = Interlocked.Decrement(ref outerWorkQueue.numWorkingThreads); Debug.Assert(numWorkers >= 0); // // If we are exiting for any reason other than that the queue is definitely empty, ask for another // thread to pick up where we left off. // if (needAnotherThread) { outerWorkQueue.EnsureThreadRequested(); } } }