Search code examples
c#asynchronousasync-awaitvaluetaskivaluetasksource

How to release a ValueTask that wraps a side-effect without using await?


If I have a "resultless" ValueTask, (as in plain ValueTask, not a ValueTask<T>) that has completed synchronously such that valueTask.IsCompletedSuccessfully is true. The ValueTask has not been awaited. How do I tell to the runtime that I'm done with the ValueTask, and that it can be reused if it happens to be backed by a IValueTaskSource?

ValueTask<T> has a Result property and accessing it (presumably) marks the ValueTask<T> as consumed, but a plain ValueTask has no Result.


Solution

  • You can notify the underlying IValueTaskSource object that the ValueTask has been consumed, by calling the .GetAwaiter().GetResult() method on the task:

    if (valueTask.IsCompletedSuccessfully)
    {
        valueTask.GetAwaiter().GetResult();
    }
    

    This call does not cause any allocation. The ValueTaskAwaiter is a struct.

    The effect that the .GetAwaiter().GetResult() has on consuming the ValueTask is not documented. It can only be derived by studying the source code of the relevant types (ValueTask, ValueTaskAwaiter, ManualResetValueTaskSourceCore).

    Caution: Calling the .GetAwaiter().GetResult() before the task is completed, is not a valid operation for a ValueTask. It is also forbidden to call the .GetAwaiter().GetResult() more than once, or in tandem with any other consuming operation like await or AsTask. These restrictions are documented in the Remarks section of the ValueTask struct.