/* This program uses one process to time processing (quicksort) in another */

#include <sys/types.h> /* file of data types needed for many compilers     */
#include <unistd.h>    /* needed for fork, getpid procedures               */
#include <sys/time.h>  /* needed for timing structures and system calls    */
#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  */

/* timer variables for seconds and microseconds */
#define start_sec  9
#define start_usec 0
#define max_usec 1000000

/* declare variables common to the process environment */
static int signal_1_flag = 0;
static int signal_2_flag = 0;

static void signal_start_handler (int signo) {
  /* function to start processing in child by setting signal 1 flag*/
  if (signo == SIGUSR1) {
    printf ("Starting work in child process.\n");
    signal_1_flag = 1;
    return;
  }
  else {
    printf ("Error handling signal to child process.\n");
    exit (1);
  }
}

static void signal_compute_time (int signo) {
  /* function to start computation of elapsed time by setting signal 2 flag */
  if (signo == SIGUSR2) {
    printf ("Starting elapsed-time computation in parent.\n");
    signal_2_flag = 1;
    return;
  }
  else {
    printf ("Error handling signal to parent process.\n");
    exit (1);
  }
}

/* child will perform quicksort on n elements as work to be timed */
#define array_size 100000
void quicksort (int a[], int n);

int main (void) {
  pid_t pid;             /* variable to record process id of child */
  int temp;              /* temporary variable, not used for real data */
  struct itimerval ival;       /* variable to set timer values */
  struct itimerval real_time;  /* variable to retrieve times from timer */

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

  /* prepare the signal handlers for two signals */
  if (signal(SIGUSR1, signal_start_handler) == SIG_ERR)
    printf ("Initial setup error:  unable to create handler for SIGUSR1.\n");
  if (signal(SIGUSR2, signal_compute_time) == SIG_ERR)
    printf ("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());

       /* waste time until SIGUSR1 signal tells child to start */
       while (!signal_1_flag)  /* terminates when SIGUSR1 signal is received */
         temp = 1;

       /* meaningful work goes here */
       { /* set up array for quicksort */
         int i;
         int a[array_size];
         for (i = 0; i < array_size; i++)
           a[i] = rand ();
         quicksort (a, array_size);
       }
       /* signal parent process when done */
       kill (getppid(), SIGUSR2);
  
       /* end child process */
       printf ("Exiting child process\n");
       exit (0);
     } 
  else 
     { /* processing for parent */
       printf ("Continuing processing in parent process.\n");

       /* set interval timer */
       ival.it_interval.tv_sec  = start_sec;  /* initialize second field */
       ival.it_interval.tv_usec = start_usec; /* initialize microsecond field */
       ival.it_value.tv_sec     = start_sec;  /* reset second field */
       ival.it_value.tv_usec    = start_usec; /* reset microsecond field */
       setitimer(ITIMER_REAL, &ival, NULL);

       /* signal child  with SIGUSR1 */
       kill (pid, SIGUSR1); 

       /* waste time until SIGUSR2 signal indicates child's work done */
       while (!signal_2_flag) /* terminates when SIGUSR2 signal is received */
         temp = 1;  
       /* above loop will stop only when SIGUSR2 signal is received */

       getitimer(ITIMER_REAL, &real_time);
       printf ("Elapsed time is %d seconds, %d microseconds\n",
               start_sec  - real_time.it_value.tv_sec - 1,
               max_usec - real_time.it_value.tv_usec);
       /* end parent process */
       printf ("Exiting parent process\n");
       exit (0);
     }
}

/* functions for the quicksort */
void partition (int a[], int left, int right, int *middle) {
  /* places a[left] in position a[*middle], 
       permuting a[left]..a[right] so a[left]..a[middle-1] <= a[*middle]
       and a[*middle] <= a[(*middle)+1]..a[right]  */
  int l_spot = left+1;
  int r_spot = right;
  int temp;

  while (l_spot <= r_spot) {
    while ((l_spot <= r_spot)&&(a[left] <= a[r_spot]))
      r_spot--;
    while ((l_spot <= r_spot)&&(a[l_spot] <= a[left]))
      l_spot++;
    if (l_spot <= r_spot) {
      temp = a[r_spot];
      a[r_spot] = a[l_spot];
      a[l_spot] = temp;
    }
  }
  temp = a[r_spot];
  a[r_spot] = a[left];
  a[left] = temp;
  *middle = r_spot;
}


void quicksort (int a[], int n) {
  /* performs quicksort to order elements in array a */
  int mid;
  if (n > 1) {
    partition (a, 0, n-1, &mid);
    quicksort (a, mid);
    quicksort (a+mid+1,n - (mid+1));
  }
}

