
Show HN: Libconcurrent – Coroutines in C - mitghi
https://github.com/sharow/libconcurrent
======
david-given
I wrote a networking daemon (SMTP greylisting proxy; now unmaintained, but
<plug> [http://spey.sf.net](http://spey.sf.net) </plug>) using coroutines
once.

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.

~~~
avdicius
I use pthreads and coroutines (actually fibers) inside pthreads and lock-free
bounded queues to communicate between pthreads. All pthreads execute
coroutines until all of them get blocked, then pthreads go to epoll. If a
pthread gets a socket readiness notification for a socket it owns or an orphan
socket then it handles I/O itself, without any synchronization. If a socket is
owned by a coroutine running on a different pthread then the notification is
forwarded to that pthread via the bounded queue. The project is called
MainMemory, and it is just here on Show HN.

------
brudgers
The links to other projects with similar goals at the bottom of the Readme is
brilliant. It helps developers solve their problems irrespective of whether
this particular solution works...and solving problems not stars and forks is
the measure of utility for a library.

~~~
zanerino
It's missing this link, which I previously thought was "the" co-routine de-
facto standard for C before seeing all those other links:
[http://www.chiark.greenend.org.uk/~sgtatham/coroutines.html](http://www.chiark.greenend.org.uk/~sgtatham/coroutines.html)

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.

------
halayli
You might also want to look at lthread. It has some interesting functions like
lthread_compute_begin()/end() to run expensive computations inside a
coroutine. the compute methods move the coroutine into an actual pthread and
resume until compute_end() is called. It also has c++11 bindings called
lthread-cpp which allows you to launch coroutines with variable arguments &
types and comes with nice socket wrappers.

    
    
      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();
      }

------
stepanhruda
How does this compare to [http://libmill.org](http://libmill.org)?

~~~
markpapadakis
libmill offers a channels(FIFO queues) abstraction. This is a
fibers/coroutines framework. They solve different problems. See go-lang
channels for what libmill is providing.

~~~
stepanhruda
Oops, sry, I meant
[https://github.com/Zewo/libvenice](https://github.com/Zewo/libvenice)

------
geofft
Cool!

A co-TA and I ported the concurrency library in JOS, MIT's teaching operating
system, to regular UNIX:

[https://github.com/geofft/vireo](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.")

~~~
gpderetta
*context are unfortunately quite slow as they require a system call to update the signal mask. The sigaltstack trick popularized by Pth is probably still more portable and faster.

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.

------
evmar
It would be helpful if the README explained how this is different than the
many other coroutine libraries.

------
delinka
How does this compare to Apple's GCD [1]?

1 -
[https://en.wikipedia.org/wiki/Grand_Central_Dispatch](https://en.wikipedia.org/wiki/Grand_Central_Dispatch)

~~~
markpapadakis
I ported GCD to Linux when it was made available/open-sourced some many months
ago. Effectively, GCD uses a bunch of (named) FIFO queues, protected by
semaphores, and manages some OS threads, which dequeue tasks(or blocks) from
the queues and execute them. It provides some nice higher level abstractions
and functionality (e.g groups ) on top of that. It also supports both
synchronous and asynchronous execution semantics. The OS threads are of course
managed by the OS scheduler. (unfortunately, pthread workqueues are not
supported on Linux yet -- the original implemntation creates workqueues for
each defined priority and that's how priorities are supported in GCD. On Linux
you can use setpriority(), ioprio_set() and other such APIs that don't really
offer much in practice. But I digress).

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).

------
ghrifter
I got a silly question:

Is this statement basically a do while/while(true) infinite loop?

    
    
        for (;;) {
             ...
        }

~~~
geocar
Yes. You're likely to run into this construct often in C.

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.

~~~
ghrifter
Cool, thanks! I have yet to learn C other than from what I've learned from
programming in C++.

I am really interested in learning some C however, especially after this post
hit front page a few days ago:

[https://matt.sh/howto-c](https://matt.sh/howto-c)

~~~
frik

      for (;;) { ... }
    

is a valid JavaScript and PHP syntax too. And used also as the first few chars
of a JSON stream to prevent other sites consuming your internal API (e.g.
Facebook uses it, Google+ uses while(1) { ... } which is longer).

------
pantalaimon
Are the Coroutines scheduled across multiple CPUs?

~~~
mitghi
No, i suggest to check lthread.

