Doc. No.: | P0991R0 |
Date: | 2018-03-12 |
Reply to: | Detlef Vollmann, dv@vollmann.ch |
Audience: | SG1 |
This is a try to understand the differences between the various (stackful) coroutine proposals. It may be useful to others as well.
While the distinction between symmetric and asymmetric seems to be established in literature, it's confusing to me. So I use switch_to for what is otherwise called symmetric and suspend/resume for what others call asymmetric.
For coroutines switch_to and suspend are blocking calls. switch_to is inherently boost blocking, suspend not necessarily.
P0073R2 is the only one that proposes an explicit blocking interface.
All except P0073R2 assume direct switching without scheduler.
For a simplified comparison, some base characteristics were chosen.
While it's understood that any coroutine proposal will contain some implementation that's not possible in pure C++ (involving assembler code or compiler intrinsics), some proposals require pretty heavy compiler support.
Some proposals require explicit passing/storing of a context object (that's returned by switch_to/resume). In other proposals it's just the state of the (stable) coroutine object.
Some proposals require implicit global/thread_local state.
For stackful coroutines it's sometimes important how the (side) stack is organized and allocated.
Allows to switch_to any context, even if not created as coroutine (especially main() or top-level thread functions).
Allows the switch_to/suspend/resume to transfer data directly.
As the switch_to approach is generally more expressive and suspend/resume can generally be built on top of switch_to with no nearly no overhead all but N3708 support switch_to, so it's not a separate coloumn in the table below.
Some proposals support suspend/resume in addition to switch_to.
Most proposals provide a pure library interface, but some use a special syntax or keywords.
current()
Some proposals provide a function to get a handle to the current coroutine.
Paper | Compiler | Explicit Context | Global State | Stack Alloc Control | suspend / resume | switch_to main | Direct Data Transfer | Syntax | current() |
---|---|---|---|---|---|---|---|---|---|
N3708 | no | no | yes | no | yes | yes | yes | no | no |
N3985 | no | no | yes | no | yes | yes | yes | no | no |
N4397 | no | no | yes | yes | no | yes | no | yes | yes |
N4398 | yes | no | yes | yes | no | yes | no | yes | yes |
P0099R0 | no | no | yes | yes | no | yes | no | no | yes |
P0073R3 | yes | no | ? | no | yes | ? | no | yes | ? |
P0099R1 | no | yes | no | yes | no | yes | yes | no | no |
P0534R0-2 | no | yes | no | yes | no | yes | yes | no | no |
P0534R3 | no | yes | no | yes | no | yes | no | no | no |
P0876R0 | no | yes | no | yes | no | yes | no | no | no |
No direct suspend/resume/switch_to API, but more generator targeted
pull_type
and push_type
with direct data transfers.
Special syntax for resumable lambdas. N4398 allows the compiler to decide between stackless and stackfull.
Conceptually like N4397, but pure library syntax.
Mostly (heavy) compiler techniques for unification and common syntax for stackless and stackful coroutines.
block()
as hint which coroutine to resumeclass event
as context object
low-level as it provides mechanisms for higher-level coroutine executors
Pretty different from P0099R0 to avoid global state.
Based on P0099R1, no major differences
Update of P0534R2 without direct data transfer.
Essentially a rename of P0534R3.