Skip to content

AsyncConditionVariable

StephenCleary edited this page Sep 4, 2014 · 2 revisions

Overview

This is an async-ready condition variable, a classical synchronization primitive in computer science without a direct .NET equivalent.

An AsyncConditionVariable is associated with a single AsyncLock. All methods on the AsyncConditionVariable type require that you hold the associated lock before calling them. There's no runtime checks for these preconditions because there is no way to check them; you'll just have to be careful. I recommend you combine the AsyncLock with its associated AsyncConditionVariable instances into a higher-level custom lock/signal system. Note that the simple case of a single AsyncLock with a single AsyncConditionVariable is equivalent to AsyncMonitor.

Waiting on an AsyncConditionVariable enables an async method to (asynchronously) wait for some condition to become true. While waiting, that async method gives up the AsyncLock. When the condition becomes true, another async method notifies the AsyncConditionVariable, which re-acquires the AsyncLock and releases the waiter.

When notifying the AsyncConditionVariable, the notifying task may choose to release only a single waiter (Notify) or all waiters (NotifyAll). If there are no waiters, the notification is "lost"; it is not remembered by the AsyncConditionVariable.

The task returned from WaitAsync will enter the Completed state when it receives a notification and re-acquires the AsyncLock. That same task will enter the Canceled state if the CancellationToken is signaled before the wait is satisfied; in that case, the task will wait to enter the Canceled state until it re-acquires the AsyncLock.

Remember that from the time WaitAsync is called to the time when its returned task completes, the AsyncLock is not held by the calling task.

Note that the correct logic for condition variables is to wait in a loop until the required condition is true. This is necessary because other tasks may execute between the notification and the completion of the wait.

API

// An async-compatible condition variable.
public sealed class AsyncConditionVariable
{
  // Creates an async-compatible condition variable associated with an async-compatible lock.
  public AsyncConditionVariable(AsyncLock asyncLock);

  // Gets a semi-unique identifier for this asynchronous condition variable.
  public int Id { get; }

  // Sends a signal to a single task waiting on this condition variable.
  // The associated lock MUST be held when calling this method, and it will still be held when this method returns.
  public void Notify();

  // Sends a signal to all tasks waiting on this condition variable.
  // The associated lock MUST be held when calling this method, and it will still be held when this method returns.
  public void NotifyAll();

  // Asynchronously waits for a signal on this condition variable.
  // The associated lock MUST be held when calling this method, and it will still be held when this method returns, even if the method is cancelled.
  public Task WaitAsync(CancellationToken cancellationToken = new CancellationToken());
}

Platform Support

The full API is supported on all platforms.