New in C++/WinRT: Async Cancellation Callback for Coroutines

Today I’d like to share another new feature in build 17709 of the Windows SDK that happens to be the most frequently requested feature for C++/WinRT’s coroutine support and that is the addition of a cancellation callback. WinRT has a very specific async pattern that provides cancellation support, but that support does not automatically flow to other async objects, as it does with some other async frameworks and libraries. The cancellation callback gives you an efficient mechanism to make this work and integrate or emulate these other async models.

Previously, cancellation within a coroutine was supported in two ways. The first is explicit:

IAsyncAction OneAsync()
{
    auto token = co_await get_cancellation_token();

    while (!token())
    {
        printf("Do some work for 1 second\n");
        co_await 1s;
    }
}

Waiting on the get_cancellation_token function returns a cancellation token with knowledge of the IAsyncAction that the coroutine is producing on your behalf. You can use a function call to query the cancellation state, essentially polling for cancellation. This makes sense if you are performing some compute-bound operation.

The second option is implicit:

IAsyncAction TwoAsync()
{
    while (true)
    {
        printf("Do some work for 1 second\n");
        co_await 1s;
    }
}

Given a co_await expression, the coroutine checks whether it has been cancelled prior to suspension and will short-circuit out of there rather than suspending. This works great, but if suspension occurs prior to cancellation then the coroutine will not actually come to an end until the nested co_await expression completes and the outer coroutine subsequently returns or hits a subsequent co_await expression.

These options also made it rather difficult to integrate with existing concurrency libraries as there was no preemptive hook by which cancellation might be propagated. No longer! There is now a third option that allows you to register a cancellation callback. Imagine you have a nested coroutine that does the actual work. Let’s use TwoAsync above for that. You can now write another coroutine that wraps it up and forwards cancellation preemptively as follows:

IAsyncAction ThirdAsync()
{
    auto token = co_await get_cancellation_token();
    auto nested = TwoAsync();

    token.callback([&]
    {
        nested.Cancel();
    });

    co_await nested;
}

Notice that the coroutine registers a lambda as the callback and then simply suspends and waits for the nested action to complete. There’s no need to poll for cancellation and the cancellation isn’t blocked indefinitely. Yay! Naturally, you can use this to interop with other coroutine or concurrency libraries that know nothing of C++/WinRT.

And that’s all for today. Stay tuned for more about the next major update to C++/WinRT and do give it a try today.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google+ photo

You are commenting using your Google+ account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

w

Connecting to %s