Coroutines in C (2/3)
Tags: c, coroutines, threads
Published: 2021-07-07 21:11:13

In the [previous post](https://dev.to/rdentato/coroutines-in-c-1-2-45ig) I briefly described the concept of *coroutines* and pointed to some well established implementation in C.

Now is time to unleash some evil on the C world and illustrate my own, extremely simple, implementation that you can find on [GitHub](https://github.com/rdentato/bee).

The evil is in the form of a [*Duff's device*](https://en.wikipedia.org/wiki/Duff%27s_device) which takes advantage of the fact that C allows jumping in the middle of a loop.

I called this version of coroutines *bees* as I imagine them to fly around swarming and buzzing while doing their work but yelding one another when they enter the hive.

A common scenario in network programming is that servers create a *worker* for each request they receive and return to listen to other requests. *Bees* are meant to be a mechanism to implement those workers.

Let's use the *iterator* example to see how everything works:

``` C
#include "bee.h"

beedef(iter, int n;)
{
   for (bee->n = 0; bee->n < 10; bee->n++) {
     beeyeld;
   }
   beereturn;
}

int main(int argc, char *argv[])
{
    iter counter = beenew(iter);
    while (beefly(counter)) {
      printf("%d\n",counter->n); 
    }
}
```

The implementation is all in the `bee.h` file (~60 lines of code).
After including it, the first step is to define our *bee*:

``` C
beedef(iter, int n;)
{
  ... // code to be executed here.
  beereturn;
}
```

The most notable points are:
  - `iter` is the type of bee we are going to define. It will be a pointer type.
  - `int n;` this is the list of variables that we want to survive after yelding.
  - `beereturn` **must** always be present and no code can appear after it.

When a *bee* is activated with `beefly()` it starts executing its code until it reaches `beereturn` or `beeyeld()`. In the first case the task is completed and the *bee* don't have any work to resume. In the second case, at the next activation, the work will start from the instruction immediately following the `beeyeld()` function.

In this example, the execution will be suspended at each iteration of the `for` loop.

``` C
   for (bee->n = 0; bee->n < 10; bee->n++) {
     beeyeld;
   }
```

Note that you access the *bee* preserved variables through the `bee` pointer (think of it as if it was `self` or `this`).

Let's see how the caller deals with *bees*. The first thing is to create a new *bee*:

``` C
    iter counter = beenew(iter);
```

Unfortunately the type of the *bee* must be specified twice but, all in all, it seems pretty nice to me.

Now you make the *bee* fly until it has completed its task:

``` C
    while (beefly(counter)) {
      printf("%d\n",counter->n); 
    }
```
Whenever the *bee* yelds, `beefly()` returns whether it can be resumed or not.

Note that you access the *bee* preserved variables using the pointer returned by `beenew()`.

Pretty simple, right? Well, maybe *too* simple. In fact to use them you will need to add some timers, non blocking I/O, network communication, etc.

Also, the `while(beefly(mybee))` idiom is a simple form of scheduler, you may need some more complex scheduling policy.

However, the fact that you may need something or may not need it it's exactly the reason why they are not included. Adding all sort of fancy stuff just because they *may* be useful or, worse, forcing a specific view on how these coroutines should be used, could limit their general usefulness and negate the beauty of their simplicity.

There will be time and place for developing complex stuff, but they don't belong to the concept of *coroutines*.

Now, there's only one step to complete my evil plan: explain how the code in `bee.h` works. But this can be the subject for another post.