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