The research Microsoft engineers did on stackful vs stackless coroutines for the c++ standard I think swayed this as “the way” to implement it for something targeting a systems level - significantly less memory overhead (you only pay for what you use) and offload the implementation details of the executor (lots of different design choices that can be made).
Yup, stackful fibers are an anti-pattern. Here's Gor Nishanov's review for the C++ ISO committee https://www.open-std.org/JTC1/SC22/WG21/docs/papers/2018/p13... linked from https://devblogs.microsoft.com/oldnewthing/20191011-00/?p=10... . Notice how it sums things up:
> DO NOT USE FIBERS!
And this is the rebuttal: https://www.open-std.org/JTC1/SC22/WG21/docs/papers/2019/p08...
There are downsides to stackful coroutines (peak stack usage for example), but I feed that p1364 was attacking a strawman: first of all it is comparing a solution with builtin compiler support against a pure library implementation, second it is not even comparing against the reference implementation of the competing proposal.
The TL;DR of that sums up my opinions pretty well.
As an aside, I know Rust would be unlikely to implement segmented stacks for fibers, given that they were burned by the performance implications thereof previously.
> DO NOT USE FIBERS!
For C++.
If your language has RAII or exceptions, it raises crazy questions about how if thread A is hosting fiber 1, which throws an exception, which propagates outside of the fiber invocation scope, destroys a bunch of objects, then we switch to fiber 2, which sees the world in an inconsistent state (outside resources have been cleaned up, inside ones still alive).
This was literally impossible in pre-fiber code, so most existing code would probably not handle it well.
That's not different from threads running concurrent exceptions (in fact it is simpler in the single threaded example). RAII or exceptions are really not an issue for stackful coroutines.
Is stackful fibers the same as stackful coroutines?
yes same thing, different names.
Many of these issues go away if you control the compiler and runtime, which Rust does (and they needed to make changes to those to add async, so changes were inevitable).
> significantly less memory overhead
On an OS with overcommit, you might also only pay for what you use (at a page granularity), but this may be defeated if the stack gets cleared (or initialized to a canary value) by the runtime.
Not just cleared. Each stack would get dirtied to the largest depth and that memory never gets reclaimed even if you only hit that deep of stack once. And the OS’s only way to reclaim that is if the allocator frees that page (in practice rarely particularly for glibc) or it gets swapped to disk (expensive since unused pages end up getting swapped too)
> Each stack would get dirtied to the largest depth and that memory never gets reclaimed even if you only hit that deep of stack once
Right, but the issue under discussion was that "right sizing" stacks is hard. You always need to have a stack sized to at least the largest depth.
Pages that get swapped out to disk and never accessed again aren't that expensive. Disk is cheap.