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 Async.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 Async.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; } }
// (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; }