Пример #1
0
        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();
                    }
                }
            }
Пример #3
0
 private static void DispatchCallback(IntPtr instance, IntPtr context, IntPtr work)
 {
     RuntimeThread.InitializeThreadPoolThread();
     Debug.Assert(s_work == work);
     ThreadPoolWorkQueue.Dispatch();
 }
Пример #4
0
 internal static bool PerformWaitCallback() => ThreadPoolWorkQueue.Dispatch();
Пример #5
0
        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);
        }
Пример #6
0
        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);
        }
Пример #7
0
 private static void Dispatch()
 {
     ThreadPoolWorkQueue.Dispatch();
 }
Пример #8
0
 private static void Callback()
 {
     _callbackQueued = false;
     ThreadPoolWorkQueue.Dispatch();
 }
Пример #9
0
 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);
        }
Пример #11
0
 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();
                    }
                }
            }
Пример #13
0
 internal static bool PerformWaitCallback()
 {
     return(ThreadPoolWorkQueue.Dispatch());
 }
Пример #14
0
            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();
                    }
                }
            }
Пример #16
0
        /// <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();
                }
            }
        }