1. Issues
The C++ Coroutine TS [N4736] has issues 31 and 32 listed in [P0664R5]:
31. Add a note warning about thread switching near await and/or
wording.
coroutine_handle Add a note warning about thread switching near await and/or
wording
coroutine_handle 32. Add a normative text making it UB to migrate coroutines between certain kind of execution agents.
Add a normative text making it UB to migrate coroutines between certain kind of execution agents. Clarify that migrating between
s is OK. But migrating between CPU and GPU is UB.
std :: thread
2. Discussion
Using
, one can teleport a suspended execution between execution agents:
thread :: id get_an_id () { // here: acquire a lock, read thread_local co_yield std :: this_thread :: get_id (); //< one result // UB: release the lock, reuse the same thread_local co_return std :: this_thread :: get_id (); //< different result }
We say "teleport" here because the code that relocates the coroutine is outside the coroutine, in a possibly unrelated part of the program. This teleportation can take your coroutine to many interesting places, for example:
-
the thread that runs
main -
threads from
/std :: thread std :: async -
elemental functions of
,std :: par
,std :: par_unseq
algorithmsstd :: unseq -
global /
constructors (see note)thread_local -
global /
/thread_local
destructors (see note)static -
functions registered with
/at_exit quick_exit -
signal handlers
-
future
of [P0876R3]fibers_context
Note that it is presently implementation-defined whether many of these functions run in a specific thread, a single thread, or in many unspecified threads—see [CWG2046].
3. Proposed Resolution
After [N4736] [dcl.fct.def.coroutine] ❡6:
A suspended coroutine can be resumed to continue execution by invoking a resumption member function of an object of type
associated with this instance of the coroutine. The function that invoked a resumption member function is called resumer. Invoking a resumption member function for a coroutine that is not suspended results in undefined behavior.
coroutine_handle & lt ; P & gt ;
Add ❡7:
Resuming a coroutine on an execution agent other than the one it was suspended on has implementation-defined behavior unless both are instances of
. [Note: a coroutine that is moved this way should avoid the use of
std :: thread or
thread_local objects. — End note.]
mutex