Manually control task completion with TaskCompletionSource
TaskCompletionSource and TaskCompletionSource<T> are types that allow you to create an unbound Task, which means it’s not tied to a delegate. This gives you manual control over when and how the task completes. You can manually set the task’s result, or signal that the task has completed due to cancellation or an error.
What does it mean?
Well, I had this flaky integration test - flaky is a fashionable term for a test that randomly fails - that checked a semaphore system worked (the process had to only be allowed to start if there wasn't one already in progress). It used a Delay task, which would normally work, until something on the test machine made the code run faster or slower, and then it failed. It ran the process in a thread, then started the process again in another thread and expected it to return false.
So how did I solve it? I've set up a TaskCompletionSource, then I've mocked a service that was used after the semaphore was tested and created to SetResult for the underlying task, I ran the process, then awaited the task.
Something like this:
var source = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously);
_someRepo
.Setup(r=>r.Insert(It.IsAny<Something>()))
.Callback(()=>source.TrySetResult());
Assert.True(target.RunTheMethod())
await source.Task.WaitAsync(TestContext.Current.CancellationToken);
Assert.False(target.RunTheMethod());
// cleanup so that the methods don't run past the end of the test
No Delay needed.
The TaskCompletionSource classes expose these methods:
- SetCancelled and TrySetCancelled
- SetException and TrySetException
- SetFromTask and TrySetFromTask
- SetResult and TrySetResult
The "Try" versions just throw InvalidOperationException if the task is already in a final state (RanToCompletion, Faulted, or Canceled).
Quite simple, but versatile. Imagine that you want to run tasks not all at once or sequentially, but with finer control, like start tasks when a particular phase of another has finished or when capacity is available.
Comments
Be the first to post a comment