|
|
|
|
|
by JonChesterfield
1041 days ago
|
|
Coroutines have a performance cost associated with allocating a stack. C++ solved this by shipping something that are not coroutines and branding it coroutines anyway. Sometimes it still needs heap allocation, but of a constant size and smaller thing. A coroutine is a thread of execution that can yield to another. The scheduler is thus under userspace control. The C++ thing is syntax sugar over a control flow transform which looks kind of similar, except you have to annotate all the functions in the call tree and can't do anything that can't be desugared to the same runtime system that was there anyway. The primary downside of C++ coroutines is then clear. They aren't coroutines, and by squatting on the name, make it borderline impossible that C++ will ever have coroutines. This is annoying as they're one of the things which really needs compiler support to do well. |
|
I am not a C++ person, but I protest this characterization. You obviously know the categories and I suppose you know the history, but I will recount them so that everyone else understands my objection.
O.G. coroutines emerged in a world where subroutines were, by and large, not reëntrant. Function parameters, local variables, and return addresses were static, and there was no call stack in the modern sense. This is why the old timers said that coroutines were a generalization of subroutines; only the jump instruction of the call/return really needed to change.
Once call stacks became common, coroutines fit awkwardly, and people tried to adapt them in various ways.¹ The world has settled on two designs for reconciling coroutines to the call stack: thick and thin coroutines. Thin coroutines allow suspension within the body of the coroutine but not within subroutine calls; this way the size of the coroutine’s state can be known at compile time and its interaction with the stack is relatively clear. Thick coroutines (i.e., green threads) can be suspended within a subroutine call, and thus require their own slices of stack — either separate from the main stack or copied from and to it as the coroutine is suspended and resumed.
Thin coroutines are absolutely coroutines. They are truer to the original definition than thick coroutines are! They are more limited than thick coroutines, true; whether that makes them better or worse is a matter of design trade-offs. But they certainly deserve the name.
[1] Simula 67, to my understanding, treated objects as a kind of coroutine instance where function definitions in the coroutine body became methods that closed over its local variables.