Behind the scenes, I mean.
I’m quite familiar with the CancellationToken paradigm, but Awaitable.Cancel() is a black box to me…
I understand that I can catch the cancellation in the calling code like this:
Awaitable myAnim = CoolAnimation();
try
{
await myAnim;
}
catch (OperationCanceledException oce)
{
Debug.Log("You didn't want to watch my animation? 😢", this);
}
But internally, how does CoolAnimation()
get stopped?
With a cancellation token, we can simply do:
private async Awaitable CoolAnimation(CancellationToken ct)
{
while (condition)
{
if (ct.IsCancellationRequested) break;
// or
ct.ThrowIfCancellationRequested();
Awaitable.NextFrameAsync();
}
}
We have control here. Happy
If, then, however, the Awaitable has .Cancel() called on it from the outside, does Unity get to decide when the logic stops? Is there an API for detecting this, outside of a CancellationToken? Is there an equivalent to a finally
block that we could throw in our Awaitable task?
Even more, is myAwaitable.IsCompleted true after myAwaitable.Cancel() is called? Assuming I have a reference, but can’t await (and subsequently wrap in try-catch) the awaitable, how would I know that it’s been cancelled?
From the decompiled code, it looks like it’s simply stopping iteration and throwing the [sometimes-catchable] exception:
//
// Summary:
// Cancel the awaitable. If the awaitable is being awaited, the awaiter will get
// a System.OperationCanceledException.
public void Cancel()
{
AwaitableHandle awaitableHandle = CheckPointerValidity();
if (awaitableHandle.IsManaged)
{
_managedCompletionQueue?.Remove(this);
RaiseManagedCompletion(new OperationCanceledException());
}
else
{
CancelNativeAwaitable(awaitableHandle);
}
}
The docs say “Note: some methods returning an awaitable also accept a CancellationToken. Both cancelation models are equivalent.”
But this seems very far from the truth, as far as my understanding goes.
Can anyone with deeper insight than the docs give me some clarity here? It’s been driving me nuts all day