Created on 2026-03-11.00:00:00 last changed 2 weeks ago
Proposed resolution:
This wording is relative to N5032.
Modify [coroutine.handle.resumption] as indicated:
-1- Resuming a coroutine via `resume`, `operator()`, or `destroy` on an execution agent other than the one on which it was suspended has implementation-defined behavior unless each execution agent either is an instance of `std::thread` or `std::jthread`, or is the thread that executes `main`. Otherwise, the execution of the coroutine is resumed, or the coroutine is destroyed, on the execution agent on which the corresponding member function is invoked.
[…]
Consider the following example:
#include <atomic>
#include <cassert>
#include <coroutine>
#include <thread>
std::atomic<bool> flag = false;
int global = 0;
struct Task
{
struct promise_type
{
Task get_return_object() {
return {std::coroutine_handle<promise_type>::from_promise(*this)};
}
std::suspend_never initial_suspend() { return {}; }
std::suspend_never final_suspend() noexcept { return {}; }
void return_void() {}
void unhandled_exception() {}
};
std::coroutine_handle<promise_type> handle;
Task(std::coroutine_handle<promise_type> h) : handle(h) {}
~Task() {}
};
struct SuspendOnce
{
bool suspended = false;
bool await_ready() { return suspended; }
void await_suspend(std::coroutine_handle<>) { suspended = true; }
void await_resume() {}
};
Task my_coroutine() {
co_await SuspendOnce{};
flag.store(true, std::memory_order::release); // #2
}
auto t1 = std::thread([]() {
while (!flag.load(std::memory_order::acquire)); //#3
assert(global == 1); // #4
});
int main() {
auto task = my_coroutine();
auto& h = task.handle;
auto t2 = std::thread([&]() {
global = 1; // #1
h.resume();
});
t2.join();
t1.join();
}
[coroutine.handle.resumption] p1 says:
Resuming a coroutine via `resume`, `operator()`, or `destroy` on an execution agent other than the one on which it was suspended has implementation-defined behavior unless each execution agent either is an instance of `std::thread` or `std::jthread`, or is the thread that executes `main`.
The wording doesn't specify the behavior for the latter case. We want `#1` to be sequenced before `#2`.
The sequenced-before is described in terms of two evaluations that are both executed by a single thread. The coroutine is initially executed in the main thread; however, it's resumed in a different thread. The first paragraph doesn't specify the specific behavior; it only says the behavior is implementation-defined if the different execution agent is not an instance of `std::thread` or `std::jthread`, or is the thread that executes `main`.| History | |||
|---|---|---|---|
| Date | User | Action | Args |
| 2026-03-15 10:26:29 | admin | set | messages: + msg16027 |
| 2026-03-11 00:00:00 | admin | create | |