Closed oldnewthing closed 11 months ago
Open issues:
resume_agile()
? resume_any_apartment()
? Something else?Name | Free | Member |
---|---|---|
resume_agile |
co_await winrt::resume_agile(Something()); |
co_await Something().resume_agile(); |
resume_any_apartment |
co_await winrt::resume_any_apartment(Something()); |
co_await Something().resume_any_apartment(); |
Note that C# uses await Something().ConfigureAwait(false);
, going for the "member function" column, though the name "ConfigureAwait" is quite bad.
IAsyncXxx
by value or reference? Reference is more efficient, but creates the risk of UAF:
// Don't do this.
auto later = resume_agile(Something());
co_await later;
I think reference is okay because resume_foreground
has the same UAF problem and nobody appears to have been badly bitten by it.
Pros/cons:
resume_agile
to things that don't work.
co_await resume_agile(resume_background())
- oopsMy vote is for free function. Our policy so far has to put these things as free functions so discoverability doesn't seem like a problem here ... resume_foreground, cancellation helpers, etc. If you want this, you know you need it, so you know where to find it. (I'm of two minds about monkeypatching via extension methods for the same reason.)
Can you capture it by move, so someone who says co_await resume_agile(ZorpAsync())
gets the best results?
I like resume_agile()
as the naming.
Can you capture it by move, so someone who says
co_await resume_agile(ZorpAsync())
gets the best results?
I like this idea - it gives us the best of both worlds.
co_await resume_async(ZorpAsync())
avoids extraneous addref/release.IAsyncAction op = ZorpAsync(); co_await resume_async(op);
performs the necessary addref/release - avoids UAF.IAsyncAction op = ZorpAsync(); co_await resume_async(std::move(op));
transfers ownership to resume_await
and avoids UAF.await_adapter is not owning, it holds Async const&
await_adapter is not owning, it holds
Async const&
The difference is that up until now, await_adapter
had been produced only by operator co_await
, so you knew that the await_adapter
was going to be awaited immediately, before the destruction of any temporaries.
This change introduces a way to obtain an await_adapter
without awaiting it, namely by doing
auto oops = resume_agile(ZorpAsync());
co_await oops;
That is a fair assessment, though one could already get an adapter without awaiting by calling the operator manually.
To make this work, await_adapter and all operators would need to be changed to take ownership, not just resume_agile.
Somebody who invokes the operator manually auto oops = operator co_await(ZorpAsync())
knows that they are holding a live wire.
By default,
co_await
'ing a Windows Runtime asynchronous operation resumes in the same COM apartment. We provide a way to override this behavior and resume in any apartment by wrapping the asynchronous operation inresume_agile()
.The guts of the change are in
disconnect_aware_handler
which accepts apreserve_context
template parameter, defaulting totrue
. Iffalse
, then we bypass theresume_apartment()
on completion. Thefalse
value is provided by theresume_agile()
function.To remove the
resume_apartment_context
from thedisconnect_aware_handler
in the case where we don't need it, I use EBO and make it a base class, so that it disappears when an emptyignore_apartment_context
is used.While I was there, I introduced
movable_primitive<T>
which provides RAII-like functionality to scalars like integers and pointers. This allows us to simplifydisconnect_aware_handler
andresume_apartment_context
, which previously had to override all the copy and move operations in order to reset the scalar on move.Also fix the
await_adapter.cpp
tests so they don't leak DispatcherQueues, by explicitly shutting down all the dispatcher queues we created. This required moving all the controllers to top-level so we can shut them down after the tests have completed.Fixes: #1355