Coroutine Examples


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 instanceC∀ 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
CC∀
#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.

CC∀
#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; 5 ) {				// groups of 5 blocks
			for ( b; 4 ) {			// blocks of 4 characters
				suspend;
				sout | ch;			// print character
			}
			sout | "  ";			// 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 format( Format & fmt ) {
	resume( fmt );
}
int main() {
	Format fmt;
	eof: for () {					// read until end of file
		sin | fmt.ch;				// read one character
	if ( eof( sin ) ) break eof;	// eof ?
		prt( fmt, ch );				// push character for formatting
	}
}

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 };
  eof: for () {											// read until end of file
		sin | byte;										// read one character
	  if ( eof( sin ) ) break eof;						// eof ?
		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: ;
		}
	}
}

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 );
}