/// <summary> /// Push a new waiter into the waiter's stack /// </summary> /// <param name="newWaiterEntry">A waiter to put in the stack</param> public void PushWaiter(WaiterEntry newWaiterEntry) { // Remove the waiter if it is already in the stack and // update waiter's count as needed RemoveWaiter(newWaiterEntry, false); // If the stack is empty then newWaiterEntry is the new head of the stack if (null == _headWaiterEntry._nextWaiterEntry) { _headWaiterEntry._nextWaiterEntry = newWaiterEntry; newWaiterEntry._prevWaiterEntry = _headWaiterEntry; } // If the stack is not empty then put newWaiterEntry as the new head // of the stack. else { // Save the old first waiter entry WaiterEntry oldFirstWaiterEntry = _headWaiterEntry._nextWaiterEntry; // Update the links _headWaiterEntry._nextWaiterEntry = newWaiterEntry; newWaiterEntry._nextWaiterEntry = oldFirstWaiterEntry; newWaiterEntry._prevWaiterEntry = _headWaiterEntry; oldFirstWaiterEntry._prevWaiterEntry = newWaiterEntry; } // Increment the number of waiters ++_waitersCount; }
public void CloseThreadWaiter() { if (_waiterEntry != null) { _waiterEntry.Close(); _waiterEntry = null; } }
/// <summary> /// Returns the WaiterEntry of the current thread /// </summary> /// <returns></returns> /// In order to avoid creation and destuction of WaiterEntry /// objects each thread has its own WaiterEntry object. private WaiterEntry GetThreadWaiterEntry() { if (null == _waiterEntry) { _waiterEntry = new WaiterEntry(); } _waiterEntry.Reset(); return(_waiterEntry); }
/// <summary> /// Returns the WaiterEntry of the current thread /// </summary> /// <returns></returns> /// In order to avoid creation and destuction of WaiterEntry /// objects each thread has its own WaiterEntry object. private static WaiterEntry GetThreadWaiterEntry() { if (null == CurrentWaiterEntry) { CurrentWaiterEntry = new WaiterEntry(); } CurrentWaiterEntry.Reset(); return(CurrentWaiterEntry); }
/// <summary> /// Returns the WaiterEntry of the current thread /// </summary> /// <returns></returns> /// In order to avoid creation and destuction of WaiterEntry /// objects each thread has its own WaiterEntry object. private static WaiterEntry GetThreadWaiterEntry() { if (_waiterEntry == null) { _waiterEntry = new WaiterEntry(); } else { _waiterEntry.Reset(); } return(_waiterEntry); }
/// <summary> /// Pop a waiter from the waiter's stack /// </summary> /// <returns>Returns the first waiter in the stack</returns> private WaiterEntry PopWaiter() { WaiterEntry waiterEntry = this._headWaiterEntry._nextWaiterEntry; WaiterEntry entry2 = waiterEntry._nextWaiterEntry; this.RemoveWaiter(waiterEntry, true); this._headWaiterEntry._nextWaiterEntry = entry2; if (entry2 != null) { entry2._prevWaiterEntry = this._headWaiterEntry; } return(waiterEntry); }
/// <summary> /// Push a new waiter into the waiter's stack /// </summary> /// <param name="newWaiterEntry">A waiter to put in the stack</param> public void PushWaiter(WaiterEntry newWaiterEntry) { this.RemoveWaiter(newWaiterEntry, false); if (this._headWaiterEntry._nextWaiterEntry == null) { this._headWaiterEntry._nextWaiterEntry = newWaiterEntry; newWaiterEntry._prevWaiterEntry = this._headWaiterEntry; } else { WaiterEntry entry = this._headWaiterEntry._nextWaiterEntry; this._headWaiterEntry._nextWaiterEntry = newWaiterEntry; newWaiterEntry._nextWaiterEntry = entry; newWaiterEntry._prevWaiterEntry = this._headWaiterEntry; entry._prevWaiterEntry = newWaiterEntry; } this._waitersCount++; }
/// <summary> /// Enqueue a work item to the queue. /// </summary> public bool EnqueueWorkItem(WorkItem workItem) { // A work item cannot be null, since null is used in the // WaitForWorkItem() method to indicate timeout or cancel if (null == workItem) { throw new ArgumentNullException("workItem", "workItem cannot be null"); } bool enqueue = true; // First check if there is a waiter waiting for work item. During // the check, timed out waiters are ignored. If there is no // waiter then the work item is queued. lock (this) { ValidateNotDisposed(); if (!_isWorkItemsQueueActive) { return(false); } while (_waitersCount > 0) { // Dequeue a waiter. WaiterEntry waiterEntry = PopWaiter(); // Signal the waiter. On success break the loop if (waiterEntry.Signal(workItem)) { enqueue = false; break; } } if (enqueue) { // Enqueue the work item _workItems.Enqueue(workItem); } } return(true); }
/// <summary> /// Remove a waiter from the stack /// </summary> /// <param name="waiterEntry">A waiter entry to remove</param> /// <param name="popDecrement">If true the waiter count is always decremented</param> private void RemoveWaiter(WaiterEntry waiterEntry, bool popDecrement) { // Store the prev entry in the list WaiterEntry prevWaiterEntry = waiterEntry._prevWaiterEntry; // Store the next entry in the list WaiterEntry nextWaiterEntry = waiterEntry._nextWaiterEntry; // A flag to indicate if we need to decrement the waiters count. // If we got here from PopWaiter then we must decrement. // If we got here from PushWaiter then we decrement only if // the waiter was already in the stack. bool decrementCounter = popDecrement; // Null the waiter's entry links waiterEntry._prevWaiterEntry = null; waiterEntry._nextWaiterEntry = null; // If the waiter entry had a prev link then update it. // It also means that the waiter is already in the list and we // need to decrement the waiters count. if (null != prevWaiterEntry) { prevWaiterEntry._nextWaiterEntry = nextWaiterEntry; decrementCounter = true; } // If the waiter entry had a next link then update it. // It also means that the waiter is already in the list and we // need to decrement the waiters count. if (null != nextWaiterEntry) { nextWaiterEntry._prevWaiterEntry = prevWaiterEntry; decrementCounter = true; } // Decrement the waiters count if needed if (decrementCounter) { --_waitersCount; } }
/// <summary> /// Waits for a work item or exits on timeout or cancel /// </summary> /// <param name="millisecondsTimeout">Timeout in milliseconds</param> /// <param name="cancelEvent">Cancel wait handle</param> /// <returns>Returns true if the resource was granted</returns> public WorkItem DequeueWorkItem(int millisecondsTimeout, WaitHandle cancelEvent) { WaiterEntry newWaiterEntry = null; WorkItem workItem = null; lock (this) { this.ValidateNotDisposed(); if (this._workItems.Count > 0) { return(this._workItems.Dequeue() as WorkItem); } newWaiterEntry = this.GetThreadWaiterEntry(); this.PushWaiter(newWaiterEntry); } WaitHandle[] waitHandles = new WaitHandle[] { newWaiterEntry.WaitHandle, cancelEvent }; int num = WaitHandle.WaitAny(waitHandles, millisecondsTimeout, true); lock (this) { bool flag = 0 == num; if (!flag) { bool flag2 = newWaiterEntry.Timeout(); if (flag2) { this.RemoveWaiter(newWaiterEntry, false); } flag = !flag2; } if (flag) { workItem = newWaiterEntry.WorkItem; if (workItem == null) { workItem = this._workItems.Dequeue() as WorkItem; } } } return(workItem); }
/// <summary> /// Pop a waiter from the waiter's stack /// </summary> /// <returns>Returns the first waiter in the stack</returns> private WaiterEntry PopWaiter() { // Store the current stack head WaiterEntry oldFirstWaiterEntry = _headWaiterEntry._nextWaiterEntry; // Store the new stack head WaiterEntry newHeadWaiterEntry = oldFirstWaiterEntry._nextWaiterEntry; // Update the old stack head list links and decrement the number // waiters. RemoveWaiter(oldFirstWaiterEntry, true); // Update the new stack head _headWaiterEntry._nextWaiterEntry = newHeadWaiterEntry; if (null != newHeadWaiterEntry) { newHeadWaiterEntry._prevWaiterEntry = _headWaiterEntry; } // Return the old stack head return(oldFirstWaiterEntry); }
/// <summary> /// Remove a waiter from the stack /// </summary> /// <param name="waiterEntry">A waiter entry to remove</param> /// <param name="popDecrement">If true the waiter count is always decremented</param> private void RemoveWaiter(WaiterEntry waiterEntry, bool popDecrement) { WaiterEntry entry = waiterEntry._prevWaiterEntry; WaiterEntry entry2 = waiterEntry._nextWaiterEntry; bool flag = popDecrement; waiterEntry._prevWaiterEntry = null; waiterEntry._nextWaiterEntry = null; if (entry != null) { entry._nextWaiterEntry = entry2; flag = true; } if (entry2 != null) { entry2._prevWaiterEntry = entry; flag = true; } if (flag) { this._waitersCount--; } }
/// <summary> /// Cleanup the work items queue, hence no more work /// items are allowed to be queue /// </summary> protected virtual void Cleanup() { lock (this) { // Deactivate only once if (!_isWorkItemsQueueActive) { return; } // Don't queue more work items _isWorkItemsQueueActive = false; foreach (WorkItem workItem in _workItems) { workItem.DisposeOfState(); } // Clear the work items that are already queued _workItems.Clear(); // Note: // I don't iterate over the queue and dispose of work items's states, // since if a work item has a state object that is still in use in the // application then I must not dispose it. // Tell the waiters that they were timed out. // It won't signal them to exit, but to ignore their // next work item. while (_waitersCount > 0) { WaiterEntry waiterEntry = PopWaiter(); waiterEntry.Timeout(); } } }
/// <summary> /// Waits for a work item or exits on timeout or cancel /// </summary> /// <param name="millisecondsTimeout">Timeout in milliseconds</param> /// <param name="cancelEvent">Cancel wait handle</param> /// <returns>Returns true if the resource was granted</returns> public WorkItem DequeueWorkItem( int millisecondsTimeout, WaitHandle cancelEvent) { /// This method cause the caller to wait for a work item. /// If there is at least one waiting work item then the /// method returns immidiately with true. /// /// If there are no waiting work items then the caller /// is queued between other waiters for a work item to arrive. /// /// If a work item didn't come within millisecondsTimeout or /// the user canceled the wait by signaling the cancelEvent /// then the method returns false to indicate that the caller /// didn't get a work item. WaiterEntry waiterEntry = null; WorkItem workItem = null; lock (this) { ValidateNotDisposed(); // If there are waiting work items then take one and return. if (_workItems.Count > 0) { workItem = _workItems.Dequeue() as WorkItem; return(workItem); } // No waiting work items ... else { // Get the wait entry for the waiters queue waiterEntry = GetThreadWaiterEntry(); // Put the waiter with the other waiters PushWaiter(waiterEntry); } } // Prepare array of wait handle for the WaitHandle.WaitAny() WaitHandle [] waitHandles = new WaitHandle [] { waiterEntry.WaitHandle, cancelEvent }; // Wait for an available resource, cancel event, or timeout. // During the wait we are supposes to exit the synchronization // domain. (Placing true as the third argument of the WaitAny()) // It just doesn't work, I don't know why, so I have lock(this) // statments insted of one. int index = WaitHandle.WaitAny( waitHandles, millisecondsTimeout, true); lock (this) { // success is true if it got a work item. bool success = (0 == index); // The timeout variable is used only for readability. // (We treat cancel as timeout) bool timeout = !success; // On timeout update the waiterEntry that it is timed out if (timeout) { // The Timeout() fails if the waiter has already been signaled timeout = waiterEntry.Timeout(); // On timeout remove the waiter from the queue. // Note that the complexity is O(1). if (timeout) { RemoveWaiter(waiterEntry, false); } // Again readability success = !timeout; } // On success return the work item if (success) { workItem = waiterEntry.WorkItem; if (null == workItem) { workItem = _workItems.Dequeue() as WorkItem; } } } // On failure return null. return(workItem); }
/// <summary> /// Returns the WaiterEntry of the current thread /// </summary> /// <returns></returns> /// In order to avoid creation and destuction of WaiterEntry /// objects each thread has its own WaiterEntry object. private WaiterEntry GetThreadWaiterEntry() { if (null == _waiterEntry) { _waiterEntry = new WaiterEntry(); } _waiterEntry.Reset(); return _waiterEntry; }