Non-preemptive multitasking made reasoning about the problem way easier, because I had no concurrency to worry about, while still allowing inversion of control flow which meant that the SMTP state machines were small and simple. It worked really well.
...until I started getting bug reports from users about weird irreproducable crashes. After a very, very long time I eventually figured out that if spey was built on a system where sqlite had been compiled with pthread support, then it automatically linked in a thread-safe malloc, which tried to fetch the current thread ID, which on some versions of pthreads on some Linux architectures on some glibc versions with some kernel versions, was stored at the top of the stack. It used alignment tricks to find this.
So, every time something called malloc(), the libc was doing the equivalent of:
threadid = *(int*) (alignup(sp, 16MB) - sizeof(int))
Except my stacks weren't allocated through pthreads, so threadid ended up being garbage. Hilarity ensued.I eventually rebuild the coroutine library to be a wrapper around pthreads with a big lock to ensure that only one would run at a time. Which was kind of evil, but much more reliable. (Previously I was using my own coroutine implementation based on getcontext/setcontext.)
So, uh. I can't remember if I had a point any more; but the pain remains.
There's a link to the .h file near the bottom of the page.
That technique is what's used in protothreads and Contiki OS.
void MyMethod(std::vector<int> my_vec) {}
std::vector<int> v{1,2,3,4};
Lthread t1{&MyMethod, v};
t1.detach()
void my_coroutine()
{
lthread_compute_begin();
ret = fibonacci(55);
lthread_compute_end();
}A co-TA and I ported the concurrency library in JOS, MIT's teaching operating system, to regular UNIX:
https://github.com/geofft/vireo
It uses the standard-ish library functions setcontext and getcontext to avoid an assembly dependency. ("POSIX.1-2008 removes the specification of getcontext(), citing portability issues, and recommending that applications be rewritten to use POSIX threads instead.")
BTW makecontext and friend were made obsolete by posix for a technicality: makecontext signature isn't expressible any longer in a strictly conforming C99 applications, although in practice it will work with any C compiler.
Ironically this happened around the same time that coroutines started becoming popular again.
Contrast that to coroutines/fibers where tasks(i.e functions), which run in the same I/O thread(though not an absolute requirement). Cooperative multitasking is used, where a running function can yield control to other runnable functions by explicitly calling a yield like function -- that is, there is no scheduler that preemptively stops a function and runs another. You need to do that yourself.
There is some overlap between the benefits and properties of tasks that run on OS threads and fibers, but for the most part, they have different pros and cons. You can't really use in practice fibers to get the benefits you get from a GCD like framework (which utilizes multiple H/W cores to run functions/tasks concurrently), but also, you can't use GCD to improve throughput on a per-thread basis and deal with long-running and/or blocking lightweight tasks, or implement a fair-scheduling execution scheme).
Fully fledged coroutines let you save and resume execution in place. The main advantage is readability of conditional/looping control flow, which tends to quickly devolve into a mess in callback style code.
Is this statement basically a do while/while(true) infinite loop?
for (;;) {
...
}Lots of people do it because stupid C compilers produce better code for `for(;;)` than they do for `while(1)` and stupid C compilers used to be very common.
It's also one less character spent: A rare win/win.