/* Test program for Portable Coroutine Library. Written in 2009 by Guenther Brunthaler. */ #include #include #include /* Get it: wget http://xmailserver.org/pcl-1.6.tar.gz */ #include #ifdef GNUC #define NORETURN __attribute__((noreturn)) #else #define NORETURN #endif /* Type of the coroutine main function expected by co_create(). */ typedef void (*CO_FUNC_T)(void *); /* Parameter passing structure for random-generator coroutine. */ union prng_arguments { /* Input parameters. */ union { /* For initialization phase only. */ struct { double seed; /* For seeding the generator. */ } initialize; /* For requesting the next random number to be generated. */ struct { int min; /* Smallest allowed value for random number. */ int max; /* Largest allowed value for random number. */ } generate; } in; /* Output parameters. */ union { int rnd; /* The generated pseudo random number. */ } out; }; /* Constants used by guesser/solver coroutines. */ enum {TOO_SMALL, JUST_RIGHT, TOO_LARGE}; /* Parameter passing structure for quiz master coroutine. */ union quizmaster_arguments { /* Input parameters. */ union { /* For initialization phase only. */ struct { coroutine_t prng; /* Random number generator coroutine. */ union prng_arguments *prng_arguments; coroutine_t candidate; /* Quiz candidate coroutine. */ union candidate_arguments *candidate_arguments; } initialize; /* The guess from the candidate. */ int guess; } in; /* Output parameters. */ union { int test_result; /* Answer for the candidate. */ } out; }; /* Parameter passing structure for quiz candidate coroutine. */ union candidate_arguments { /* Input parameters. */ union { /* For initialization phase only. */ struct { int smallest; /* Lower bound for number to be guessed. */ int largest; /* Upper bound for number to be guessed. */ } initialize; /* What the quiz master tells us. */ int test_result; } in; /* Output parameters. */ union { int guess; /* Our guess. */ } out; }; /* Stores seed value when called first. From then on, yields one pseudo random number per activation. */ static void prng_coroutine(union prng_arguments *xch) { double const multiplier= 997; double const displacement= atan2(0, -1); /* Pi! */ double seed; seed= xch->in.initialize.seed; co_resume(); /* Resume caller. */ for (;;) { int min, max; /* Generate next normalized pseudo random number. */ seed= seed * multiplier + displacement; seed-= floor(seed); /* Emit next random number scaled to the desired value range. */ min= xch->in.generate.min; max= xch->in.generate.max; xch->out.rnd= min + (int)(seed * (max - min + 1)); co_resume(); /* Yield result to caller. */ } } /* Use a candidate coroutine to solve a quiz generated here. */ static void quizmaster_coroutine(union quizmaster_arguments *xch) { int const lower= 1; int const upper= 1000; coroutine_t prng= xch->in.initialize.prng; union prng_arguments *pa= xch->in.initialize.prng_arguments; coroutine_t candidate= xch->in.initialize.candidate; union candidate_arguments *ca= xch->in.initialize.candidate_arguments; int secret; /* Run an infinite number of games. */ /* (We want to avoid leaving the coroutine as this would destroy it.) */ for (;;) { { /* Generate the random number to be guessed. */ pa->in.generate.min= lower; pa->in.generate.max= upper; co_call(prng); secret= pa->out.rnd; } (void)printf( "A: Try to guess a number in the range from %d to %d!\n" , lower, upper ); { /* Tell the candidate the numeric range of the secret number. */ ca->in.initialize.smallest= lower; ca->in.initialize.largest= upper; /* Processed on first invocation. */ /* Iterate until the candidate finds the solution. */ for (;;) { /* Request next guess. (Also initialize if first invocation.) */ co_call(candidate); if (ca->out.guess == secret) { (void)printf( "A: Congratulations - %d is the secret number!\n", secret ); ca->in.test_result= JUST_RIGHT; co_call(candidate); break; /* Coroutine will finish execution. */ } else if (ca->out.guess < secret) { (void)printf("A: %d is too small!\n", ca->out.guess); ca->in.test_result= TOO_SMALL; } else { (void)printf("A: %d is too large!\n", ca->out.guess); ca->in.test_result= TOO_LARGE; } } } /* Give our primary caller a chance to terminate the series of games. */ co_resume(); } } static void NORETURN die(char const *message) { (void)fputs("An error has occurred: ", stderr); (void)fputs(message, stderr); (void)fputc('\n', stderr); exit(EXIT_FAILURE); } /* Let a quizmaster coroutine assess our guesses. */ static void candidate_coroutine(union candidate_arguments *xch) { /* Play an infinite number of games. */ /* (We want to avoid leaving the coroutine as this would destroy it.) */ for (;;) { int lower= xch->in.initialize.smallest; int upper= xch->in.initialize.largest; int try= 1; (void)printf( "Q: OK, so it's a number from %d to %d - let's see!\n" , lower, upper ); for (;;) { int guess= (lower + upper) / 2; (void)printf("Q: My guess # %d: What about %d?\n", try, guess); xch->out.guess= guess; co_resume(); /* Evaluate our query. */ switch (xch->in.test_result) { case TOO_SMALL: lower= guess + 1; break; case TOO_LARGE: upper= guess - 1; break; case JUST_RIGHT: goto won; default: die("Unrecognized assessment!"); } ++try; } won: (void)printf("Q: I knew I would finally guess it!\n"); co_resume(); /* Give our caller a chance to terminate. */ } } /* Mostly a co_create() wrapper with included error checking. */ static coroutine_t safe_co_create(CO_FUNC_T func, void *data, int stacksize) { coroutine_t *co; if (!(co= co_create(func, data, 0, stacksize))) { die("Could not allocate coroutine resources!"); } return co; } /* Bundled resources for playing a series of games. */ struct game_arguments { coroutine_t pco; union prng_arguments pa; coroutine_t qco; union quizmaster_arguments qa; coroutine_t cco; union candidate_arguments ca; }; static void play_game(struct game_arguments *ga) { /* Set up arguments for quizmaster. */ ga->qa.in.initialize.prng= ga->pco; ga->qa.in.initialize.prng_arguments= &ga->pa; ga->qa.in.initialize.candidate= ga->cco; ga->qa.in.initialize.candidate_arguments= &ga->ca; /* I admit there is no actual reason why quizmaster is a coroutine as it has only a single exit point. However, this is a coroutine test applcation - so let's test! */ co_call(ga->qco); /* Run quizmaster coroutine. */ } int main(int argc, char **argv) { int const stksz= 0x2000; /* The stack size we use for coroutines. */ struct game_arguments ga; int g, num_games; if ( argc < 2 || argc > 3 || sscanf(argv[1], "%lf", &ga.pa.in.initialize.seed) != 1 || ga.pa.in.initialize.seed <= 0 || argc < 3 ? (num_games= 1, 0) : sscanf(argv[2], "%d", &num_games) != 1 ) { printf("Usage: %s [ ]\n", argv[0]); return EXIT_FAILURE; } /* Create coroutines. */ ga.pco= safe_co_create((CO_FUNC_T)prng_coroutine, &ga.pa, stksz); ga.qco= safe_co_create((CO_FUNC_T)quizmaster_coroutine, &ga.qa, stksz); ga.cco= safe_co_create((CO_FUNC_T)candidate_coroutine, &ga.ca, stksz); /* Initialize random number generator. */ co_call(ga.pco); /* Coroutine initializes itself using the seed value. */ /* Run the requested number of games. */ for (g= 1; g <= num_games; ++g) { if (g > 1) (void)putchar('\n'); (void)printf("Playing game %d.\n", g); play_game(&ga); /* Play a single game. */ } /* Abort and destroy all coroutines. */ co_delete(ga.cco); co_delete(ga.qco); co_delete(ga.pco); return EXIT_SUCCESS; }