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 output](#Fibonacci)
- [Semi-coroutine input](#Format)
- [Device Driver input/output](#DeviceDriver)
- [Full coroutine no communication](#PingPong)
- [Full coroutine bi-directional communication](#ProdCons)

---

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