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()
var waitHandles = new[]
{
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;
}