Live data from Hacker News

Fiber in C++: Understanding the Basics

agraphicsguynotes.com

41–50 of 77 posts

Re: Fiber in C++: Understanding the Basics

#41
post #32

Earlier quoted context omitted.

Unfortunately in 2023 c++20 coroutines are just barely starting to become usable. Compilers have bugs, especially with symmetric transfer, e.g. msvc doesn't suspend until await_suspend returns (which causes races and double-frees), gcc stops using tail calls in some modes (which can cause stack overflow), I ended up emulating symmetric transfer because you can't rely on it, and emulating it is of course slower. Compi…

> exception safe, because you need to save/restore exception globals when switching fibers, Do you have more details about this issue? What exception globals? And for which ABI? You mean some sort of current pending exception when switching during unwinding? [I'm a huge fan of stackful coroutines and I wish were blessed by the standard]

C++ ABI has some per-thread globals: the number that is returned from std::uncaught_exceptions(), and the chain of currently caught exceptions. For example in llvm this is available with a cxa_get_globals call:

https://github.com/llvm/llvm-project/blob/b05f1d93469fbd6451...

These need to be saved/restored when switching fibers, otherwise fiber switches from catch clauses (and destructors!) are unsafe, throw without argument may rethrow incorrect exception, code that commits/rollbacks based on uncaught exceptions counter will not work correctly, etc.

One example I know where this save/restore is implemented is the userver framework, but it seems to be unexpectedly rare in fiber implementations last time I looked.

Re: Fiber in C++: Understanding the Basics

#43

I'm confused in how this article defines "fiber." At the beginning of the article, fibers are described as a lightweight userspace thread, similar to what is provided in the runtimes of Haskell and Go, and has been recently revived in Java as "virtual threads." However, later in the article (after a long and apparently irrelevant digression about stack management), they describe Windows API functions that are require…

Personally if I had a choice of just one I’d pick the threads over fibers if the threads facility is really high performance like the one in Java.

Today you have quad cores even on a cheap phone and 16 on a desktop and 60+ on a server and OS and some runtimes make it pretty easy to get large speed ups on many tasks even when subtasks are closely coordinated. (One thing I like about Java is that it has low-level thread primitives that really scale as opposed to many systems have have a small set of low-level primitives that in theory let you do everything but not scale.)

Contrast that to fibers and similar things (JS and Python async) that do a great job of keeping a CPU busy when you are waiting for network activity but are limited to one CPU.

Re: Fiber in C++: Understanding the Basics

#44
post #32

Since coroutines have been in the standard since C++20, I wish he would have explained better what are the advantages of using a Fiber over a Coroutine.

Unfortunately in 2023 c++20 coroutines are just barely starting to become usable. Compilers have bugs, especially with symmetric transfer, e.g. msvc doesn't suspend until await_suspend returns (which causes races and double-frees), gcc stops using tail calls in some modes (which can cause stack overflow), I ended up emulating symmetric transfer because you can't rely on it, and emulating it is of course slower. Compi…

Yes, in my (limited) use case with coroutines for a recursive descent generator I got it working by following this tutorial:

https://www.scs.stanford.edu/~dm/blog/c++-coroutines.html

but I was unpleasantly surprised by much extra code it required to make it run.

The Tiny Fiber library in the article looks pretty hacky right now and the amount of x64/arm specific code might cause trouble with portability, but the concept itself is intriguing. Thank you for the details.

Re: Fiber in C++: Understanding the Basics

#45

Earlier quoted context omitted.

"Fibers", "green threads", "stack switching", "cooperative multitasking" are essentially all the same thing, they all rely on being able to switch execution context by switching to a different stack within the same OS thread. As such they can be implemented either in (CPU/OS-specific) user code or in "OS APIs" (like the Windows Fiber functions). Only downside of the technique is that it cannot be implemented in WASM…

Having a native stack switch for WASM would be nice but it's not necessary. Map all memory segments as shared into several instances of a single module -- each instance is a fiber. Implementation is left as an exercise for the reader.

This doesn't address the shadow stack at all. It doesn't work.

Re: Fiber in C++: Understanding the Basics

#46

Remind me again why no OS other than Solaris ever provided "scheduler activations" (i.e. the kernel calls back into a user-space scheduler whenever a kernel-level scheduling event occurs)

Windows had a custom scheduler API (https://learn.microsoft.com/en-us/windows/win32/procthread/u...) but they removed it, presumably because it wasn't used much

Re: Fiber in C++: Understanding the Basics

#47

Remind me again why no OS other than Solaris ever provided "scheduler activations" (i.e. the kernel calls back into a user-space scheduler whenever a kernel-level scheduling event occurs)

Windows had a custom scheduler API ( https://learn.microsoft.com/en-us/windows/win32/procthread/u... ) but they removed it, presumably because it wasn't used much

As the Wikipedia page shows, NetBSD had an implementation but later removed it. There isn't really enough information to make a sound judgement on scheduler activations. Although if I had to guess, there might be too much overhead (maintenance of code and/or actual performance) for something that is rarely used. It might make more sense now that we're moving towards user-level concurrency everywhere (async/await, goroutines, virtual threads, etc.).

Re: Fiber in C++: Understanding the Basics

#48

Since coroutines have been in the standard since C++20, I wish he would have explained better what are the advantages of using a Fiber over a Coroutine.

Advantage: No function colouring. Just like with threads, you don't need to declare which functions might potentially yield. There's none of that noisy async/await syntax. Advantage: Fast switching. Switching between fibers is done by swapping in new values for the program counter and stack pointer. Compare with async, where yielding only takes you up one level of stack, so your code ends up walking a stack of corout…

I see. Seems pretty similar conceptually to C++ coroutines.

Fast switching between fibers is mainly what got me interested, of course. Thank you for the explanations.

Re: Fiber in C++: Understanding the Basics

#49

Since coroutines have been in the standard since C++20, I wish he would have explained better what are the advantages of using a Fiber over a Coroutine.

Advantage: No function colouring. Just like with threads, you don't need to declare which functions might potentially yield. There's none of that noisy async/await syntax. Advantage: Fast switching. Switching between fibers is done by swapping in new values for the program counter and stack pointer. Compare with async, where yielding only takes you up one level of stack, so your code ends up walking a stack of corout…

Just a note on naming: fibers, goroutines, green threads, virtual threads and the like are stackful coroutines (miniature threads, essentially). Then there are stackless coroutines, which are state machine objects that have function coloring (async/await).

Re: Fiber in C++: Understanding the Basics

#50
post #17

Earlier quoted context omitted.

No shared resources can be a performance problem, I can see why there are rusts and vales efforts to come to a slightly different tradeoff.

Pony/Ponylang claims that they can do actors fast and safe (even safer than rust). There is a type system that allows one to share data between actors safely, or so they claim.

I believe they use a GC, so they may be able to leverage the runtime to help out. If it's all type system-based, the compiler probably has to be stricter to ensure safety (Rice's theorem implies that compilers err on the side of rejecting some valid programs in order to reject all invalid programs). Maybe Pony could be a blend of Erlang and Rust?
Post reply on HN