What is the difference between suspend-up and suspend-down coroutines?

Viewed 366
2 Answers

The terminology seems to be explained in p0099r1: "A low-level API for stackful context switching" which says:

Notes on suspend-up and suspend-down terminology The terms suspend-up and suspend-down were introduced in paper N4232 2 and carried forward in P0158 9 to distinguish stackless ( suspend-up ) and stackful ( suspend-down ) context switching. These terms rely on a particular visualization of the C++ function call operation in which calling a function passes control “downwards,” whereas returning from a function passes control “upwards.” The authors recommend the terms suspend-by-return instead of suspend-up , and suspend-by-call instead of
suspend-down . The recommended terminology directly references the underlying C++ operations, without requiring a particular visualization.
suspend-by-return ( suspend-up , or “stackless” context switching) is based on returning control from a called function to its caller, along with some indication as to whether the called function has completed and is returning a result or is merely suspending and expects to be called again. The called function’s body is coded in such a way that – if it suspended – calling it again will direct control to the point from which it last returned. This describes both P0057 6 resumable functions and earlier technologies such as Boost.Asio coroutines. 12
suspend-by-call ( suspend-down, or “stackful” context switching) is based on calling a function which, transpar- ently to its caller, switches to some other logical chain of function activation records. (This may or may not be a contiguous stack area. The processor’s stack pointer register, if any, may or may not be involved.) This describes N4397 3 coroutines as well as Boost.Context, 13 Boost.Coroutine2 14 and Boost.Fiber. 15 std::execution_context<>::operator()() requires suspend-by-call semantics.

Both are old papers and is separate from p0057 which seems to be the main coroutines paper. p0444 discusses trying to unify these paper but does not seem to have gone anywhere. Also see Trip Report: C++ Standards Meeting in Issaquah, November 2016 which says:

The Coroutines TS contains the co_await proposal, based on Microsoft’s original design.

As mentioned previously, there are efforts underway to standardize a proposal for a different, stackful flavour of coroutines, as well as an exploratory effort to unify the two flavours under a common syntax. These proposals, however, are not currently slated to target the Coroutines TS. They may instead target a different TS (and if a unified syntax emerges, it could be that syntax, rather than the one in the Coroutines TS, that’s ultimately merged into the C++ standard).

each function creates a stack frame (reserves space on the stack for local variables etc.)

suspend-up:

  • used by stackless context switching (coroutines...)
  • because you have only one stack (applications stack), you have to remove the stack frame of the suspended function (function of stackless coroutine)
  • otherwise other functions, executed after the coroutine has been suspended, would write their own stack frame and thus corrupt the stack frame of the suspended one
  • suspend-up == remove stack frame of suspended function == step some addresses at the stack upwards (for architectures where the stack grows from high to low addresses)

suspend-down:

  • used by stackful context switching (coroutines, fibers, ...)
  • each coroutine/fiber gets their own stack, thus the application consist out of multiple stacks
  • if a stackful coroutine is suspended, the stack frame remains on the stack (because zhr stack is specific to/owned by the coroutine)
  • the stack pointer is simply changed to another stack (== switching to another stackful coroutine/fiber)
  • because the stack frame remains on the stack, it is called suspend-down
Related