Coroutine Examples
Source: https://cforall.uwaterloo.ca/features/coroutine.shtml Parent: https://cforall.uwaterloo.ca/features/
- Semi-coroutine output
- Semi-coroutine input
- Device Driver input/output
- Full coroutine no communication
- Full coroutine bi-directional communication
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 = ∂
}
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 = ∂
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 ); } |