/* Written in the misty eons of time by Henry Walker
 * Revised:
 *   Janet Davis, September 20, 2006
 *   Jerod Weinman, June 24, 2008
 * signal-1.c:
 * This program uses one process to time work done in another.
 */

#include <sys/types.h> /* file of data types needed for many compilers     */
#include <unistd.h>    /* needed for fork, getpid procedures               */
#include <signal.h>    /* needed for signaling between parent and child    */
#include <stdio.h>     /* needed for printing                              */
#include <stdlib.h>    /* for random number generator for quicksort array  */
#include "timers.h"    /* convenient packaged timing routines              */

/* declare flag variables common to both processes */
static int child_may_start_work = 0;
static int child_done = 0;

/* Handles the signal to start work in the child process.
 * Parameters:
 *   signo, the signal number
 * Preconditions:
 *   signo is SIGUSR1
 * Postconditions:
 *   child_may_start_work is 1
 */
static void signal_start_handler (int signo) 
{
     if (signo == SIGUSR1) {
          printf( "Starting work in child process.\n" );
          child_may_start_work = 1;
          return;
     }
     else 
     {
          printf( "Error handling signal to child process.\n" );
          exit( 1 );
     }
}

/* Handles the signal that the child process is done computing.
 *
 * Parameters:
 *   signo, the signal number
 *
 * Preconditions:
 *   signo is SIGUSR2
 *
 * Postconditions:
 *   child_done is 1
 */
static void signal_child_done (int signo)
{
     if (signo == SIGUSR2) {
          printf( "Parent received signal that child is done.\n" );
          child_done = 1;
          return;
     } 
     else 
     {
          printf( "Error handling signal to parent process.\n" );
          exit (1);
     }
}

/* The child will perform quicksort on ARRAY_SIZE elements as work to
 * be timed. TIMER_SEC should be sufficiently large to avoid the timer
 * expiring and causing a SIGALRM. */
#define ARRAY_SIZE 1000000
#define TIMER_SEC 9

void quicksort(int a[], int n);

int main (void) 
{
     pid_t pid;             /* variable to record process id of child */

     printf( "Starting program in which parent prcess "
             "times work done by child process.\n\n" );

     /* register the signal handlers for two signals */
     if (signal(SIGUSR1, signal_start_handler) == SIG_ERR)
          fprintf( stderr, "Initial setup error: "
                  "unable to create handler for SIGUSR1.\n" );

     if (signal(SIGUSR2, signal_child_done) == SIG_ERR)
          fprintf( stderr, "Initial setup error: "
                   "unable to create handler for SIGUSR2.\n" );

     /* Spawn child process */
     pid = fork();             /* create new process */

     if (-1 == pid)            /* check for error in spawning child process */
     {          
          perror( "Error in fork" );  
          exit (1);
     }

     if (0 == pid)             /* check if this is the new child process */
     {
          /* processing for child */
          printf( "Starting child process...\n" );
          printf( "Child pid: %d; parent pid: %d\n", getpid(), getppid() );

          /* busy-wait until SIGUSR1 signal tells child to start */
          while( !child_may_start_work )
               /* do nothing */ ;

          /* meaningful work goes here ... */

          /* set up array for quicksort */
          int a[ARRAY_SIZE];
          int i;
          for (i = 0; i < ARRAY_SIZE; i++)
               a[i] = rand();

          quicksort( a, ARRAY_SIZE );
       
          /* signal parent process when done */
          if (-1 == kill( getppid(), SIGUSR2 ) )
															perror("Error signaling parent");

          printf ("Exiting child process.\n");

          exit (0);

     } else { 
          
          /* processing for parent */
          printf ("Continuing processing in parent process.\n");

          /* set interval timer */
          if (-1 == setTimer(ITIMER_REAL,TIMER_SEC,0))
										{
															perror("Error setting timer");
															exit (1);
										}

          /* signal child to start with SIGUSR1 */
          if (-1 == kill( pid, SIGUSR1 ))
										{
															perror("Error signaling child");
															exit (1);
										}

          /* busy-wait until SIGUSR2 signal indicates child's work done */
          while( !child_done )
               /* do nothing */ ;
										
										float elapsedTime = getTime(ITIMER_REAL,TIMER_SEC);

										if (-1 == elapsedTime)
										{
															perror("Error getting time");
															exit (1);
										}

          /* Print the timer value for real (or wall-clock) time. */
          printf ("Elapsed time is %f seconds\n", elapsedTime);


    /* end parent process */
    printf ("Exiting parent process.\n");
    exit (0);
  }
}
