May 26th, 2026
0 reactions

If C# and JavaScript lets me await a Windows Runtime asynchronous operation more than once, why not C++/WinRT?

The Windows Runtime expresses asynchronous execution in the form of types like IAsync­Operation. Different languages choose to represent this concept in different ways.

  • C# wraps it inside a System.Threading.Tasks.Task.
  • JavaScript wraps it inside a Promise.
  • Python wraps it inside an asyncio.Future.

All of the above wrappers support multiple continuations, which means that you can call await on them multiple times.

But C++/WinRT doesn’t provide such a wrapper. C++/WinRT just exposes the original IAsync­Operation. The IAsync­Operation doesn’t allow multiple continuations to be attached, so you cannot co_await it multiple times.

Boo.

Why doesn’t C++/WinRT get with the program and provide a wrapper that supports multiple continuations?

Well, one reason is that C#’s System.Threading.Tasks.Task, JavaScript’s Promise, and Python’s asyncio.Future are part of the respective languages’ standard libraries. This makes them an obvious choice for projection since they are a way to represent asynchronous execution that is universally understood by anybody writing code in that language. Furthermore, the fact that they are standard library features lets the projections build on the work of others: The standard library maintainers have already implemented, tested, and optimized these classes.

C++ does not have a standard library for asynchronous execution. Instead, C++ provides raw materials out of which you can write your own library. And we wrote one such library back when we learned about C++ coroutines and implemented a simple_task class.

The lack of a standard library task type means that there isn’t a pre-existing wrapper that C++ projections could take advantage of. It’s every man for himself.

C++/WinRT provides a minimal coroutine promise for IAsync­Operation in deference to the general C++ principle of “you don’t pay for what you don’t use.” The overwhelming majority of the time, you have no need to await an IAsync­Operation more than once, so why make every IAsync­Operation pay for it?

Next time, we’ll look at a case where you would want to await an IAsync­Operation more than once, and see what we can do to work around the C++/WinRT limitation.

Bonus chatter: There does exist a pre-existing library that supports multiple continuations: The Parallel Patterns Library concurrency::task object. The older C++/CX projection does not natively wrap the IAsyncOperation^, but the Parallel Patterns Library does have special knowledge of the C++/CX IAsyncOperation^, so you can wrap an IAsyncOperation^ inside a PPL task and co_await the task more than once.

So I guess you could say that somebody already wrote the wrapper for you. Unfortunately, that wrapper is quite bulky because the PPL library has so many features, most of which you probably aren’t using. You could say that while C++/WinRT tries to be minimalist, PPL tries to be maximalist. It’s easier to start small and grow and it is to start big and try to pare back. (And C++/WinRT generally delegates the “grow” part to the Windows Implementation Library, which adds additional features with headers like cppwinrt_helpers.h and cppwinrt_authoring.h.)

Topics

Author

Raymond has been involved in the evolution of Windows for more than 30 years. In 2003, he began a Web site known as The Old New Thing which has grown in popularity far beyond his wildest imagination, a development which still gives him the heebie-jeebies. The Web site spawned a book, coincidentally also titled The Old New Thing (Addison Wesley 2007). He occasionally appears on the Windows Dev Docs Twitter account to tell stories which convey no useful information.

0 comments