microsoft / cppwinrt

C++/WinRT
MIT License
1.64k stars 236 forks source link

Allow resume_agile to be stored in a variable #1358

Closed oldnewthing closed 11 months ago

oldnewthing commented 11 months ago

resume_agile exposes the ability to save the await_adapter in a variable. This was not possible without resume_agile because the await_adapter had previously been available only via operator co_await, which means that it is created only in response to an immediate attempt to co_await it, so we knew that it would be consumed before its argument (possibly a temporary) was destructed.

resume_agile returns the await_adapter, and we expect people to await it immediately, but it's possible that they decide to save it in a variable and await it later. In that case, we have to record the Async as a value instead of a reference. We forward the resume_agile argument into the Async so that it moves if given an rvalue reference, or copies if given an lvalue reference. This ensure that the common case where somebody does co_await resume_agile(DoSomething()), we do not incur any additional AddRefs or Releases.

Now that it's possible to co_await the await_adapter twice, we have to worry about await_suspend being called twice. It had previously assumed that suspending was true (since that's how it was constructed), but that is no longer valid in the resume_agile case if somebody tries to await the resume_agile twice. So we have to force it to true. (Now, the second await will fail with "illegal delegate assignment", but our failure to set suspending to true led to double-resumption, which is super-bad.)

Fixes #1357