This page is a snapshot from the LWG issues list, see the Library Active Issues List for more information and the meaning of New status.
std::thread is underspecifiedSection: 17.13.4.6 [coroutine.handle.resumption] Status: New Submitter: jim x Opened: 2026-03-11 Last modified: 2026-03-15
Priority: Not Prioritized
View all issues with New status.
Discussion:
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();
}
17.13.4.6 [coroutine.handle.resumption] p1 says:
Resuming a coroutine via
resume,operator(), ordestroyon an execution agent other than the one on which it was suspended has implementation-defined behavior unless each execution agent either is an instance ofstd::threadorstd::jthread, or is the thread that executesmain.
The wording doesn't specify the behavior for the latter case. We want #1 to be
sequenced before #2.
std::thread or std::jthread, or is the thread that executes main.
Proposed resolution:
This wording is relative to N5032.
Modify 17.13.4.6 [coroutine.handle.resumption] as indicated:
-1- Resuming a coroutine via
[…]resume,operator(), ordestroyon an execution agent other than the one on which it was suspended has implementation-defined behavior unless each execution agent either is an instance ofstd::threadorstd::jthread, or is the thread that executesmain. 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.