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