Metadata
Title
Coroutine Examples
Category
general
UUID
bcccf8232bac4eeeb6f91ef928f8dc36
Source URL
https://cforall.uwaterloo.ca/features/coroutine.shtml
Parent URL
https://cforall.uwaterloo.ca/features/
Crawl Time
2026-03-18T05:16:13+00:00
Rendered Raw Markdown

Coroutine Examples

Source: https://cforall.uwaterloo.ca/features/coroutine.shtml Parent: https://cforall.uwaterloo.ca/features/


Semi-Coroutine

A semi-coroutine asymmetrically reactivates the coroutine that previously activated it.

Fibonacci

Output successive Fibonacci numbers on each call to routine next.

3 state
C single instance C∀ multiple instances
#include <stdio.h> int fn1, fn2, state = 1; // global variables int fib() { int fn; switch ( state ) { case 1: fn = 0; fn1 = fn; state = 2; break; case 2: fn = 1; fn2 = fn1; fn1 = fn; state = 3; break; case 3: fn = fn1 + fn2; fn2 = fn1; fn1 = fn; break; } return fn; } int main() { for ( int i = 0; i < 10; i += 1 ) { printf( "%d\n", fib() ); } } #include <fstream.hfa> #include <coroutine.hfa> coroutine Fibonacci { int fn; }; // used for communication void main( Fibonacci & fib ) with( fib ) { // called on first resume int fn1, fn2; // retained between resumes fn = 0; fn1 = fn; // 1st case suspend; // restart last resume fn = 1; fn2 = fn1; fn1 = fn; // 2nd case suspend; // restart last resume for () { fn = fn1 + fn2; fn2 = fn1; fn1 = fn; // general case suspend; // restart last resume } } int next( Fibonacci & fib ) with( fib ) { resume( fib ); // restart last suspend return fn; } int main() { Fibonacci f1, f2; for ( 10 ) { // print N Fibonacci values sout | next( f1 ) | next( f2 ); } }
1 state, multiple instances
C C∀
#include <stdio.h> typedef struct { int fn1, fn; } Fib; #define FibCtor { 0, 1 } int fib( Fib * f ) { int ret = f->fn1; f->fn1 = f->fn; f->fn = f->fn + fn; return ret; } int main() { Fib f1 = FibCtor, f2 = FibCtor; for ( int i = 0; i < 10; i += 1 ) { printf( "%d %d\n", fib( &f1 ), fib( &f2 ) ); } } #include <fstream.hfa> #include <coroutine.hfa> coroutine Fibonacci { int fn1; }; // used for communication void main( Fibonacci & fib ) with( fib ) { // called on first resume int fn; [fn1, fn] = [0, 1]; // precompute first two states for () { suspend; // restart last resume [fn1, fn] = [fn, fn1 + fn]; // general case } } int ?()( Fibonacci & fib ) with( fib ) { // function call operator resume( fib ); // restart last suspend return fn1; } int main() { Fibonacci f1, f2; for ( 10 ) { // print N Fibonacci values sout | f1() | f2(); } }

Format

Input successive characters on each call to routine prt and reformat the characters into 4 character per block and 5 blocks in a group per line.

C C∀
#include <stdio.h> struct Format { char ch; int g, b; }; void format( struct Format * fmt ) { if ( fmt->ch != -1 ) { // not EOF ? printf( "%c", fmt->ch ); fmt->b += 1; if ( fmt->b == 4 ) { // block ? printf( " " ); // separator fmt->b = 0; fmt->g += 1; } if ( fmt->g == 5 ) { // group ? printf( "\n" ); // separator fmt->g = 0; } } else { if ( fmt->g != 0 || fmt->b != 0 ) printf( "\n" ); } } int main() { struct Format fmt = { 0, 0, 0 }; for () { scanf( "%c", &fmt.ch ); if ( feof( stdin ) ) break; format( &fmt ); } fmt.ch = -1; format( &fmt ); } #include <fstream.hfa> #include <coroutine.hfa> coroutine Format { char ch; // used for communication int g, b; // global because used in destructor }; void main( Format & fmt ) with( fmt ) { for () { // for as many characters for ( g = 0; g < 5; g += 1 ) { // groups of 5 blocks for ( b = 0; b < 4; b += 1 ) { // blocks of 4 characters suspend; sout | ch | nonl; // print character } sout | " " | nonl; // print block separator } sout | nl; // print group separator } } void ?{}( Format & fmt ) { resume( fmt ); } // prime (start) coroutine void ^?{}( Format & fmt ) with( fmt ) { if ( g != 0 || b != 0 ) sout | nl; } void prt( Format & fmt ) { resume( fmt ); } int main() { Format fmt; try { for () { // read until end of file sin | fmt.ch; // read one character prt( fmt ); // push character for formatting } } catch( end_of_file * ) { // end-of-file raised } }

Device Driver

Input successive characters on each call to routine next to parse a simple network protocol and output message text.

// network protocol:  ... STX ... message ... ESC ETX ... message ... ETX 2-byte CRC ...

#include <fstream.hfa>
#include <coroutine.hfa>

enum Status { CONT, MSG, ESTX, ELNTH, ECRC };
coroutine Driver {
    Status status;
    char * msg, byte;
};
void ?{}( Driver & d, char * m ) { d.msg = m; }
Status next( Driver & d, char b ) with( d ) {
    byte = b; resume( d ); return status;
}
void checkCRC( Driver & d, unsigned int sum ) with( d ) {
    suspend;
    unsigned short int crc = byte << 8;         // sign extension over written
    suspend;
    // prevent sign extension for signed char
    status = (crc | (unsigned char)byte) == sum ? MSG : ECRC;
}
void main( Driver & d ) with( d ) {
    enum { STX = '\002', ESC = '\033', ETX = '\003', MaxMsg = 64 };
  msg: for () {                                 // parse message
        status = CONT;
        unsigned int lnth = 0, sum = 0;
        while ( byte != STX ) suspend;
      emsg: for () {
            suspend;
            choose ( byte ) {                   // process byte
              case STX:
                status = ESTX; suspend; continue msg;
              case ETX:
                break emsg;
              case ESC:
                suspend;
            }
            if ( lnth >= MaxMsg ) {             // buffer full ?
                status = ELNTH; suspend; continue msg;
            }
            msg[lnth++] = byte;
            sum += byte;
        }
        msg[lnth] = '\0';                       // terminate string
        checkCRC( d, sum );                     // refactor CRC check
        suspend;
    }
}
int main() {
    char msg[65], byte;
    Driver driver = { msg };
    try {
        for () {                                // read until end of file
            sin | byte;                         // read one character
            choose( next( driver, byte ) ) {    // analyse character
              case MSG:   sout | "msg:" | msg;
              case ESTX:  sout | "STX in message";
              case ELNTH: sout | "message too long";
              case ECRC:  sout | "CRC failure";
              default: ;
            }
        }
    } catch( end_of_file * ) {                  // end-of-file raised
    }
}

Full Coroutine

A full-coroutine symmetrically activates another coroutine, which directly or indirectly reactivates the original coroutine (activation cycle).

Ping Pong

Resume-resume cycle between coroutines ping and pong, no communication.

#include <fstream.hfa>
#include <coroutine.hfa>
coroutine PingPong {
    const char * name;
    unsigned int N;
    PingPong & part;
};
void ?{}( PingPong & this, const char * name, unsigned int N, PingPong & part ) {
        this.[name, N] = [name, N];  &this.part = &part;
}
void ?{}( PingPong & this, const char * name, unsigned int N ) {
    this{ name, N, *0p };                               // call first constructor
}
void cycle( PingPong & pingpong ) {
    resume( pingpong );
}
void partner( PingPong & this, PingPong & part ) {
    &this.part = &part;
    resume( this );
}
void main( PingPong & pingpong ) with(pingpong) {       // ping's starter ::main, pong's starter ping
    for ( N ) {                                         // N ping-pongs
        sout | name;
        cycle( part );
    }
}
int main() {
    enum { N = 20 };
    PingPong ping = { "ping", N }, pong = { "pong", N, ping };
    partner( ping, pong );
}

Producer / Consumer

Resume-resume cycle between coroutines prod and cons, bi-directional communication.

#include <fstream.hfa> #include <coroutine.hfa> #include <stdlib.hfa> // random #include <unistd.h> // getpid coroutine Cons; // forward int delivery( Cons & cons, int p1, int p2 ); void stop( Cons & cons ); coroutine Prod { Cons & c; int N, money, receipt; }; void main( Prod & prod ) with( prod ) { // starter ::main // 1st resume starts here for ( i; N ) { // N pairs of values int p1 = random( 100 ), p2 = random( 100 ); sout | p1 | " " | p2; int status = delivery( c, p1, p2 ); sout | " $" | money | nl | status; receipt += 1; } stop( c ); sout | "prod stops"; } int payment( Prod & prod, int money ) { prod.money = money; resume( prod ); // main 1st time, then return prod.receipt; // prod in delivery } void start( Prod & prod, int N, Cons &c ) { &prod.c = &c; prod.[N, receipt] = [N, 0]; resume( prod ); // activate main } coroutine Cons { Prod & p; int p1, p2, status; _Bool done; }; void ?{}( Cons & cons, Prod & p ) { &cons.p = &p; cons.[status, done ] = [0, false]; } void ^?{}( Cons & cons ) {} void main( Cons & cons ) with( cons ) { // starter prod // 1st resume starts here int money = 1, receipt; for ( ; ! done; ) { sout | p1 | " " | p2 | nl | " $" | money; status += 1; receipt = payment( p, money ); sout | " #" | receipt; money += 1; } sout | "cons stops"; } int delivery( Cons & cons, int p1, int p2 ) { cons.[p1, p2] = [p1, p2]; resume( cons ); // main 1st time, then return cons.status; // cons in payment } void stop( Cons & cons ) { cons.done = true; resume( cons ); // activate payment } int main() { Prod prod; Cons cons = { prod }; srandom( getpid() ); start( prod, 5, cons ); }