/* 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 { /* Initialize the pseudo random number generator. */ union { struct { double seed; /* For seeding the generator. */ } in; } initialize; /* For requesting the next random number to be generated. */ union { struct { int min; /* Smallest allowed value for random number. */ int max; /* Largest allowed value for random number. */ } in; struct { int rnd; /* The generated pseudo random number. */ } out; } generate; }; /* Constants used by guesser/solver coroutines. */ enum {TOO_SMALL, JUST_RIGHT, TOO_LARGE}; /* Parameter passing structure for quiz master coroutine. */ union quizmaster_arguments { /* Initialization phase. */ union { struct { coroutine_t prng; /* Random number generator coroutine. */ union prng_arguments *prng_arguments; coroutine_t candidate; /* Quiz candidate coroutine. */ union candidate_arguments *candidate_arguments; } in; } initialize; union { struct { /* The guess from the candidate. */ int guess; } in; } probe; }; /* Parameter passing structure for quiz candidate coroutine. */ union candidate_arguments { /* Initialization phase. */ union { struct { int smallest; /* Lower bound for number to be guessed. */ int largest; /* Upper bound for number to be guessed. */ } in; } initialize; union { struct { int guess; /* Our guess. */ } out; struct { /* What the quiz master tells us. */ int test_result; } in; } evaluate; }; /* 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->initialize.in.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->generate.in.min; max= xch->generate.in.max; xch->generate.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->initialize.in.prng; union prng_arguments *pa= xch->initialize.in.prng_arguments; coroutine_t candidate= xch->initialize.in.candidate; union candidate_arguments *ca= xch->initialize.in.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->generate.in.min= lower; pa->generate.in.max= upper; co_call(prng); secret= pa->generate.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->initialize.in.smallest= lower; ca->initialize.in.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->evaluate.out.guess == secret) { (void)printf( "A: Congratulations - %d is the secret number!\n", secret ); ca->evaluate.in.test_result= JUST_RIGHT; co_call(candidate); break; /* Coroutine will finish execution. */ } else if (ca->evaluate.out.guess < secret) { (void)printf("A: %d is too small!\n", ca->evaluate.out.guess); ca->evaluate.in.test_result= TOO_SMALL; } else { (void)printf("A: %d is too large!\n", ca->evaluate.out.guess); ca->evaluate.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->initialize.in.smallest; int upper= xch->initialize.in.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->evaluate.out.guess= guess; co_resume(); /* Evaluate our query. */ switch (xch->evaluate.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.initialize.in.prng= ga->pco; ga->qa.initialize.in.prng_arguments= &ga->pa; ga->qa.initialize.in.candidate= ga->cco; ga->qa.initialize.in.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.initialize.in.seed) != 1 || ga.pa.initialize.in.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; }