4 ms·
A disadvantage of the ‘no function coloring’ in fibers is that it makes lockless programming harder. A nested function call can switch from under you without yo
by geertj 3y ago
A disadvantage of the ‘no function coloring’ in fibers is that it makes lockless programming harder. A nested function call can switch from under you without your knowledge, making it hard to know where the preemption points are and whether to take locks when making updates to shared state. With function coloring you immediately see whether a function might switch as there’s a different calling syntax.
I’ve programmed both fiber based systems and coroutines. I even created my own fiber libraries for Python (https://github.com/geertj/gruvi https://github.com/geertj/gruvi) and C++ (https://github.com/geertj/cgreenlet https://github.com/geertj/cgreenlet, mostly an experiment, and incorrectly named coroutines for C++ while it’s really fibers). In the Python version I experimented with some features to help you know whether a nested function might switch.
In the end, for me and for the problem domains I worked in, the explicit async/await co-routine style wins over fibers. It gives you most of the performance benefits of user mode switching, all of the memory benefits, while keeping your code mostly lock free.
- gpderetta 3y ago[I assume you are using lock free in a loose way, as lacking explicit mutual exclusion]. In practice in most real world applications in C++, absence of async calls is not enough to guard against re-entrancy. Shared state might still be mutated by callbacks, by recursively re-entering the event loop or by other threads. So you need to either document explicitly all the assumptions you are making and vet every single function you are calling in your implicit critical section, or you need some other mechanism that is going to look like a critical section anyway.
- geertj 3y agoYeah I was using the term lockless loosely here, not referring to atomics. I agree that using explicitly switched coroutines is not sufficient for mostly “lockless” programming (but I do think it’s necessary). I was coming from a thread per core perspective where you run one event loop per core and limit cross core communications (like in the Seastar framework). With this you should be able to perform most state manipulations without locks.
- dfawcus 3y agoThat is where an Actor or CSP style comes in useful, one then avoids the locks by having that state mutated in the context of one coroutine alone. Most problems can be recast in to that form, only a small subset are better handled by mutable shared state (and hence explicit locks. In those cases one should probably be modelling the updates as 'Compare and Swap' / 'Compare and Set' types of mutable operation, hence avoiding the situation where unexpected preemption can occur while holding a lock. From my perspective, having use both forms (manually and with language support), I generally find the coroutine (plus Actor/CSP) scheme to be easier to work with and reason about.
- munch117 3y ago> A disadvantage of the ‘no function coloring’ in fibers is that it makes lockless programming harder. But if you treat fiber-style coroutines like threads, and use locks and message queues exactly the same as you would with threads (although differently implemented, of course), then coroutines are basically better-behaved, and nicely deterministic, threads. It's a wonderful programming model, with the one big drawback that you can't take advantage of multiple cores.