Continuations in C; easy asynchronous stuff

If you have worked with asynchronous stuff, you surely must have reached a point where you need to save your current context, in order to continue the computation after you’ve received some data asynchronously.

For example, let’s say you want to upload a file to a web service, then you need to wait for some kind of id, and then you can add tags to identify it.

The simplest way to do it would be to just block until you receive the id, and then tag it. That is easy, but in the meantime nothing else can be done, and if you have a UI you’ll just see an unresponsive application.

Another approach is to create a new thread, that way other stuff can keep running while your thread waits for the id. The problem here is that threads are not exactly easy to handle, and some UI’s aren’t particularity good at handling threads. Another problem is that threads are expensive, if you have many threads being created and destroyed rapidly you are probably wasting resources.

Then if you are not lazy, you might want to save the context in a separate structure, write a separate a callback to handle the response, and the continue the processing. That is not too difficult, there are no performance penalties and should play nicely with every other code you might be running. Unfortunately it’s tedious, and if you have many contexts to store, it becomes painful.

So, here comes continuations the rescue. The concept is really simple, you reach a point in your code where don’t have anything else to compute, so you yield to allow other computations to continue, and when the relevant data is ready, the context is resumed and your code continues the execution as if nothing happened.

yield id = file.get_id # wait, but let other stuff to happen

It sound too good to be truth, and it is. Most languages don’t have support for this continuations, so you end up with ugly workarounds in order to make them work.

Fortunately there are some C primitives you can use for continuations. Wikipedia has a nice list of alternatives.

So I implemented something very basic based on Marc Lehmann’s libcoro, which makes this code possible:

struct task *test_task (const char *str) {
  struct task *t;
  void helper (struct task *t) {
    const char *tmp = str;
    printf ("%s: test: tmp=%s\n", __func__, tmp);
    task_yield (t);
    printf ("%s: after yield: tmp=%s\n", __func__, tmp);
    task_complete (t);
  t = task_new (helper);
  return t;

The output goes like this:
main: begin
helper: test: tmp=hello world!
main: foo
helper: after yield: tmp=hello world!
main: end

I still haven’t used this for anything on real-world scenarios, but looks like an interesting idea to explore.


3 thoughts on “Continuations in C; easy asynchronous stuff

  1. Felipe,
    You seem to live in Mexico, few careers include continuations in their curricula.
    Have you studied continuations in your school?

  2. These are coroutines, not continuations. Real continuations allow you to arbitrarily jump back and forth in your program’s execution, e.g. you can go back, change a variabale, the code will execute again but differently, with a different outcome, then go back into forward in the code, to the original outcome, continue, go back to the different outcome, etc…

    Imagine your program is a time traveler. It goes back in time, kills someone’s grandparents, and as that timeline of your program executes, that someone is never born, but then goes into the timeline where the someone was born, interact with the someone, then goes back into the timeline without the one…

Leave a Reply

Fill in your details below or click an icon to log in: Logo

You are commenting using your account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s