The GNU Pth (portable threads) library is a "very portable POSIX/ANSI-C library for Unix platforms which provides...non-preemptive multithreading inside event-driven applications." Sound familiar? It's gevent for C!

I'd vaguely heard about GNU Pth before, but only as a "user-level threading library." I had no idea it was non-preemptive. I assumed it was something like the old LinuxThreads library, which used multi-processing and virtual memory tricks to simulate multiple threads.

How wrong I was! This article on making ncurses (a terminal user interface library—a GUI for the console) cooperate with GNU Pth got me to take a closer look at GNU Pth and its implementation. You can read all about it in that article and the manual; here I'll hit some highlights.

greenlets

To implement the concept of multiple threads of control in a single-threaded process, instead of greenlets, Pth uses the C standard setjmp/longjmp functions (or enhanced equivalents). These basically let a program save its call stack, swap it out, and return to it later, which is the key requirement for a greenlet.

monkey-patching

Pth provides a cooperative API that programs can call, but it also provides monkey-patching so existing programs can be made to be cooperative. The "soft" version of this monkey-patching is done at compile time, redirecting standard functions to Pth's cooperative versions:

#define fork          pth_fork
#define waitpid       pth_waitpid
#define system        pth_system
#define nanosleep     pth_nanosleep
#define usleep        pth_usleep
#define sleep         pth_sleep
#define sigprocmask   pth_sigmask
#define sigwait       pth_sigwait
#define select        pth_select

There's an additional "hard" version that catches the actual system calls themselves and provides link-able function definitions under the original names. This is more similar to what gevent does.

event primitives are exposed

Much like with gevent's hub and loop, the Pth event loop primitives are exposed to the user. In this way, the article could monkey-patch the ncurses event loop's blocking function and replace it with one that was Pth cooperative.

int pth_getch ()
{
    pth_event_t ev;
    ev = pth_event (PTH_EVENT_FD | PTH_UNTIL_FD_READABLE, 0);
    pth_wait (ev);
    return getch ();
}
#undef getch
#define getch pth_getch