| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
Sorry, something went wrong.
cancellable_promise exposes both a setter (originate_on_cancel) and a getter (should_originate_on_cancel), but both consumption sites called the setter. Because its parameter defaults to true, each guard evaluation performed std::exchange(m_originate_on_cancel, true): it returned the previous value, so the first check behaved correctly, and then wrote the flag back to true. Every later cancellation on that same promise originated again. A test that cancels only once passes even with the bug present. Three awaiter resume paths also threw hresult_canceled unconditionally, so originate_on_cancel(false) had no effect on them at all: impl::check_status_canceled (reached from await_adapter::await_resume for any coroutine awaiting a WinRT async that completes Canceled), timespan_awaiter::await_resume (resume_after) and signal_awaiter::await_resume (resume_on_signal). cancellable_awaiter now captures the promise's preference in await_suspend, where the promise is known to be alive, and consults it on resume. Capturing at suspend time rather than reaching for the promise at resume time keeps await_adapter::await_resume const. check_status_canceled takes an originate parameter defaulting to true, so wait_get and any external callers keep originating exactly as before. Adds async_originate_count_on_cancel, covering zero, single and repeated cancellation checks plus each of the three awaiter paths. It counts winrt_throw_hresult_handler invocations as a proxy for RoOriginateLanguageException, since observing the latter requires an out-of-process debugger. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 5ecf487c-b73b-4c7c-940b-98920af404a5
…routine The awaiter captures its preference from the awaiting coroutine's promise, so an async that opts out only suppresses the origination sites it reaches directly. Whoever awaits it makes its own decision. fire_and_forget's promise_type does not derive from cancellable_promise, so a fire_and_forget awaiting an opted-out async still originates from await_adapter::await_resume. It cannot state a preference either, having no await_transform for get_cancellation_token. This is a common shape: a fire_and_forget starter that stores and awaits an annotated IAsyncAction. Giving the awaiting coroutine a cancellable promise is necessary but not sufficient. An IAsyncAction parent that does not itself opt out still originates, because the default is to originate. These cases document current behaviour rather than assert a desired end state. Routing a child's preference to its awaiter needs a way to carry it across the await, which is a larger change than the flag fix. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 5ecf487c-b73b-4c7c-940b-98920af404a5
There was a problem hiding this comment.
Fixes cancellation origination behavior in C++/WinRT coroutines so cancellation_token::originate_on_cancel(false) reliably suppresses RoOriginate... across repeated cancellation checks and across additional awaiter paths (await_adapter, resume_after, resume_on_signal), including for IAsyncAction scenarios called out in #1617.
Changes:
Copilot reviewed 4 out of 4 changed files in this pull request and generated 1 comment.
| File | Description |
|---|---|
| test/test/test.vcxproj | Adds the new cancellation-origination regression test to the test project build. |
| test/test/async_originate_count_on_cancel.cpp | New test coverage for origination suppression across repeated cancellation checks and multiple awaiter resume paths. |
| strings/base_coroutine_threadpool.h | Captures per-promise origination preference at suspend time and applies it in timespan_awaiter / signal_awaiter cancellation throws. |
| strings/base_coroutine_foundation.h | Threads an originate choice into check_status_canceled and uses should_originate_on_cancel() in cancellation sites instead of the setter. |
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
Sorry, something went wrong.
| auto async = make(resume.get(), originate); | ||
| async.Completed([&](auto&&, AsyncStatus status) | ||
| { | ||
| REQUIRE(status == expected); | ||
| SetEvent(completed.get()); | ||
| }); | ||
|
|
||
| if (cancel) | ||
| { | ||
| async.Cancel(); | ||
| } |
| Back | FazBrowse Home | New Git URL |
Bug: #1617
IAsyncAction DoWork(HANDLE ready) { auto cancel = co_await get_cancellation_token(); cancel.originate_on_cancel(false); // "don't debug spew when I'm cancelled" co_await resume_on_signal(ready); // If cancelled before `ready`, doesn't originate. // The first Cancel() consumes the opt-out, re-arms the flag co_await CleanupAsync(); // If cancelled after, then the next co_await will // call Cancel() again, and since should_originate is true now // it will Originate, thus causing the debug spew. }These three awaiter resume paths also threw hresult_canceled unconditionally, so originate_on_cancel(false) had no effect on them at all: impl::check_status_canceled (reached from await_adapter::await_resume for any coroutine awaiting a WinRT async that completes Canceled), timespan_awaiter::await_resume (resume_after) and signal_awaiter::await_resume (resume_on_signal).
Fix:
Test:
Adds async_originate_count_on_cancel, covering zero, single and repeated cancellation checks plus each of the three awaiter paths. It counts winrt_throw_hresult_handler invocations as a proxy for RoOriginateLanguageException, since observing the latter requires an out-of-process debugger.