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.

#include <fstream>
#include <coroutine>
coroutine Fibonacci { int fn; };       // used for communication
void ?{}( Fibonacci & fib ) with( fib ) { fn = 0; }
void main( Fibonacci & fib ) with( fib ) {
	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 ( int i = 1; i <= 10; i += 1 ) {
		sout | next( f1 ) | next( f2 ) | endl;
	}
}

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.

#include <fstream>
#include <coroutine>
coroutine Format {
	char ch;                                  // used for communication
	int g, b;                                   // global because used in destructor
};
void ?{}( Format & fmt ) { resume( fmt ); }  // prime (start) coroutine
void ^?{}( Format & fmt ) with( fmt ) { if ( g != 0 || b != 0 ) sout | endl; }
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;              // print character
			}
			sout | "  ";                   // print block separator
		}
		sout | endl;                       // print group separator
	}
}
void prt( Format & fmt, char ch ) {
	fmt.ch = ch;
	resume( fmt );
}
int main() {
	Format fmt;                            // format characters into blocks of 4 and groups of 5 blocks per line
	char ch;
	Eof: for ( ;; ) {                         // read until end of file
		sin | ch;                            // read one character
	  if ( eof( sin ) ) break Eof;      // eof ?
		prt( fmt, ch );                    // push character for formatting
	}
}

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>
#include <coroutine>
coroutine PingPong {
	const char * name;
	unsigned int N;
	PingPong * part;
};
void ?{}( PingPong & this, const char * name, unsigned int N, PingPong & part ) {
	this.name = name;
	this.N = N;
	this.part = &part;
}
void ?{}( PingPong & this, const char * name, unsigned int N ) {
	this{ name, N, *(PingPong *)0 };
}
void cycle( PingPong & pingpong ) {
	resume( pingpong );
}
void partner( PingPong & this, PingPong & part ) {
	this.part = &part;
	resume( this );
}
void main( PingPong & pingpong ) { // ping's starter ::main, pong's starter ping
	for ( unsigned int i = 0; i < pingpong.N; i += 1 ) {
		sout | pingpong.name | endl;
		cycle( *pingpong.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>
#include <coroutine>
#include <stdlib>                          // random
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 ( int i = 0; i < N; i += 1 ) {
		int p1 = random( 100 );
		int p2 = random( 100 );
		sout | p1 | " " | p2 | endl;
		int status = delivery( *c, p1, p2 );
		sout | " $" | money | endl;
		sout | status | endl;
		receipt += 1;
	}
	stop( *c );
	sout | "prod stops" | endl;
}
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.N = N;
	prod.c = &c;
	prod.receipt = 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 = 0;
	cons.done = 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 | endl;
		sout | " $" | money | endl;
		status += 1;
		receipt = payment( *p, money );
		sout | " #" | receipt | endl;
		money += 1;
	}
	sout | "cons stops" | endl;
}
int delivery( Cons & cons, int p1, int p2 ) {
	cons.p1 = p1;
	cons.p2 = 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 };
	random_seed( getpid() );
	start( prod, 5, cons );
}