How to cancel ValueTask<T> - c#

I know how to cancel a Task, but couldn't find any information on how to add cancellation to ValueTask methods.
Normally I would cancel a Task like this:
public async Task<int> Foo(
CancellationToken cancellationToken)
{
TaskCompletionSource<int> tcsCancel =
new TaskCompletionSource<int>();
cancellationToken.Register(() =>
{
tcsCancel.TrySetCanceled();
});
Task<int> task = LongOperation();
var completedTask = await Task.WhenAny(
tcsCancel.Task,
task).ConfigureAwait(false);
return await completedTask.ConfigureAwait(false);
}
Or like this:
if (cancellationToken.IsCancellationRequested)
return Task.FromCanceled<int>(cancellationToken);
The things is, that ValueTask has neither FromCanceled nor WhenAny. Should I just do ...
cancellationToken.ThrowIfCancellationRequested();

The kind of "cancellation" you're referring to isn't a real cancellation; you're cancelling the wait, not the operation.
You can do the same kind of fake-cancellation with ValueTask<T>; you just need to convert it to Task<T> in order to do it:
public async ValueTask<int> Foo(CancellationToken cancellationToken)
{
using var tcsCancel = new TaskCompletionSource<int>();
using var registration = cancellationToken.Register(() =>
{
tcsCancel.TrySetCanceled();
});
ValueTask<int> valueTask = LongOperation();
Task<int> task = valueTask.AsTask();
var completedTask = await Task.WhenAny(
tcsCancel.Task,
task).ConfigureAwait(false);
return await completedTask.ConfigureAwait(false);
}

Related

How to make an async Task continue with next task after first one completed?

How to continue with next task after the dependent one completed? Will Task.ContinueWith() help? If not, how can I effectively handle the below case without clubbing the functions into a single function? Let's say there are 4 async functions:
async Task<string> Func1(string a){}
async Task<string> Func2(string returnedStringFromFunc1 ){}
async Task<string> Func3(string b){}
async Task<string> Func4(string returnedStringFromFunc3){}
Task tsk1 = func1("abc");
Task tsk3 = func3("efg");
await tsk1; // In case tsk3 is completed early, flow is waiting here.
// Instead I could have started tsk4.
Task tsk2 = func2(tsk1.Result);
await tsk3;
Task tsk4 = func4(tsk3.Result);
You could use two separate functions, even local functions, to combine the tasks:
async Task<string> Flow1(string input)
{
var r1=await func1(input);
var r2=await func2(r1);
return r2;
}
async Task<string> Flow2(string input)
{
var r1=await func3(input);
var r2=await func4(r1);
return r2;
}
var task1=Flow1("abc");
var task2=Flow2("efg");
await Task.WhenAll(task1,task2);
Try dropping in these extremely helpful extension methods:
public static class TaskEx
{
public static async Task<R> Select<T, R>(this Task<T> task, Func<T, R> s)
{
var t = await task;
return s(t);
}
public static async Task<R> SelectMany<T, R>(this Task<T> task, Func<T, Task<R>> k) => await k(await task);
public static async Task<R> SelectMany<T, U, R>(this Task<T> task, Func<T, Task<U>> k, Func<T, U, R> s)
{
var t = await task;
var u = await k(t);
return s(t, u);
}
}
Then you can do this:
async Task Main()
{
Task<string> q1 =
from x in Func1("abc")
from y in Func2(x)
select y;
Task<string> q2 =
from x in Func3("efg")
from y in Func4(x)
select y;
string r1 = await q1;
string r2 = await q2;
Console.WriteLine(r1);
Console.WriteLine(r2);
}
Task<string> Func1(string a) => Task.FromResult(a);
Task<string> Func2(string c) => Task.FromResult($"{c}!");
Task<string> Func3(string b) => Task.FromResult(b);
Task<string> Func4(string d) => Task.FromResult($"{d}!");
I get the following:
abc!
efg!
You can use Task.WhenAny and depending on which task completed start the next one and then wait for the last one to complete.
Task tsk1 = func1("abc");
Task tsk3 = func3("efg");
Task tsk2 = null, tsk4 = null;
var completed = await Task.WhenAny(new [] {tsk1, tsk3});
if (completed == tsk1)
{
tsk2 = func2(tsk1.Result);
await tsk3;
tsk4 = func4(tsk3.Result);
}
else
{
tsk4 = func4(tsk3.Result);
await tsk1;
tsk2 = func2(tsk1.Result);
}
Here is a more conventional way of using Enigmativity's quite handy SelectMany extension method for tasks:
Task<string> combo12 = Func1("abc").SelectMany(async x => await Func2(x));
Task<string> combo34 = Func3("efg").SelectMany(async x => await Func4(x));
await Task.WhenAll(combo12, combo34); // Await the completion of both workflows
string result12 = await combo12;
string result34 = await combo34;
The SelectMany extension method:
public static async Task<TResult> SelectMany<TSource, TResult>(
this Task<TSource> task, Func<TSource, Task<TResult>> function)
{
return await function(await task);
}
The code could be simplified by eliding async/await in the asynchronous delegate, like this:
Task<string> combo12 = Func1("abc").SelectMany(x => Func2(x));
Task<string> combo34 = Func3("efg").SelectMany(x => Func4(x));
...or even like this:
Task<string> combo12 = Func1("abc").SelectMany(Func2);
Task<string> combo34 = Func3("efg").SelectMany(Func4);
You could do the same thing without the SelectMany operator, but it becomes rather awkward:
Task<string> combo12 = ((Func<Task<string>>)(
async () => await Func2(await Func1("abc"))))();
Task<string> combo34 = ((Func<Task<string>>)(
async () => await Func4(await Func3("efg"))))();
If you want to learn more about the theory behind the SelectMany operator, you may find this article interesting: Tasks, Monads, and LINQ by Stephen Toub

Number of times trigger has executed

I'm trying to save the number of times a trigger has fired into a database table but I can't find anything in the quartz-scheduler.net regarding this. Is there any method I can invoke to achieve this?
With simple trigger, there's a .GetTimesTriggered() method. Is there any equivalent for the cron triggers?
You can simply implement ISchedulerListener.
I have implemented it like following for some logging on trigger execution
public class QuartzSchedulerListener : ISchedulerListener//todo R & D to catch schdeuler error/invalid shutdown event and do possible implementation there
{
private static readonly log4net.ILog Logger = log4net.LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);
public Task JobScheduled(ITrigger trigger, CancellationToken cancellationToken = default(CancellationToken))
{
var t = Task.Factory.StartNew
(() =>
{
Logger.Debug("Quartz Scheduler JobScheduled");
});
return t;
}
public Task JobUnscheduled(TriggerKey triggerKey, CancellationToken cancellationToken = default(CancellationToken))
{
var t = Task.Factory.StartNew
(() =>
{
Logger.Debug("Quartz Scheduler JobUnscheduled");
});
return t;
}
public Task TriggerFinalized(ITrigger trigger, CancellationToken cancellationToken = default(CancellationToken))
{
var t = Task.Factory.StartNew
(() =>
{
Logger.Debug("Quartz Scheduler TriggerFinalized");
});
return t;
}
public Task TriggerPaused(TriggerKey triggerKey, CancellationToken cancellationToken = default(CancellationToken))
{
var t = Task.Factory.StartNew
(() =>
{
Logger.Debug("Quartz Scheduler TriggerPaused");
});
return t;
}
public Task TriggersPaused(string triggerGroup, CancellationToken cancellationToken = default(CancellationToken))
{
var t = Task.Factory.StartNew
(() =>
{
Logger.DebugFormat("Quartz Scheduler TriggersPaused {0}", triggerGroup);
});
return t;
}
public Task TriggerResumed(TriggerKey triggerKey, CancellationToken cancellationToken = default(CancellationToken))
{
var t = Task.Factory.StartNew
(() =>
{
Logger.Debug("Quartz Scheduler TriggersPaused");
});
return t;
}
public Task TriggersResumed(string triggerGroup, CancellationToken cancellationToken = default(CancellationToken))
{
var t = Task.Factory.StartNew
(() =>
{
Logger.DebugFormat("Quartz Scheduler TriggersResumed {0}", triggerGroup);
});
return t;
}
public Task JobAdded(IJobDetail jobDetail, CancellationToken cancellationToken = default(CancellationToken))
{
var t = Task.Factory.StartNew
(() =>
{
Logger.Debug("Quartz Scheduler JobAdded");
});
return t;
}
public Task JobDeleted(JobKey jobKey, CancellationToken cancellationToken = default(CancellationToken))
{
var t = Task.Factory.StartNew
(() =>
{
Logger.Debug("Quartz Scheduler JobDeleted");
});
return t;
}
public Task JobPaused(JobKey jobKey, CancellationToken cancellationToken = default(CancellationToken))
{
var t = Task.Factory.StartNew
(() =>
{
Logger.Debug("Quartz Scheduler JobPaused");
});
return t;
}
public Task JobInterrupted(JobKey jobKey, CancellationToken cancellationToken = default(CancellationToken))
{
var t = Task.Factory.StartNew
(() =>
{
Logger.Debug("Quartz Scheduler JobInterrupted");
});
return t;
}
public Task JobsPaused(string jobGroup, CancellationToken cancellationToken = default(CancellationToken))
{
var t = Task.Factory.StartNew
(() =>
{
Logger.DebugFormat("Quartz Scheduler JobsPaused {0}", jobGroup);
});
return t;
}
public Task JobResumed(JobKey jobKey, CancellationToken cancellationToken = default(CancellationToken))
{
var t = Task.Factory.StartNew
(() =>
{
Logger.Debug("Quartz Scheduler JobResumed");
});
return t;
}
public Task JobsResumed(string jobGroup, CancellationToken cancellationToken = default(CancellationToken))
{
var t = Task.Factory.StartNew
(() =>
{
Logger.DebugFormat("Quartz Scheduler JobsResumed {0}",jobGroup);
});
return t;
}
public Task SchedulerError(string msg, SchedulerException cause, CancellationToken cancellationToken = default(CancellationToken))
{
var t = Task.Factory.StartNew
(() =>
{
Logger.DebugFormat("Quartz Scheduler is Error State message:{0}", msg);
if (cause != null)
{
Logger.Error(cause);
}
});
return t;
}
public Task SchedulerInStandbyMode(CancellationToken cancellationToken = default(CancellationToken))
{
var t = Task.Factory.StartNew
(() =>
{
Logger.Debug("Quartz Scheduler is in Stand By Mode");
});
return t;
}
public Task SchedulerStarted(CancellationToken cancellationToken = default(CancellationToken))
{
var t = Task.Factory.StartNew
(() =>
{
Logger.Debug("Quartz Scheduler is Started");
});
return t;
}
public Task SchedulerStarting(CancellationToken cancellationToken = default(CancellationToken))
{
var t = Task.Factory.StartNew
(() =>
{
Logger.Debug("Quartz Scheduler is Starting");
});
return t;
}
public Task SchedulerShutdown(CancellationToken cancellationToken = default(CancellationToken))
{
var t = Task.Factory.StartNew
(() =>
{
Logger.Debug("Quartz Scheduler is Shut Down");
});
return t;
}
public Task SchedulerShuttingdown(CancellationToken cancellationToken = default(CancellationToken))
{
var t = Task.Factory.StartNew
(() =>
{
Logger.Debug("Quartz Scheduler is Shutting Down");
});
return t;
}
public Task SchedulingDataCleared(CancellationToken cancellationToken = default(CancellationToken))
{
var t = Task.Factory.StartNew
(() =>
{
Logger.Debug("Quartz Scheduler SchedulingDataCleared");
});
return t;
}
}
StdSchedulerFactory factory = new StdSchedulerFactory();
var MyQuartzScheduler = await factory.GetScheduler();
QuartzSchedulerListener olistener = new QuartzSchedulerListener();
MyQuartzScheduler.ListenerManager.AddSchedulerListener(olistener);
await MyQuartzScheduler.Start();

Correct use of CancellationToken

This is my situation:
private CancellationTokenSource cancellationTokenSource;
private CancellationToken cancellationToken;
public IoTHub()
{
cancellationTokenSource = new CancellationTokenSource();
cancellationToken = cancellationTokenSource.Token;
receive();
}
private void receive()
{
eventHubClient = EventHubClient.CreateFromConnectionString(connectionString, iotHubD2cEndpoint);
var d2cPartitions = eventHubClient.GetRuntimeInformation().PartitionIds;
foreach (string partition in d2cPartitions)
{
ReceiveMessagesFromDeviceAsync(partition, cancellationToken);
}
}
private async Task ReceiveMessagesFromDeviceAsync(CancellationToken ct)
{
var eventHubReceiver = eventHubClient.GetDefaultConsumerGroup().CreateReceiver(partition, DateTime.UtcNow);
while (true)
{
if(ct.IsCancellationRequested)
{
break;
}
EventData eventData = await eventHubReceiver.ReceiveAsync();
if (eventData == null) continue;
string data = Encoding.UTF8.GetString(eventData.GetBytes());
// Javascript function with Websocket
Clients.All.setMessage(data);
}
}
public void cancelToken()
{
cancellationTokenSource.Cancel();
}
The Task will not be cancelled, when calling the cancelToken method. How come?
I have read the Microsoft guide, an other Stackoverflow questions about Task cancellation.
But still have difficulty using them correctly.
You can consider CancellationToken like a flag, indicating if a cancellation signal is received. Thus:
while (true)
{
//you check the "flag" here, to see if the operation is cancelled, correct usage
if(ct.IsCancellationRequested)
{
break;
}
//your instance of CancellationToken (ct) can't stop this task from running
await LongRunningTask();
}
If you want LongRunningTask to be cancelled, you should use CancellationToken inside the task body and check it when necessary, like this:
async Task LongRunningTask()
{
await DoPrepareWorkAsync();
if (ct.IsCancellationRequested)
{
//it's cancelled!
return;
}
//let's do it
await DoItAsync();
if (ct.IsCancellationRequested)
{
//oh, it's cancelled after we already did something!
//fortunately we have rollback function
await RollbackAsync();
}
}

Equivalent of ContinueWith(delegate, CancellationToken) with await continuation

I have that situation:
private Task LongRunningTask = /* Something */;
private void DoSomethingMore(Task previousTask) { }
public Task IndependentlyCancelableSuccessorTask(CancellationToken cancellationToken)
{
return LongRunningTask.ContinueWith(DoSomethingMore, cancellationToken);
}
In particular, the behavior that interests me here is detailed in MSDN's page about Continuation Tasks in the following terms:
A continuation goes into the Canceled state in these scenarios:
[...]
When the continuation was passed a System.Threading.CancellationToken as an argument and the IsCancellationRequested property of the token is true before the continuation runs. In such a case, the continuation does not start and it transitions to the Canceled state.
The code above works. However, I am in the process of converting as many as possible of my continuations to using the await keyword.
Is there an equivalent using await that would allow the continuation to be canceled before the awaited task completes?
The following should do it, albeit it looks a bit awkward:
private Task LongRunningTask = /* Something */;
private void DoSomethingMore() { }
public async Task IndependentlyCancelableSuccessorTask(
CancellationToken cancellationToken)
{
cancellationToken.ThrowIfCancellationRequested();
var tcs = new TaskCompletionSource<bool>();
using (cancellationToken.Register(() => tcs.TrySetCanceled()))
await Task.WhenAny(LongRunningTask, tcs.Task);
cancellationToken.ThrowIfCancellationRequested();
DoSomethingMore();
}
[UPDATE] Following svick's suggestion, here it is shaped as a helper, based on Stephen Toub's Implementing Then with Await pattern:
public static class TaskExt
{
/// <summary>
/// Use: await LongRunningTask.Then(DoSomethingMore, cancellationToken)
/// </summary>
public static async Task Then(
this Task antecedent, Action continuation, CancellationToken token)
{
await antecedent.When(token);
continuation();
}
/// <summary>
/// Use: await LongRunningTask.When(cancellationToken)
/// </summary>
public static async Task When(
this Task antecedent, CancellationToken token)
{
token.ThrowIfCancellationRequested();
var tcs = new TaskCompletionSource<Empty>();
using (token.Register(() => tcs.TrySetCanceled()))
await Task.WhenAny(antecedent, tcs.Task);
token.ThrowIfCancellationRequested();
}
struct Empty { };
}
Perhaps, the first ThrowIfCancellationRequested() is redundant, but I haven't thoroughly considered all edge cases.
While this answer is conceptually the same as Noseratio's, I am not satisfied by a few details of the implementation, and as such am publishing my proposed implementation of the helper so that it can be commented on by other people on this question.
public static async Task<TResult> WhenNotCanceled<TResult>(this Task<TResult> mainTask, CancellationToken cancellationToken)
{
if (!cancellationToken.CanBeCanceled) {
return await mainTask.ConfigureAwait(false);
}
cancellationToken.ThrowIfCancellationRequested();
Task<TResult> completedTask;
var cancellationTaskSource = new TaskCompletionSource<TResult>();
using (cancellationToken.Register(() => cancellationTaskSource.TrySetCanceled(), useSynchronizationContext: false)
completedTask = await Task.WhenAny(mainTask, cancellationTaskSource.Task).ConfigureAwait(false);
cancellationToken.ThrowIfCancellationRequested();
return await completedTask.ConfigureAwait(false);
}
public static async Task WhenNotCanceled(this Task mainTask, CancellationToken cancellationToken)
{
if (!cancellationToken.CanBeCanceled) {
await mainTask.ConfigureAwait(false);
return;
}
cancellationToken.ThrowIfCancellationRequested();
Task completedTask;
var cancellationTaskSource = new TaskCompletionSource<object>();
using (cancellationToken.Register(() => cancellationTaskSource.TrySetCanceled(), useSynchronizationContext: false)
completedTask = await Task.WhenAny(mainTask, cancellationTaskSource.Task).ConfigureAwait(false);
cancellationToken.ThrowIfCancellationRequested();
await completedTask.ConfigureAwait(false);
}
Async pattern without cancel:
public async Task IndependentlyCancelableSuccessorTask()
{
await LongRunningTask;
DoSomethingMore();
}
Async pattern with cancel and WhenNotCanceled:
public async Task IndependentlyCancelableSuccessorTask(CancellationToken cancellationToken)
{
await LongRunningTask.WhenNotCanceled(cancellationToken);
DoSomethingMore();
}
My answer is only slightly different than #Jean Hominal's answer and incorporates #Noseratio's approach as well:
public static class TaskExtensionMethods
{
public static Task<TResult> OrWhenCancelled<TResult>(this Task<TResult> mainTask, CancellationToken cancellationToken)
{
if (!cancellationToken.CanBeCanceled)
return mainTask;
return OrWhenCancelled_(mainTask, cancellationToken);
}
private static async Task<TResult> OrWhenCancelled_<TResult>(this Task<TResult> mainTask, CancellationToken cancellationToken)
{
Task cancellationTask = Task.Delay(Timeout.Infinite, cancellationToken);
await Task.WhenAny(mainTask, cancellationTask).ConfigureAwait(false);
cancellationToken.ThrowIfCancellationRequested();
return await mainTask;
}
public static Task OrWhenCancelled(this Task mainTask, CancellationToken cancellationToken)
{
if (!cancellationToken.CanBeCanceled)
return mainTask;
return OrWhenCancelled_(mainTask, cancellationToken);
}
private static async Task OrWhenCancelled_(this Task mainTask, CancellationToken cancellationToken)
{
Task cancellationTask = Task.Delay(Timeout.Infinite, cancellationToken);
await Task.WhenAny(mainTask, cancellationTask).ConfigureAwait(false);
cancellationToken.ThrowIfCancellationRequested();
await mainTask;
}
}
Discussion:
All of the solutions (including this one), do not correctly handle the case where the original ContinueWith specified a TaskScheduler. Specifically, consider a TaskScheduler created TaskScheduler.FromCurrentSynchronizationContext for usage in UI scenarios. In that case, with the original ContinueWith approach you were guaranteed that the cancellation token was checked prior to running the delegate but after already getting on to Main thread (see this answer). That is, the old approach has the nice effect of checking the Cancellation token "one last time" on the main thread prior to considering the result of the task (i.e. trumping whether the main task finished or faulted). This means that in addition to using these extension methods, the new code must wrap its await in a try/finally to do its final check of the CancellationToken :(. See this question.
#Noseratio's solution could handle the above issue (if needed), but it has the downside of requiring that continuation be placed into a delegate. In my opinion, this defeats one of the big advantages of converting to using await: the code doesn't end up in a delegate, it is just after an await and reads like normal sequential code.
Notes:
I wish I could have specified that the empty lambda never runs (i.e. instead of only running on cancellation), but the .ContinueWith method doesn't allow that. So, I (mostly arbitrarily chose OnlyOnCancelled)
This answer comes from #Servy from this answer (with modifications):
public static Task WithCancellation(this Task task,
CancellationToken token)
{
return task.ContinueWith(t => t.GetAwaiter().GetResult(), token, TaskContinuationOptions.ExecuteSynchronously, TaskScheduler.Default);
}
public static Task<T> WithCancellation<T>(this Task<T> task,
CancellationToken token)
{
return task.ContinueWith(t => t.GetAwaiter().GetResult(), token, TaskContinuationOptions.ExecuteSynchronously, TaskScheduler.Default);
}

How to get asynchronousy without new threads or async and await keywords but only Task

I wonder how to accomplish the same thing the below program does without using extra threads or await and async keywords but only Tasks. A sample code would be awesome. It seems to me that we need to use TaskCompletionSource and Async versions of the IO-bound operations or any long-running operations.
static void Main(string[] args)
{
Task t = Go();
Console.WriteLine("Hello World");
Task.Delay(1000).GetAwaiter().OnCompleted(() => { Console.WriteLine("Completed"); });
Console.ReadLine();
}
static async Task Go()
{
var task = PrintAnswerToLife();
await task;
Console.WriteLine("Done");
}
static async Task PrintAnswerToLife()
{
var task = GetAnswerToLife();
int answer = await task;
Console.WriteLine(answer);
}
static async Task<int> GetAnswerToLife()
{
var task = Task.Delay(2000);
await task;
int answer = 21 * 2;
return answer;
}
You can do a pretty straightforward translation of async / await into Task by using ContinueWith. Other translations are also possible, e.g., Task.Delay becomes System.Threading.Timer.
The basic pattern is, for any async method that does an await:
static async Task Go()
{
var task = PrintAnswerToLife();
await task;
Console.WriteLine("Done");
}
becomes:
static Task Go()
{
var tcs = new TaskCompletionSource<object>();
var task = PrintAnswerToLife();
task.ContinueWith(_ =>
{
Console.WriteLine("Done");
tcs.SetResult(null);
});
return tcs.Task;
}
Correct error handling is a lot more work.

Categories