Welp, I've this code:
public static async Task TimedSync (CancellationToken ct)
{
try {
if (ct.IsCancellationRequested)
ct.ThrowIfCancellationRequested();
await Task.Run(async () => await UP.Sincronizacao.SyncDB(true));
Xamarin.Forms.Device.StartTimer(TimeSpan.FromMinutes(1), () => {
if (ct.IsCancellationRequested)
ct.ThrowIfCancellationRequested();
Task.Run(async () => await UP.Sincronizacao.SyncDB(false));
return true;
});
} catch (OperationCanceledException) {
await Current.MainPage.DisplayAlert("Got it", "Good", "ok");
} catch (Exception e) {
await Current.MainPage.DisplayAlert("Oops", e.Message, "dismiss");
}
}
The app just crashes at this point, and on debug I find that the exception thrown by ThrowIfCancellationRequested()
is unhandled.
Edit:
Ok, something really weird happened, I removed the first if(ct.IsCancellationRequested) ct.ThrowIfCancellationRequested();
and followed Peter's suggestion, the Throw inside the lambda now throws the exception, the try catch block I put on it as well didn't work, but the try catch outside the lambda caught the exception. Here's the code:
public static async Task TimedSync (CancellationToken ct)
{
try {
await Task.Run(async () => await UP.Sincronizacao.SyncDB(true));
Xamarin.Forms.Device.StartTimer(TimeSpan.FromMinutes(1), () => {
try {
if (ct.IsCancellationRequested)
ct.ThrowIfCancellationRequested();
Task.Run(async () => await UP.Sincronizacao.SyncDB(false));
return true;
} catch (OperationCanceledException) {
return false;
}
});
} catch (OperationCanceledException) {
await Current.MainPage.DisplayAlert("Got it", "Good", "ok");
} catch (Exception e) {
await Current.MainPage.DisplayAlert("Oops", e.Message, "dismiss");
}
}
It kinda works for me :) But still would like to understand what's going on here
You are passing StartTimer
a lambda that will throw a CancellationException
when cancellation happens, but this exception doesn't necessarily fire inside StartTimer
or the scope of TimedSync
.
My guess, because I don't use Xamarin, is that the timer code running your lambda sees the exception on a separate task and promotes that to an application failure.
If you catch CancellationException
in the lambda and return false this should have the desired effect of stopping the timer without propagating an exception to the Xamarin timer code.
Note that the direct call to ct.ThrowIfCancellationRequested()
will be caught inside TimedSync
and hit your catch block.