I needed a code that:
- when it's called the first time, starts a cancellable task and returns a cancellable awaiter-task for it
- on subsequent calls, returns cancellable awaiter-tasks for the original task
- lets the task keep running while there is someone waiting for its completion (some awaiter-tasks are not cancelled)
- cancels the task when there is no one left waiting for its completion (all awaiter-tasks are cancelled)
I came up with several classes where AwaiterTaskSource
is the centerpiece.
Let's say we are building a web-service with an endpoint that accepts a string and performs a long-running calculation based on this string. And we want this endpoint to behave like this:
time
Start of the operation. ---
First actor calls the endpoint |
with a 1 second timeout, passes "a". |
Calculation for "a" starts, |
first actor waits for its result. |
|
--- 0.5 seconds later.
| Second actor calls the endpoint
| with no timeout, passes "a" too.
| Calculation for "a" is already running,
| second actor waits for its result.
|
1 second into the operation. ---
First actor's request times out, |
cancellation issued to the calculation |
yet the calculation keeps running |
because second actor still waits for it. |
|
--- 1.5 seconds into the operation.
The calculation is finished,
second actor gets the calculation result.
OneTaskManyAwaitersService
allows us to achieve this if we pass the incoming "a" and a task-creating delegate to its RunOrAwait
method.
AwaiterTaskSource
executes a task factory and then issues awaiter-tasks for the task created by the factoryOneTaskManyAwaitersService
creates and accesses instances ofAwaiterTaskSource
by key, promotes their awaiter-tasksOneTaskManyAwaitersServiceTests
contains a slew of tests (and example usages) for the above classes.