scvalex.net

2. Coroutines in C

There’s a pair of C functions that I feel are underused, namely setjmp(3), and longjmp(3). With them, you can fake exceptions in C, implement coroutines, and much more.

The two functions work as a pair: setjmp saves the current stack context in a jmp_buf; longjmp can then revert back to the saved state. Ideally, a jmp_buf is a pointer to a position in the stack; setjmp saves the current position, and longjmp jumps back to a saved position. All of this is a bit more complicated in real life, and is very implementation specific.

For example, here’s a couple of coroutines that work through the Collatz Conjecture. One of the functions divides even numbers by two, while the other multiplies odd numbers by three and adds one.

#include <stdio.h>
#include <setjmp.h>

void even(int);
void odd(int);

int main(int argc, char *argv[]) {
    even(7);
    return 0;
}

jmp_buf even_task, odd_task;

void even(int n) {
    int aux;
    if (!(aux = setjmp(even_task))) {
        odd(n);
    } else {
        n = aux;
    }
    while (n > 1) {
        while (n % 2 == 0) {
            printf("Even! %3d/2 =\n", n);
            n /= 2;
        }
        if (!(aux = setjmp(even_task))) {
            longjmp(odd_task, n);
        } else {
            n = aux;
        }
    }
    printf("Done!   1\n");
}

void odd(int n) {
    while (n > 1) {
        if (n % 2 == 1) {
            printf("Odd!  %3d*3+1 =\n", n);
            n = 3 * n + 1;
        }
        int aux;
        if (!(aux = setjmp(odd_task))) {
            longjmp(even_task, n);
        } else {
            n = aux;
        }
    }
}

I’m not particularly happy with the lack of symmetry between odd and even. Here’s a forkable gist of the above, if anyone wants to have a go at improving it.