SOURCE FILE: conc.cpp



#include <iostream>
#include <sched.h>
#include <time.h>
#include <pthread.h>
#include "sem.h"

using namespace std ;

/* Global declarations: These are shared by all threads. */

extern sim_semaphore create_sim_sem(int) ;
extern void wait_sem (sim_semaphore) ;
extern void signal_sem (sim_semaphore) ;

      /* When debugging, we need to use this lock to get intelligible
         printouts of what the various threads are doing.  Warning:  Don't
         change how this is done until you've thought a WHOLE lot about the
         consequences. */

pthread_mutex_t stdoutLock ;

int checking ;   /* JUST FOR DEBUGGING */

      /* SHARED VARIABLE: number of child threads active */ 
int             c_count;                

sim_semaphore   countSem ;      /* For synchronization. */
sim_semaphore finished ;        /* To provide a condition for the 
                                   mother to wait on. */

     /* global variable to represent threads created dynamically. */
pthread_t thr[16] ; 

/* A structured data type with two int fields
   to represent the identity and the workload
   of a child thread. . */

struct attribType
{
  int id ;
  int value ;
};

extern long random(void);

/* ################################################## */
/*                         init                       */
/* ################################################## */
void init() 
{ 

  if ( 0!=pthread_mutex_init(&stdoutLock, NULL) )
  {  cout << "MUTEX INITIALIZATION FAILURE!" << endl ;
     exit(-1) ;}

            /* counter:  current number of children. */

  c_count = 0; 
  countSem = create_sim_sem(1) ;  /* initialize the semaphore
                                     for access to counter */
  finished = create_sim_sem(0) ;  /* initialize "finished" to 0 
                                     to force a block if no signal
                                     has been done first. */

       /* initialize random number generator */ 
  srandom(time((time_t *) 0));

}

/* ################################################## */
/*                        child                       */
/* ################################################## */

/* Each child simulates doing work. It does this by iterating a
   loop body "value" times.  "Value" is a number stored in the
   child's parameter. The child offers to yield the processor on
   each iteration.  This increases the probability that
   iterations of this child will be interleaved with iterations
   of other child threads.  When the child finishes, it announces
   that fact, after it first decrements the global c_count
   variable.  */

void * child(void * valPtr) 
{
  int value =  ( (attribType *) (valPtr) )->value ;
  int id = ( (attribType *) (valPtr) )->id ;
  int  i, temp;

  for (i = 0; i < value; i += 1) 
  {  sched_yield();
  }

  pthread_mutex_lock(&stdoutLock) ;
  cout << "Child #" << id << " has finished " ;
  if   (id%2) cout << "his"; else cout << "her";
  cout << " work of " << value << " cycle" ;
  if    (1 != value) cout << "s";
  cout << "." << endl ;
  pthread_mutex_unlock(&stdoutLock) ;
        
        /* If any thread wants to access the c_count variable, it
           first waits on countSem to insure exclusive access. */

  wait_sem(countSem); 
  c_count -= 1 ;                        
        
          /* signal mother to exit if last thread is done */ 
  if (c_count == 0) 
  {
       signal_sem(finished);    
  }

   signal_sem(countSem); 

   pthread_mutex_lock(&stdoutLock) ;
   cout << "Child #" << id << " EXITS." << endl ;
   pthread_mutex_unlock(&stdoutLock) ;

   pthread_exit ((void *)0) ;
}

/* ################################################## */
/*                       mother                       */
/* ################################################## */

/* The mother spawns a given number of children and then waits
   for them all to finish.  */

void mother(int nchildren) 
{ 
  int i, j, cnt_cpy, m_delay ;
  
  attribType * valPtr ;

  pthread_mutex_lock(&stdoutLock) ;  
  cout << "Mother will spawn " 
  << nchildren << " child" ;
  if (nchildren != 1)  cout << "ren"; 
  cout << "." << endl ;
  pthread_mutex_unlock(&stdoutLock) ;
  
  for (i = 0; i < nchildren; i += 1) 
  {
        /* Increment c_count with the creation of each child thread.  */
    wait_sem(countSem); 
    c_count += 1; 
    signal_sem(countSem); 

        /* create a random workload to assign to a child */
    valPtr = new attribType ; 
    valPtr->value = random()%100 ;
    valPtr->id = i ; // record the child's sequence number

        /* Fork the child and detach it, since the mother never joins it. */
    if ( 0!=pthread_create( &thr[i], NULL, child, (void *) valPtr ) )
    {  cout << "THREAD CREATION FAILURE!" << endl ;
       exit(-1) ; }
     
    if (0!=pthread_detach(thr[i]))
    {  cout << "THREAD DETACHMENT FAILURE!" << endl ;
       exit(-1) ;}

    m_delay = (int) random()%10 ;

    for (j=0; j<m_delay; j++) sched_yield();
  } 
        /* Mother thread loops waiting on the semaphore
           "finished".  When a child signals on "finished", the
           mother thread tests the c_count for a value of zero.  */
  do   
  {
    wait_sem(countSem) ;
    cnt_cpy = c_count ;
    signal_sem(countSem) ;

    if (cnt_cpy != 0)
    {
      wait_sem(finished) ;
    }
  }
  while (cnt_cpy != 0) ;

  pthread_mutex_lock(&stdoutLock) ;
  if (0 != nchildren)
  {
      cout << "Mother now knows that " ;
      if   (1 == nchildren)  cout << "the child has finished." << endl ; 
      else if   (2 == nchildren) 
                cout << "both children have finished." << endl ; 
           else cout << "all " << nchildren 
                << " children have finished." << endl ; 
  }

  cout << "Mother now EXITS." << endl ;
  pthread_mutex_unlock(&stdoutLock) ; 
  pthread_exit ((void *) 0) ;
}

/* ################################################## */
/*                         main                       */
/* ################################################## */

int main() 
{ 
  init(); 
    
      /* create mother thread and up to 15 children */ 

  mother((int) random() % 16);  
  return 0 ;
}