private void RemoveThread(string reason, DispatchThread thread, Result <DispatchWorkItem> result) { if (thread == null) { throw new ArgumentNullException("thread"); } if (result == null) { throw new ArgumentNullException("result"); } if (thread.PendingWorkItemCount != 0) { throw new ArgumentException(string.Format("thread #{1} still has work-items in queue (items: {0})", thread.PendingWorkItemCount, thread.Id), "thread"); } // remove thread from list of allocated threads lock (_syncRoot) { _threadVelocity = 0; UnregisterThread(reason, thread); } // check if we can put thread into the reserved list if (_reservedThreads.Count < MinReservedThreads) { if (!_reservedThreads.TryPush(new KeyValuePair <DispatchThread, Result <DispatchWorkItem> >(thread, result))) { throw new NotSupportedException("TryPush failed"); } } else { // return thread to resource manager DispatchThreadScheduler.ReleaseThread(thread, result); } }
/// <summary> /// Shutdown the ElasticThreadPool instance. This method blocks until all pending items have finished processing. /// </summary> public void Dispose() { if (!_disposed) { _disposed = true; _log.DebugFormat("Dispose @{0}", this); // TODO (steveb): make dispose more reliable // 1) we can't wait indefinitively! // 2) we should progressively sleep longer and longer to avoid unnecessary overhead // 3) this pattern feels useful enough to be captured into a helper method // wait until all threads have been decommissioned while (ThreadCount > 0) { AsyncUtil.Sleep(100.Milliseconds()); } // discard all reserved threads KeyValuePair <DispatchThread, Result <DispatchWorkItem> > reserved; while (_reservedThreads.TryPop(out reserved)) { DispatchThreadScheduler.ReleaseThread(reserved.Key, reserved.Value); } DispatchThreadScheduler.UnregisterHost(this); } }
// (yurig): This method has been intentionally split from DispatchLoop() // result.Block() inside causes Result<DispatchWorkItem> to never be disposed. // By moving it into its own method we ensure its garbage collection as it is // popped off the stack. Do NOT inline this method into DispatchLoop(). private bool GetNextWorkItem(out Action callback) { if (!_inbox.TryPop(out callback)) { var result = new Result <DispatchWorkItem>(TimeSpan.MaxValue); // reset the dispatch queue for this thread AsyncUtil.CurrentDispatchQueue = null; _queue = null; // check if thread is associated with a host already if (_host == null) { // NOTE (steveb): this is a brand new thread without a host yet // return the thread to the dispatch scheduler DispatchThreadScheduler.ReleaseThread(this, result); } else { // request another work-item _host.RequestWorkItem(this, result); } // block until a work item is available result.Block(); // check if we received a work item or an exception to shutdown if (result.HasException && (result.Exception is DispatchThreadShutdownException)) { // time to shut down _log.DebugFormat("DispatchThread #{0} destroyed", _id); return(false); } callback = result.Value.WorkItem; _queue = result.Value.DispatchQueue; // TODO (steveb): handle the weird case where _queue is null // set the dispatch queue for this thread AsyncUtil.CurrentDispatchQueue = _queue; } return(true); }
private void DispatchLoop() { // set thread-local self-reference CurrentThread = this; // begin thread loop try { while (true) { // check if queue has a work-item Action callback; if (!_inbox.TryPop(out callback)) { var result = new Result <DispatchWorkItem>(TimeSpan.MaxValue); // reset the dispatch queue for this thread AsyncUtil.CurrentDispatchQueue = null; // check if thread is associated with a host already if (_host == null) { // NOTE (steveb): this is a brand new thread without a host yet // return the thread to the dispatch scheduler DispatchThreadScheduler.ReleaseThread(this, result); } else { // request another work-item _host.RequestWorkItem(this, result); } // block until a work item is available result.Block(); // check if we received a work item or an exception to shutdown if (result.HasException && (result.Exception is DispatchThreadShutdownException)) { // time to shut down _log.DebugFormat("DispatchThread #{0} destroyed", _id); return; } callback = result.Value.WorkItem; _queue = result.Value.DispatchQueue; // TODO (steveb): handle the weird case where _queue is null // set the dispatch queue for this thread AsyncUtil.CurrentDispatchQueue = _queue; } // execute work-item if (callback != null) { try { callback(); } catch (Exception e) { _log.Warn("an unhandled exception occurred while executing the work-item", e); } } } } catch (Exception e) { // something went wrong that shouldn't have! _log.ErrorExceptionMethodCall(e, string.Format("DispatchLoop #{0}: FATAL ERROR", _id)); // TODO (steveb): tell _host about untimely exit; post items in queue to host inbox (o/w we lose them) } finally { CurrentThread = null; } }