Should I always complete a TaskCompletionSource?

1.1k views Asked by At

What happens to a TaskCompletionSource and its Task if the TaskCompletionSource is never completed (i.e. SetCancelled, SetException or SetResultis never called? Would the Task live on forever because it never completes?

In the below example I have a simplified version of a parameterized test. In case of a timeout (MyEevent is not called within 1000ms) the TaskCompletionSource (tcs) is never completed. I have a lot of tests like this. Do I need to do any sort of cleanup (e.g. make sure tcs.SetCancelled() is called).?

[Theory]
[InlineData("aa")]
[InlineData(2)]
[InlineData(true)]
[InlineData(null)]
public async Task RaiseMyEvent_RaisesMyEvent_WithOriginalValue(object value)
{
    var sut = new Thing();
    var tcs = new TaskCompletionSource<object>();
    sut.MyEvent += (_, args) => tcs.SetResult(args.Value);

    sut.RaiseMyEvent(value);

    tcs.Task.Should().BeSameAs(await Task.WhenAny(Task.Delay(1000), tcs.Task), "MyEvent should be raised within 1000ms");
    tcs.Task.Result.Should().Be(value);
}

While we are at it, is there any way I could improve the above test (e.g. make it even more concise/simple/readable)?

1

There are 1 answers

0
Timothy Shields On BEST ANSWER

Do I need to do any sort of cleanup (e.g. make sure tcs.SetCancelled() is called)?

Using a TaskCompletionSource<T> doesn't require any cleanup. In fact, it doesn't even admit any cleanup. So the answer to your question is "no."

The TaskCompletionSource<T> is just a conceptually simple data structure that allows you to push in at most a single thing (a result of type T, an exception, or a cancellation). Its Task property exposes a Task<T> that is just a wrapper around this promised single thing that will be pushed into the TaskCompletionSource<T> at some time in the future. It does not use the task pool whatsoever.

To never push anything into a TaskCompletionSource<T> is perfectly valid. This just corresponds to a Task<T> that will "run" forever and never complete.