Not much has been written on ManualResetValueTaskSourceCore<TResult>
. It's essentially a TaskCompletionSource<T>
but for ValueTask<T>
.
It's really intended to be used inside a type like your timer, but you can create a wrapper for it if you want to use it as an external method:
public sealed class ManualResetValueTaskSource<T> : IValueTaskSource<T>, IValueTaskSource
{
private ManualResetValueTaskSourceCore<T> _logic; // mutable struct; do not make this readonly
public bool RunContinuationsAsynchronously
{
get => _logic.RunContinuationsAsynchronously;
set => _logic.RunContinuationsAsynchronously = value;
}
public void Reset() => _logic.Reset();
public void SetResult(T result) => _logic.SetResult(result);
public void SetException(Exception error) => _logic.SetException(error);
short IValueTaskSource.Version => _logic.Version;
short IValueTaskSource<T>.Version => _logic.Version;
void IValueTaskSource.GetResult(short token) => _logic.GetResult(token);
T IValueTaskSource<T>.GetResult(short token) => _logic.GetResult(token);
ValueTaskSourceStatus IValueTaskSource.GetStatus(short token) => _logic.GetStatus(token);
ValueTaskSourceStatus IValueTaskSource<T>.GetStatus(short token) => _logic.GetStatus(token);
void IValueTaskSource.OnCompleted(Action<object?> continuation, object? state, short token, ValueTaskSourceOnCompletedFlags flags) => _logic.OnCompleted(continuation, state, token, flags);
void IValueTaskSource<T>.OnCompleted(Action<object?> continuation, object? state, short token, ValueTaskSourceOnCompletedFlags flags) => _logic.OnCompleted(continuation, state, token, flags);
}
You can then use it as such (cancellation removed for simplicity):
ValueTask Delay(TimeSpan duration)
{
ITimer timer = timerFactory.Create();
var vts = new ManualResetValueTaskSource<object>();
TimerElapsed eventHandler = null;
eventHandler = (_, __) =>
{
timer.Elapsed -= eventHandler;
vts.SetResult(null);
};
timer.Elapsed += eventHandler;
timer.Start(duration);
return new ValueTask(vts);
}
Note that, since this is an external method, you'd still be allocating the ManualResetValueTaskSource<T>
(as well as the delegate). So this doesn't really buy you anything, and I would choose to use TaskCompletionSource<T>
here. This is especially true when you add CancellationToken
support and have to handle the race condition between the timer firing (vts.SetResult
) and the cancellation (vts.SetException
), since I'm pretty sure only one of those is supposed to happen per ValueTask operation, and there's no Try*
variants in our ManualResetValueTaskSource<T>
like TaskCompletionSource<T>
has.
ValueTask
is really intended to be more of a first-class citizen; i.e., ideally you would update the actual timer implementation (and interface) to support ValueTask Delay(TimeSpan)
in its own class instead of an external method. Inside the timer instance, it can reuse the ValueTask
backing type (ManualResetValueTaskSource<T>
), it can know when to call Reset
, and it can avoid the delegate/event completely.