SOURCE FILE: conc.strp.cpp



/* This is a version of the conc.cpp program.  It's really the
   same program, except with most of the delay code and
   diagnotics code removed.  The aim is to make it easier on the
   student trying to do a first reading of the program.  */

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

using namespace std ;

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] ; 

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 just counts up to its argument, yielding the
   processor on each iteration.  When it's finished, it
   decrements the global c_count and signals that it's done.  */

void * child(void * N) 
{
  int n = (int) N, i, temp;

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

  pthread_mutex_lock(&stdoutLock) ;
  cout << "Child #" << pthread_self() << " has finished " ;
  if   (pthread_self()%2) cout << "his"; else cout << "her";
  cout << " work of " << n << " cycle" ;
  if    (1 != n) 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 #" << pthread_self() << " 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 ;

  pthread_mutex_lock(&stdoutLock) ;  
  cout << "Mother #" << pthread_self() << " will spawn " 
  << nchildren << " child" ;
  if (nchildren != 1)  cout << "ren"; 
  cout << "." << endl ;
  pthread_mutex_unlock(&stdoutLock) ;
  
  for (i = 0; i < nchildren; i += 1) 
  {
    wait_sem(countSem); 

        /* Increment c_count with the creation of each child thread.  */
    c_count += 1; 

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

    signal_sem(countSem); 

    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 #" << pthread_self() << " 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 #" << pthread_self() << " now EXITS." << endl ;
  pthread_mutex_unlock(&stdoutLock) ; 
  pthread_exit ((void *) 0) ;
}

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

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

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