#include <stdio.h> #include <string.h> #include <stdlib.h> #include <unistd.h> #include <poll.h> #include <sys/types.h> #include <sys/stat.h> int next_free_pipe = 0; FILE *to_app[3], *from_app[3]; // application-specific data: arguments and results int arg1[3], arg2[3], result[3]; // The actual application is placed here for now but could just as easily be // a complete external program invoked by 'ssh'. It receives its parameters // and returns its results on stdin and stdout. Simplest unix paradigm possible. int app_global_evaluation_move_finder (int argc, char **argv) { // just like 'main()' int i, j; // This is for use with a scrabble move finder which takes a board // description and other parameters on stdin, and writes its results // to stdout - this trivial 'add two numbers' is just a placeholder for // debugging the method... // NOTE: the application should not produce *any* output until it is ready // to produce all the output. // The ultimate intention is to invoke the move finder via calls to 'ssh' // and have multiple copies of it execute in parallel on other systems... fscanf (stdin, "%d", &i); fscanf (stdin, "%d", &j); sleep (i); // simulate a lot of computation... fprintf (stdout, "%d\n", i + j); exit (EXIT_SUCCESS); return (EXIT_FAILURE); } // These two procedures (app_add and app_collect_results) have to be in the calling // program and need to know the specifics of how parameters are passed in to the app // and returned from the app via stdio. Other than these interface procedures, the // rest of this framework is more or less independent of the details of the application. int new_process (void); void app_add (int a, int b) { int instance = new_process (); arg1[instance] = a; arg2[instance] = b; // save parameters to be used once result is calcuated // Application-specific transmission of parameters to app: fprintf (to_app[instance], "%d\n%d\n", a, b); fflush (to_app[instance]); return; } void cleanup (int pipeno); void app_collect_result (int pipeno) { // read the whole stream unconditionally // Application-specific collection of results from app: fscanf (from_app[pipeno], "%d", &result[pipeno]); fprintf (stderr, "%d + %d = %d\n", arg1[pipeno], arg2[pipeno], result[pipeno]); // or save and display later? fflush (stderr); cleanup (pipeno); } char *fifo[3]; char *progname; int new_process (void) { static char command[1024]; int status; fifo[next_free_pipe] = tempnam ("/tmp", "fifo_"); if (fifo[next_free_pipe] == NULL) { perror ("tempnam"); exit (EXIT_FAILURE); } status = mkfifo (fifo[next_free_pipe], S_IWUSR | S_IRUSR /* | S_IRGRP | S_IROTH */ ); if (status == -1) { perror ("mkfifo"); exit (EXIT_FAILURE); } sprintf (command, "%s app_main < %s", progname, fifo[next_free_pipe]); from_app[next_free_pipe] = popen (command, "r"); if (from_app[next_free_pipe] == NULL) { perror ("popen"); exit (EXIT_FAILURE); } setvbuf (from_app[next_free_pipe], 0, _IOLBF, 0); to_app[next_free_pipe] = fopen (fifo[next_free_pipe], "w"); setvbuf (to_app[next_free_pipe], 0, _IOLBF, 0); return next_free_pipe++; } int data_available (int pipeno) { // poll for first piece of output, struct pollfd fds[1]; if (from_app[pipeno]) { fds[0].fd = fileno (from_app[pipeno]); fds[0].events = POLLIN; fds[0].revents = 0; return ((poll (fds, 1, 0) > 0) && (fds[0].revents & POLLIN)); } else return (0 != 0); } void cleanup (int pipeno) { fclose (to_app[pipeno]); pclose (from_app[pipeno]); from_app[pipeno] = NULL; // mark stream so it will not be read from again remove (fifo[pipeno]); free (fifo[pipeno]); } void collect_all_results (void) { int pipeno, reaped = 0; while (reaped < next_free_pipe) { // wait for any process to have data, then gobble it. for (pipeno = 0; pipeno < next_free_pipe; pipeno++) { if (data_available (pipeno)) { // could handle result here or store and handle all results at end. app_collect_result (pipeno); reaped++; } } usleep (10); // Centisecond sleep not significant in a table game } } int main (int argc, char **argv) { if ((argc == 2) && (strcmp (argv[1], "app_main") == 0)) { exit (app_global_evaluation_move_finder (argc, argv)); } else if (argc == 1) { int i; /* test code - send multiple pairs of ints, from array, save results in indexed slots have the app do a random wait (within bounds) to simulate cpu expenditure be prepared for the case where not complete in time? have app send intermediate results if useful? */ for (i = 0; i < 2; i++) { // test that we can perform the whole sequence repeatedly next_free_pipe = 0; // MUST be reset for each cycle progname = argv[0]; app_add (2, 9); app_add (5, 3); app_add (1, 4); collect_all_results (); // could code this to EITHER handle results // incrementally or all at end depending on program logic } exit (EXIT_SUCCESS); } else { fprintf (stderr, "syntax: apptest\n"); exit (EXIT_FAILURE); } return (EXIT_FAILURE); }