/// <summary> /// Waits for any of the work items in the specified array to complete, cancel, or timeout /// </summary> /// <param name="waitableResults">Array of work item result objects</param> /// <param name="millisecondsTimeout">The number of milliseconds to wait, or Timeout.Infinite (-1) to wait indefinitely.</param> /// <param name="exitContext"> /// true to exit the synchronization domain for the context before the wait (if in a synchronized context), and reacquire it; otherwise, false. /// </param> /// <param name="cancelWaitHandle">A cancel wait handle to interrupt the wait if needed</param> /// <returns> /// The array index of the work item result that satisfied the wait, or WaitTimeout if no work item result satisfied the wait and a time interval equivalent to millisecondsTimeout has passed or the work item has been canceled. /// </returns> internal static int WaitAny( IWaitableResult[] waitableResults, int millisecondsTimeout, bool exitContext, WaitHandle cancelWaitHandle) { WaitHandle[] waitHandles; if (null != cancelWaitHandle) { waitHandles = new WaitHandle[waitableResults.Length + 1]; GetWaitHandles(waitableResults, waitHandles); waitHandles[waitableResults.Length] = cancelWaitHandle; } else { waitHandles = new WaitHandle[waitableResults.Length]; GetWaitHandles(waitableResults, waitHandles); } int result = STPEventWaitHandle.WaitAny(waitHandles, millisecondsTimeout, exitContext); // Treat cancel as timeout if (null != cancelWaitHandle) { if (result == waitableResults.Length) { result = STPEventWaitHandle.WaitTimeout; } } ReleaseWaitHandles(waitableResults); return(result); }
/// <summary> /// Get the result of the work item. /// If the work item didn't run yet then the caller waits for the result, timeout, or cancel. /// In case of error the e argument is filled with the exception /// </summary> /// <returns>The result of the work item</returns> private object GetResult( int millisecondsTimeout, bool exitContext, WaitHandle cancelWaitHandle, out Exception e) { e = null; // Check for cancel if (WorkItemState.Canceled == GetWorkItemState()) { throw new WorkItemCancelException("Work item canceled"); } // Check for completion if (IsCompleted) { e = _exception; return(_result); } // If no cancelWaitHandle is provided if (null == cancelWaitHandle) { WaitHandle wh = GetWaitHandle(); bool timeout = !STPEventWaitHandle.WaitOne(wh, millisecondsTimeout, exitContext); ReleaseWaitHandle(); if (timeout) { throw new WorkItemTimeoutException("Work item timeout"); } } else { WaitHandle wh = GetWaitHandle(); int result = STPEventWaitHandle.WaitAny(new WaitHandle[] { wh, cancelWaitHandle }); ReleaseWaitHandle(); switch (result) { case 0: // The work item signaled // Note that the signal could be also as a result of canceling the // work item (not the get result) break; case 1: case STPEventWaitHandle.WaitTimeout: throw new WorkItemTimeoutException("Work item timeout"); default: Debug.Assert(false); break; } } // Check for cancel if (WorkItemState.Canceled == GetWorkItemState()) { throw new WorkItemCancelException("Work item canceled"); } Debug.Assert(IsCompleted); e = _exception; // Return the result return(_result); }
/// <summary> /// Wait for all work items to complete /// </summary> /// <param name="waitableResults">Array of work item result objects</param> /// <param name="millisecondsTimeout">The number of milliseconds to wait, or Timeout.Infinite (-1) to wait indefinitely.</param> /// <param name="exitContext"> /// true to exit the synchronization domain for the context before the wait (if in a synchronized context), and reacquire it; otherwise, false. /// </param> /// <param name="cancelWaitHandle">A cancel wait handle to interrupt the wait if needed</param> /// <returns> /// true when every work item in waitableResults has completed; otherwise false. /// </returns> internal static bool WaitAll( IWaitableResult[] waitableResults, int millisecondsTimeout, bool exitContext, WaitHandle cancelWaitHandle) { if (0 == waitableResults.Length) { return(true); } bool success; WaitHandle[] waitHandles = new WaitHandle[waitableResults.Length]; GetWaitHandles(waitableResults, waitHandles); if ((null == cancelWaitHandle) && (waitHandles.Length <= 64)) { success = STPEventWaitHandle.WaitAll(waitHandles, millisecondsTimeout, exitContext); } else { success = true; int millisecondsLeft = millisecondsTimeout; Stopwatch stopwatch = Stopwatch.StartNew(); WaitHandle[] whs; if (null != cancelWaitHandle) { whs = new WaitHandle[] { null, cancelWaitHandle }; } else { whs = new WaitHandle[] { null }; } bool waitInfinitely = (Timeout.Infinite == millisecondsTimeout); // Iterate over the wait handles and wait for each one to complete. // We cannot use WaitHandle.WaitAll directly, because the cancelWaitHandle // won't affect it. // Each iteration we update the time left for the timeout. for (int i = 0; i < waitableResults.Length; ++i) { // WaitAny don't work with negative numbers if (!waitInfinitely && (millisecondsLeft < 0)) { success = false; break; } whs[0] = waitHandles[i]; int result = STPEventWaitHandle.WaitAny(whs, millisecondsLeft, exitContext); if ((result > 0) || (STPEventWaitHandle.WaitTimeout == result)) { success = false; break; } if (!waitInfinitely) { // Update the time left to wait millisecondsLeft = millisecondsTimeout - (int)stopwatch.ElapsedMilliseconds; } } } // Release the wait handles ReleaseWaitHandles(waitableResults); return(success); }
/// <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 it. // // 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 null to indicate that the caller // didn't get a work item. WaiterEntry waiterEntry; lock (this) { ValidateNotDisposed(); // If there are waiting work items then take one and return. if (_workItems.Count > 0) { return(_workItems.Dequeue()); } // No waiting work items ... // Get the waiter 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 two lock(this) // statments instead of one. int index = STPEventWaitHandle.WaitAny(waitHandles, millisecondsTimeout, true); lock (this) { // On timeout update the waiterEntry that it is timed out if (index != 0) { // The Timeout() fails if the waiter has already been signaled // On timeout remove the waiter from the queue. // Note that the complexity is O(1). if (waiterEntry.Timeout()) { RemoveWaiter(waiterEntry, false); return(null); } } // On success return the work item WorkItem workItem = waiterEntry.WorkItem; if (workItem == null) { workItem = _workItems.Dequeue(); } return(workItem); } }