Meet C++/WinRT 2.0: Async Timeouts Made Easy

C++/WinRT took a big bet on C++ coroutines and that bet has paid off. Coroutines are in C++20 and the effect on writing concurrency code in C++ has been transformational. C++/WinRT was also the primary driver for the adoption of coroutines within Windows. Still, there are times when the fact that some API call is async is completely irrelevant and all you want is the result here and now. For that reason, C++/WinRT’s implementation of the various WinRT async interfaces has always sported a get function, similar to what std::function provides:

int main()
{
    IAsyncAction async = ...
    async.get();
    puts("done!");
}

This get function will block indefinitely for the async object to complete. Async objects tend to be very short-lived so this is often all you need. There are however times when this really doesn’t cut it and you need to abandon the wait after some time has elapsed. Writing this has always been possible, thanks to the building blocks provided by WinRT, but it has never been easy. Well C++/WinRT now makes it trivial by providing a wait_for function, again similar to what std::function provides:

int main()
{
    IAsyncAction async = ...

    if (async.wait_for(5s) == AsyncStatus::Completed)
    {
        puts("done");
    }
}

The wait_for in this example (using std::literals) will wait around 5 seconds before checking completion. If the comparison is favorable then you know that the async object completed successfully and you’re done. If you are waiting for some result, then you can simply follow that with a call to the get function to retrieve the result:

int main()
{
    IAsyncOperation<int> async = ...

    if (async.wait_for(5s) == AsyncStatus::Completed)
    {
        printf("result %d\n", async.get());
    }
}

Since the async object has already completed, the get function will return the result immediately without any further wait. As you can see, the wait_for function returns the state of the async object. You can thus use this for more fine-grained control:

switch (async.wait_for(5s))
{
case AsyncStatus::Completed:
    printf("result %d\n", async.get());
    break;
case AsyncStatus::Canceled:
    puts("canceled");
    break;
case AsyncStatus::Error:
    puts("failed");
    break;
case AsyncStatus::Started:
    puts("still running");
    break;
}

As I mentioned, AsyncStatus::Completed means the async object completed successfully and you may call the get function for any result.

AsyncStatus::Canceled means the async object was canceled. Note that the cancellation is typically requested by the caller, so it would be rare to handle this state. Typically, a cancelled async object is simply discarded.

AsyncStatus::Error means the async object has failed in some way. You may call the get function to rethrow the exception if so desired.

Finally, AsyncStatus::Started means that the async object is still running. This is where it gets tricky. The WinRT async pattern does not allow multiple waits or waiters. That means that you cannot call wait_for in a loop. If the wait has effectively timed-out, you are left with a few choices. You may abandon the object or you may poll its status before calling get to retrieve any result, but it’s best just to discard the object at this point.

And that’s all for today. I hope you enjoy using C++/WinRT!

4 thoughts on “Meet C++/WinRT 2.0: Async Timeouts Made Easy

  1. Tim Weis

    This is a great addition. Iff it works the way I assume it does. While I wasn’t able to find any documentation on wait_for online I have to ask: Does wait_for always wait for the timeout to expire, or does it return early, in case the asynchronous operation completes prior to the timeout? I’m hoping for the latter, but simply don’t know.

    Reply
      1. Tim Weis

        I just gave this a spin, reworking my existing Bluetooth networking code. Result: The code was cut in half, turned readable and maintainable, and now has robust error handling and reporting. The binary size also decreased after ditching the dependency on the Concurrency Runtime. It’s already become a feature that I can hardly imagine ever having to do without. Thank you!

        As always, questions…
        * What’s the minimum C++/WinRT version requirement for wait_for?
        * What happens to the asynchronous task when it times out?
        * More generally, how is this implemented?

        I’m sorry to keep nagging you with questions. As it stands, though, this blog is the only resource I know of that offers this sort of information on the C++/WinRT projection. Hope you don’t mind all that much.

      2. Kenny Kerr Post author

        > What’s the minimum C++/WinRT version requirement for wait_for?

        The PR’s date is April 23 so any build later than 2.0.190423.0 will do.

        > What happens to the asynchronous task when it times out?

        The async object is unaware that the caller has decided to timeout. The caller may choose to cancel the async object.

        > More generally, how is this implemented?

        Have a look at the PR.

Leave a Reply to Kenny Kerr Cancel 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 )

Connecting to %s