#ifdef AIX_PROD
/* "@(#)43  1.6  src/examples/pubsex/prime/prime.c, examples.src, os2dce21.dss, 960602a.1 1/12/96 15:21:23" */
/*
 *   COMPONENT_NAME: examples.src
 *
 *   FUNCTIONS: check
 *              main
 *              prime_search
 *
 *   ORIGINS: 72
 *
 */
#endif /* AIX_PROD */

#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
/*
 * Constants used by the example.
 */
#define     workers     5           /* Threads to perform prime check  */
#define     request     110         /* Number of primes to find        */
/*
 * Macros
 */
#define check(status,string)     if (status == -1) perror (string)
/*
 * Global data
 */
pthread_mutex_t prime_list;     /* Mutex for use in accessing the prime     */
pthread_mutex_t current_mutex;  /* Mutex associated with current number     */
pthread_mutex_t cond_mutex;     /* Mutex used for ensuring CV integrity     */
pthread_cond_t  cond_var;       /* Condition variable for thread start      */
int             current_num= -1;/* Next number to be checked, start odd     */
int             thread_hold= 1; /* Number associated with condition state   */
int             count=0;        /* Count of prime numbers - index to primes */
int             primes[request];/* Store prime numbers - synchronize access */
pthread_t       threads[workers];/* Array of worker threads                 */

/*
 * Worker thread routine.
 *
 * Worker threads start with this routine, which begins with a condition
 * wait designed to synchronize the workers and the parent.  Each worker
 * thread then takes a turn taking a number for which it will determine
 * whether or not it is prime.
 *
 */
void
prime_search (pthread_addr_t arg)
{
  int     numerator;              /* Used for determining primeness   */
  int     denominator;            /* Used for determining primeness   */
  int     cut_off;                /* Number being checked div 2       */
  int     notifiee;               /* Used during a cancelation        */
  int     prime;                  /* Flag used to indicate primeness  */
  int     my_number;              /* Worker thread identifier         */
  int     status;                 /* Hold status from pthread calls   */
  int     not_done=1;             /* Work loop predicate              */

  my_number = (int)arg;

  /*
   * Synchronize threads and the parent using a condition variable, for
   * which the predicate (thread_hold) will be set by the parent.
  */
  status = pthread_mutex_lock (&cond_mutex);
  check(status,"1:Mutex_lock bad status\n");

  while (thread_hold) {
      status = pthread_cond_wait (&cond_var, &cond_mutex);
      check(status,"3:Cond_wait bad status\n");
  }

  status = pthread_mutex_unlock (&cond_mutex);
  check(status,"4:Mutex_unlock bad status\n");

  /*
   * Perform checks on ever larger integers until the requested
   * number of primes is found.
  */
  while (not_done) {

      /* cancelation point */
      pthread_testcancel ();


      /* Get next integer to be checked */
      status = pthread_mutex_lock (&current_mutex);
      check(status,"6:Mutex_lock bad status\n");

      current_num = current_num + 2;              /* Skip even numbers */
      numerator = current_num;

      status = pthread_mutex_unlock (&current_mutex);
      check(status,"9:Mutex_unlock bad status\n");

      /* Only need to divide in half of number to verify not prime */
      cut_off = numerator/2 + 1;
      prime = 1;

      /* Check for prime; exit if something evenly divides */
      for (denominator = 2; ((denominator < cut_off) && (prime));
                                                      denominator++) {
          prime = numerator % denominator;
      }
      if (prime != 0) {

          /* Explicitly turn off all cancels */
          pthread_setcancel(CANCEL_OFF);

          /*
           * Lock a mutex and add this prime number to the list. Also,
           * if this fulfills the request, cancel all other threads.
           */
          status = pthread_mutex_lock (&prime_list);
          check(status,"10:Mutex_lock bad status\n");

          if (count < request)  {
              primes[count] = numerator;
              count++;
              }
          else if (count == request) {
              not_done = 0;
              count++;
              for (notifiee = 0; notifiee < workers; notifiee++) {
                  if (notifiee != my_number) {
                      status = pthread_cancel ( threads[notifiee] );
                      check(status,"12:Cancel bad status\n");
                      }
                  }
              }

          status = pthread_mutex_unlock (&prime_list);
          check(status,"13:Mutex_unlock bad status\n");

          /* Reenable cancels */
          pthread_setcancel(CANCEL_ON);
          }
      pthread_testcancel ();
  }

  pthread_exit ((pthread_addr_t)my_number);
}

void main(void)
{
  int     worker_num;     /* Counter used when indexing workers   */
  int     exit_value;     /* Individual worker's return status    */
  int     list;           /* Used to print list of found primes   */
  int     status;         /* Hold status from pthread calls       */
  int     index1;         /* Used in sorting prime numbers        */
  int     index2;         /* Used in sorting prime numbers        */
  int     temp;           /* Used in a swap; part of sort         */

  /*
  * Create mutexes
  */
  status = pthread_mutex_init (&prime_list, pthread_mutexattr_default);
  check(status,"15:Mutex_init bad status\n");
  status = pthread_mutex_init (&cond_mutex, pthread_mutexattr_default);
  check(status,"16:Mutex_init bad status\n");
  status = pthread_mutex_init (&current_mutex, pthread_mutexattr_default);
  check(status,"17:Mutex_init bad status\n");

  /*
   * Create condition variable
   */
  status = pthread_cond_init (&cond_var, pthread_condattr_default);
  check(status,"45:Cond_init bad status\n");

  /*
   * Create the worker threads.
   */
  for (worker_num = 0; worker_num < workers; worker_num++) {
      status = pthread_create (
          &threads[worker_num],
          pthread_attr_default,
          (pthread_startroutine_t)prime_search,
          (pthread_addr_t)worker_num);
      check(status,"19:Pthread_create bad status\n");
  }

  /*
   * Set the predicate thread_hold to zero, and broadcast on the
   * condition variable that the worker threads may proceed.
   */
  status = pthread_mutex_lock (&cond_mutex);
  check(status,"20:Mutex_lock bad status\n");

  thread_hold = 0;

  status = pthread_cond_broadcast (&cond_var);
  check(status,"20.5:cond_broadcast bad status");

  status = pthread_mutex_unlock (&cond_mutex);
  check(status,"21:Mutex_unlock bad status\n");

  /*
   * Join each of the worker threads in order to obtain their
   * summation totals, and to ensure each has completed
   * successfully.
   *
   * Mark thread storage free to be reclaimed upon termination by
   * detaching it.
  */
  for (worker_num = 0; worker_num < workers; worker_num++) {

      status = pthread_join (
          threads[worker_num],
          (void *)&exit_value );
      check(status,"23:Pthread_join bad status\n");

      if (exit_value == worker_num) printf("thread terminated normally\n");

      status = pthread_detach ( &threads[worker_num] );
      check(status,"25:Pthread_detach bad status\n");
  }

   /*
  * Take the list of prime numbers found by the worker threads and
  * sort them from lowest value to highest.  The worker threads work
  * concurrently; there is no guarantee that the prime numbers
  * will be found in order. Therefore, a sort is performed.
  */
  for (index1 = 1; (index1 < request); index1++) {
      for (index2 = 0; index2 < index1; index2++) {
          if (primes[index1] < primes[index2]) {
              temp = primes[index2];
              primes[index2] = primes[index1];
              primes[index1] = temp;
          }
      }
  }

  /*
   * Print out the list of prime numbers that the worker threads
   * found.
   */
  printf ("The list of %d primes follows:\n", request);
  printf("%d ",primes[0]);

  for (list = 1; list < request; list += 15) {
    int k;
    for (k = list; (k < list + 15) && (k < request); k++) {
        printf ("%d ", primes[k]);
    }
    printf ("\n");
  }
  printf ("\n");
}
