/** * Try to cancel an async put request. */ private void PutCancellationHandler(object _putNode, bool canceling) { LinkedListNode <AsyncPut> putNode = (LinkedListNode <AsyncPut>)_putNode; AsyncPut put = null; lock (theLock) { if (!putNode.Value.done) { asyncPuts.Remove(putNode); put = putNode.Value; put.done = true; } } if (put != null) { // Dispose resources associated with this async put request. put.Dispose(canceling); // Complete the underlying task properly. if (canceling) { put.SetCanceled(); } else { put.SetResult(false); } } }
/** * Asynchronous TAP interface */ /** * Take a message from the queue asynchronously enabling, optionally, * timeout and/or cancellation. */ public Task <T> TakeAsync(int timeout = Timeout.Infinite, CancellationToken cToken = default(CancellationToken)) { Task <T> takeTask = null; AsyncPut put = null; lock (theLock) { if (putIdx - takeIdx > 0) { // Immediate take takeTask = Task.FromResult <T>(messageRoom[takeIdx++ & mask]); // Try to satisfy a pending async put request if (asyncPuts.Count > 0) { put = asyncPuts.First.Value; asyncPuts.RemoveFirst(); messageRoom[putIdx++ & mask] = put.sentMessage; put.done = true; } /** * If the queue is in the COMPLETING state, the message queue is empty and * we released the last pending put, then transition the queue to the * COMPLETED state. */ if (putIdx == takeIdx && state == COMPLETING && asyncPuts.Count == 0) { state = COMPLETED; } } else { // If the queue was already completed or an immediate take was spedified // return failure. if (state != OPERATING) { throw new InvalidOperationException(); } if (timeout == 0) { return(nullTask); } // If a cancellation was requested return a task in the Canceled state if (cToken.IsCancellationRequested) { return(Task.FromCanceled <T>(cToken)); } // Create a waiter node and insert it in the wait queue AsyncTake take = new AsyncTake(cToken); LinkedListNode <AsyncTake> takeNode = asyncTakes.AddLast(take); /** * Activate the specified cancellers owning the lock. * Since the timeout handler acquires the lock before use the "take.timer" and * "take.cTokenRegistration" the assignements will be visible. */ if (timeout != Timeout.Infinite) { take.timer = new Timer(takeTimeoutHandler, takeNode, timeout, Timeout.Infinite); } /** * If the cancellation token is already in the cancelled state, the cancellation * will run immediately and synchronously, which causes no damage because the * implicit locks can be acquired recursively and this is a terminal processing. */ if (cToken.CanBeCanceled) { take.cTokenRegistration = cToken.Register(takeCancellationHandler, takeNode); } // Set the result task that represents the asynchronous operation takeTask = take.Task; } } // If we released any putter, cancel its cancellers and complete its task. if (put != null) { put.Dispose(); put.SetResult(true); } return(takeTask); }