Show / Hide Table of Contents

ValueTask Completion Source

TaskCompletionSource<T> from .NET standard library represents the producer side of a Task<T>. Once the source is completed, it cannot be reused. It happens because of nature of the Task type: task instance can be awaited multiple times. This is not true for value task because it must be awaited once. See this article for detailed explanation.

The fact that value task must be consumed only once is actively used by AsyncValueTaskMethodBuilder when constructing state machine for async methods in C#. The actual implementation of IValueTaskSource can be reused between multiple instances of ValueTask. When the task is completed, the builder waits for consumption of the task result. When the result is acquired, the source can be reset and placed to the pool for future use. However, .NET standard library doesn't offer this behavior as a public API.

.NEXT Threading library provides the producer side of ValueTask and ValueTask<T> suitable for pooling and reuse: ValueTaskCompletionSource and ValueTaskCompletionSource<T> respectively. In contrast to TaskCompletionSource type from .NET, these types can reused for multiple value tasks. As a result, you can place them to the pool and reduce memory allocation associated with tasks in async context. Additionally, these types support timeout and cancellation tracking.

Completion source can be in the following states:

  • Ready for use means that the source can be used to obtain a fresh incompleted value task
  • Completed means that the producer turns the source into completed state (completed successfully or with exception, canceled, timed out)
  • Consumed means that the value task is awaited by consumer side. The source can be reused only after this event

Completion source offers the following extension points:

  • AfterConsumed virtual method that is called automatically when the task is awaited by the consumer. You can override it to return completion source back to the pool.
  • OnTimeout virtual method that is called when the task is timed out. The method allows to override the result to be passed to the task consumer. By default, it turns the task into failed state with TimeoutException exception.
  • OnCanceled virtual method that is called when the task is canceled. The method allows to override the result to be passed to the task consumer. By default, it turns the task into failed state with OperationCanceledException exception.

CreateTask(TimeSpan timeout, CancellationToken token) method allows to obtain the task from the source. The method must be called only if previously produced task has been awaited and Reset method has been called. The produced task can be completed using TrySetCanceled, TrySetException or TrySetResult methods.

The common usage pattern for this kind of completion source:

  1. Create or obtain an instance of completion source from the pool
  2. Call CreateTask method (timeout and cancellation token are optional)
  3. Return the task to consumer
  4. Complete the task with TrySetCanceled, TrySetException or TrySetResult methods
  5. Override AfterConsumed method and call Reset method on completion source, then return completion source back to the pool
  • Edit this page
☀
☾
In this article
Back to top
Supported by the .NET Foundation
☀
☾