So I wanted to write a TaskScheduler
for this, but that interface doesn't provide a mechanism for ensuring that the tasks all have a cancellation token and being able to cancel an already running tasks, so we'll have to make due with a scheduler that doesn't follow a provided TPL model/interface.
When scheduling a first cancels the previous task (which won't do anything if there was no previous task or it has since finished). Next it sets the current task to be a task that isn't started until the previous task ends, and in its body runs the given action. A lock
is needed so that if there are two calls to Schedule
one will immediately cancel the other, and the non-cancelled version will be the one assigned to current
.
public class SingleThreadedKillPredecssorScheduler
{
private Task current = Task.FromResult(true);
private CancellationTokenSource cts = new CancellationTokenSource();
private object key = new object();
public Task Schedule(Action<CancellationToken> action)
{
lock (key)
{
cts.Cancel();
cts = new CancellationTokenSource();
current = current.ContinueWith(t => action(cts.Token), cts.Token);
return current;
}
}
public Task<T> Schedule<T>(Func<CancellationToken, T> function)
{
lock (key)
{
cts.Cancel();
cts = new CancellationTokenSource();
var next = current.ContinueWith(t => function(cts.Token), cts.Token);
current = next;
return next;
}
}
}