private void AddThread(KeyValuePair <DispatchThread, Result <DispatchWorkItem> > keyvalue) { DispatchThread thread = keyvalue.Key; Result <DispatchWorkItem> result = keyvalue.Value; if (_threadVelocity >= 0) { lock (_syncRoot) { _threadVelocity = 0; // check if an item is available for dispatch Action callback; if (TryRequestItem(null, out callback)) { RegisterThread("new thread", thread); // dispatch work-item result.Return(new DispatchWorkItem(callback, this)); return; } } } // we have no need for this thread RemoveThread("insufficient work for new thread", thread, result); }
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); } }
private bool TryRequestItem(DispatchThread thread, out Action callback) { // check if we can find a work-item in the shared queue if (_inbox.TryDequeue(out callback)) { return(true); } // try to steal a work-item from another thread; take a snapshot of all allocated threads (needed in case the array is copied for resizing) DispatchThread[] threads = _activeThreads; foreach (DispatchThread entry in threads) { // check if we can steal a work-item from this thread if ((entry != null) && !ReferenceEquals(entry, thread) && entry.TryStealWorkItem(out callback)) { return(true); } } // check again if we can find a work-item in the shared queue since trying to steal may have overlapped with the arrival of a new item if (_inbox.TryDequeue(out callback)) { return(true); } return(false); }
private void DispatchLoop() { // set thread-local self-reference CurrentThread = this; // begin thread loop try { Action callback; while (GetNextWorkItem(out callback)) { // 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; } }
//--- Class Methods --- public static bool TryQueueWorkItem(IDispatchQueue queue, Action callback) { DispatchThread current = CurrentThread; if ((current != null) && ReferenceEquals(AsyncUtil.CurrentDispatchQueue, queue)) { // NOTE (steveb): next call can never fail since we're calling the queue work-item method of the current thread current.QueueWorkItem(callback); return(true); } return(false); }
public void EvictWorkItems() { // clear CurrentThread to avoid having the evicted items being immediately added back again to the thread (i.e. infinite loop) CurrentThread = null; try { Action item; // remove up to 100 items from the current threads work queue for (int i = 0; (i < 100) && _inbox.TryPop(out item); ++i) { _queue.QueueWorkItem(item); } } finally { // restore CurrentThread before we exit CurrentThread = this; } }
private void UnregisterThread(string reason, DispatchThread thread) { thread.Host = null; // find thread and remove it for (int i = 0; i < _activeThreads.Length; ++i) { if (ReferenceEquals(_activeThreads[i], thread)) { --_threadCount; _activeThreads[i] = null; #if EXTRA_DEBUG _log.DebugFormat("RemoveThread: {1} - {0}", this, reason); #endif break; } } }
private bool TryQueueWorkItem(int priority, Action callback) { if (_disposed) { throw new ObjectDisposedException("ElasticThreadPool has already been disposed"); } // check if we can enqueue work-item into current dispatch thread IDispatchQueue queue = this[priority]; if (DispatchThread.TryQueueWorkItem(queue, callback)) { return(true); } // check if there are available threads to which the work-item can be given to KeyValuePair <DispatchThread, Result <DispatchWorkItem> > entry; if (_reservedThreads.TryPop(out entry)) { lock (_syncRoot) { RegisterThread("new item", entry.Key); } // found an available thread, let's resume it with the work-item entry.Value.Return(new DispatchWorkItem(callback, queue)); return(true); } // no threads available, keep work-item for later if (!_inbox.TryEnqueue(priority, callback)) { return(false); } // check if we need to request a thread to kick things off if (ThreadCount == 0) { ((IDispatchHost)this).IncreaseThreadCount("request first thread"); } return(true); }
public static void ReleaseThread(DispatchThread thread, Result <DispatchWorkItem> result) { if (thread == null) { throw new ArgumentNullException("thread"); } if (result == null) { throw new ArgumentNullException("result"); } // unbind thread from current host thread.Host = null; // add thread to list of idle threads if (!_threadReserve.TryEnqueue(new KeyValuePair <DispatchThread, Result <DispatchWorkItem> >(thread, result))) { throw new NotSupportedException("TryEnqueue failed"); } }
void IDispatchHost.RequestWorkItem(DispatchThread thread, Result <DispatchWorkItem> result) { if (thread == null) { throw new ArgumentNullException("thread"); } if (thread.PendingWorkItemCount > 0) { throw new ArgumentException(string.Format("thread #{1} still has work-items in queue (items: {0})", thread.PendingWorkItemCount, thread.Id), "thread"); } if (!ReferenceEquals(thread.Host, this)) { throw new InvalidOperationException(string.Format("thread is allocated to another queue: received {0}, expected: {1}", thread.Host, this)); } if (result == null) { throw new ArgumentNullException("result"); } // check if we need to decommission threads without causing starvation if (_threadVelocity < 0) { RemoveThread("system saturation", thread, result); return; } // check if we found a work-item int priority; Action callback; if (TryRequestItem(thread, out priority, out callback)) { // dispatch work-item result.Return(new DispatchWorkItem(callback, this[priority])); } else { // relinquich thread; it's not required anymore RemoveThread("insufficient work", thread, result); } }
private void RegisterThread(string reason, DispatchThread thread) { ++_threadCount; thread.Host = this; // find an empty slot in the array of all threads int index; for (index = 0; index < _activeThreads.Length; ++index) { // check if we found an empty slot if (_activeThreads[index] == null) { // assign it to the found slot and stop iterating _activeThreads[index] = thread; break; } } // check if we need to grow the array if (index == _activeThreads.Length) { // make room to add a new thread by doubling the array size and copying over the existing entries DispatchThread[] newArray = new DispatchThread[2 * _activeThreads.Length]; Array.Copy(_activeThreads, newArray, _activeThreads.Length); // assign new thread newArray[index] = thread; // update instance field _activeThreads = newArray; } #if EXTRA_DEBUG _log.DebugFormat("AddThread: {1} - {0}", this, reason); #endif }
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; } }
void IDispatchHost.RequestWorkItem(DispatchThread thread, Result<DispatchWorkItem> result) { if(thread == null) { throw new ArgumentNullException("thread"); } if(thread.PendingWorkItemCount > 0) { throw new ArgumentException(string.Format("thread #{1} still has work-items in queue (items: {0})", thread.PendingWorkItemCount, thread.Id), "thread"); } if(!ReferenceEquals(thread.Host, this)) { throw new InvalidOperationException(string.Format("thread is allocated to another queue: received {0}, expected: {1}", thread.Host, this)); } if(result == null) { throw new ArgumentNullException("result"); } Action callback; // check if we need to decommission threads without causing starvation if(_threadVelocity < 0) { RemoveThread("system saturation", thread, result); return; } // check if we found a work-item if(TryRequestItem(thread, out callback)) { // dispatch work-item result.Return(new DispatchWorkItem(callback, this)); } else { // relinquich thread; it's not required anymore RemoveThread("insufficient work", thread, result); } }
private void DispatchLoop() { // set thread-local self-reference CurrentThread = this; // begin thread loop try { Action callback; while(GetNextWorkItem(out callback)) { // 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; } }
public static void ReleaseThread(DispatchThread thread, Result<DispatchWorkItem> result) { if(thread == null) { throw new ArgumentNullException("thread"); } if(result == null) { throw new ArgumentNullException("result"); } // unbind thread from current host thread.Host = null; // add thread to list of idle threads if(!_threadReserve.TryEnqueue(new KeyValuePair<DispatchThread, Result<DispatchWorkItem>>(thread, result))) { throw new NotSupportedException("TryEnqueue failed"); } }
private void RegisterThread(string reason, DispatchThread thread) { ++_threadCount; thread.Host = this; // find an empty slot in the array of all threads int index; for(index = 0; index < _activeThreads.Length; ++index) { // check if we found an empty slot if(_activeThreads[index] == null) { // assign it to the found slot and stop iterating _activeThreads[index] = thread; break; } } // check if we need to grow the array if(index == _activeThreads.Length) { // make room to add a new thread by doubling the array size and copying over the existing entries DispatchThread[] newArray = new DispatchThread[2 * _activeThreads.Length]; Array.Copy(_activeThreads, newArray, _activeThreads.Length); // assign new thread newArray[index] = thread; // update instance field _activeThreads = newArray; } #if EXTRA_DEBUG _log.DebugFormat("AddThread: {1} - {0}", this, reason); #endif }
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; } }
public void EvictWorkItems() { // clear CurrentThread to avoid having the evicted items being immediately added back again to the thread (i.e. infinite loop) CurrentThread = null; try { Action item; // remove up to 100 items from the current threads work queue for(int i = 0; (i < 100) && _inbox.TryPop(out item); ++i) { _queue.QueueWorkItem(item); } } finally { // restore CurrentThread before we exit CurrentThread = this; } }
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); } }
private bool TryRequestItem(DispatchThread thread, out Action callback) { // check if we can find a work-item in the shared queue if(_inbox.TryDequeue(out callback)) { return true; } // try to steal a work-item from another thread; take a snapshot of all allocated threads (needed in case the array is copied for resizing) DispatchThread[] threads = _activeThreads; foreach(DispatchThread entry in threads) { // check if we can steal a work-item from this thread if((entry != null) && !ReferenceEquals(entry, thread) && entry.TryStealWorkItem(out callback)) { return true; } } // check again if we can find a work-item in the shared queue since trying to steal may have overlapped with the arrival of a new item if(_inbox.TryDequeue(out callback)) { return true; } return false; }
private void UnregisterThread(string reason, DispatchThread thread) { thread.Host = null; // find thread and remove it for(int i = 0; i < _activeThreads.Length; ++i) { if(ReferenceEquals(_activeThreads[i], thread)) { --_threadCount; _activeThreads[i] = null; #if EXTRA_DEBUG _log.DebugFormat("RemoveThread: {1} - {0}", this, reason); #endif break; } } }