Home > C++, Dev > A Lightweight Approach to Coroutine/Continuation in C/C++

A Lightweight Approach to Coroutine/Continuation in C/C++

It is considered to be useful to support efficient user-level thread scheduling, which is usually referred as Coroutine or Continuation. Its key building block is about leaving an execution point and getting back to that point, as if leaving does not happen. The common translation of the building block leads to CPU level context switch just as threads. Based on this wrong understanding, it typically requires primitives from native OS or runtime VM, such as fiber in Windows. The building block, however, is about the semantics of control flow.

When the requirement is clarified, the whole thing can be simplified as follows. Let’s start with a piece of simple runnable code.

#include <stdio.h>

struct Run {
    int cc;
    int i;
    Run() : cc(0) {}

    bool operator ()() {
        switch (cc) {
        case 0:;
            puts("a");
            cc = 1;
            return true;
        case 1:;
            for (i = 0; i < 2; ++i) {
                puts("b");
                cc = 2;
                return true;
        case 2:;
            }
            cc = 3;
            return true;
        case 3:;
            puts("c");
            cc = 4;
            return true;
        }
        return false;
    }
};

int main() {
    Run run;
    while (run());
    return 0;
}

This looks silly. Why not use function pointers to build a blazing beautiful dispatch table? The reason is simple. Function boundaries truncate program flow information and lose semantics of continuation. The following code is cleaned by using MacrO.

#include <stdio.h>

#define CallccBegiN(cc) switch (cc) { case 0:
#define YielD() cc = __LINE__; return true; case __LINE__:
#define CallccEnD() } return false

struct Run {
    int cc;
    int i;
    Run() : cc(0) {}

    bool operator ()() {
        CallccBegiN(cc);
            puts("a");
            YielD();
            for (i = 0; i < 2; ++i) {
                puts("b");
                YielD();
            }
            YielD();
            puts("c");
        CallccEnD();
    }
};

int main() {
    Run run;
    while (run());
    return 0;
}

The trick here is the feature of switch-case in C/C++ that can branch into if/for flow structures. This limits the user code from using switch-case. If compiler extension supports the address of label operator &&label, such as gcc, it could be a better trick.

Advertisements
Categories: C++, Dev
  1. No comments yet.
  1. No trackbacks yet.

Leave a Reply

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

WordPress.com Logo

You are commenting using your WordPress.com 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

%d bloggers like this: