SOURCE FILE: conc.cpp


#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() 
{ 

  checking = 1 ;    /* JUST FOR DEBUGGING: change to
                       1 if you want messages. */
  		       
  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 count and
   signals that it's done.  */

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

  if  (checking)
  {  pthread_mutex_lock(&stdoutLock) ;
     cout << "Child #" << pthread_self() << " is BORN  with workload = "
     << n << "." << endl ;
     pthread_mutex_unlock(&stdoutLock) ; }

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

     if  (checking)
     {  pthread_mutex_lock(&stdoutLock) ;
        cout << "Child #" << pthread_self() << " executes work unit #"
        << i << "." << endl ;
	pthread_mutex_unlock(&stdoutLock) ; }
  }

  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 count variable, it first waits
	   on countSem to insure exclusive access. */
 
  if (checking)
  { pthread_mutex_lock(&stdoutLock) ;
    cout << "Child #" << pthread_self() 
    << " will wait on semaphore \"countSem.\"" << endl ;
    pthread_mutex_unlock(&stdoutLock) ; }

  wait_sem(countSem); 
  c_count -= 1 ;			
  
     /* Replace the previous line with the stuff below if you want to make
	sure the decrementing is NOT done with just one instruction. */

/*	temp = c_count;
	temp = temp - 1;
        sched_yield();
	c_count = temp ;
*/

  if  (checking)
  {  pthread_mutex_lock(&stdoutLock) ;
     cout << "Child #" << pthread_self()  
     << " decrements shared variable \"c_count\" to: " << c_count << "." << endl ;
     pthread_mutex_unlock(&stdoutLock) ; }
	
          /* signal mother to exit if last thread is done */ 
  if (c_count == 0) 
  {
    if (checking)
    {  pthread_mutex_lock(&stdoutLock) ;
       cout << "Child #" << pthread_self() 
       << " sees " ;  
       if   (!(pthread_self()%2)) cout << "s";
       cout << "he may be THE LAST child." << endl << "\t" ;
       if   (!(pthread_self()%2)) cout << "Sh"; else cout << "H" ;
       cout << "e will now signal on semaphore \"finished.\"" << endl ;
       pthread_mutex_unlock(&stdoutLock) ;  }

       signal_sem(finished);	

  }
  else
    if (checking)
    {  pthread_mutex_lock(&stdoutLock) ;
       cout << "Child #" << pthread_self() << " sees that " ;
       if (!(pthread_self()%2)) cout << "s" ;
       cout << "he is not the last child to exit, and " ;
       if (!(pthread_self()%2)) cout << "s" ;
       cout << "he decides" << endl << "\t not to signal on semaphore"
       << " \"finished.\"" << endl ;
       pthread_mutex_unlock(&stdoutLock) ; }

  if (checking)
  { pthread_mutex_lock(&stdoutLock) ;
    cout << "Child #" << pthread_self() 
    << " will signal on semaphore \"countSem.\"" << endl ;
    pthread_mutex_unlock(&stdoutLock) ; }

  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) 
  {
    if (checking)
    { pthread_mutex_lock(&stdoutLock) ;
      cout << "Mother #" << pthread_self() 
      << " will wait on semaphore \"countSem.\"" << endl ;
      pthread_mutex_unlock(&stdoutLock) ; }

    wait_sem(countSem); 

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

    if (checking)
    {  pthread_mutex_lock(&stdoutLock) ;
       cout << "Mother #" << pthread_self() << " increments shared variable "
       << "\"c_count\" to: " << c_count << endl ;
       pthread_mutex_unlock(&stdoutLock) ;  }

        /* 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) ;}

    if (checking)
    {  pthread_mutex_lock(&stdoutLock) ;
       cout << "Mother #" << pthread_self() << " has SPAWNED a child." << endl;
       pthread_mutex_unlock (&stdoutLock) ;   }

   if (checking)
   { pthread_mutex_lock(&stdoutLock) ;
     cout << "Mother #" << pthread_self() 
     << " will signal on semaphore \"countSem.\"" << endl ;
     pthread_mutex_unlock(&stdoutLock) ; }

    signal_sem(countSem); 

    m_delay = (int) random()%10 ;

    if (checking)
    { pthread_mutex_lock(&stdoutLock) ;
      cout << "Mother #" << pthread_self() << " decides to delay for "
      << m_delay << " calls to sched_yield() " << "." << endl ;
      pthread_mutex_unlock(&stdoutLock) ; }

    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   
  {
    if (checking)
    { pthread_mutex_lock(&stdoutLock) ;
      cout << "Mother #" << pthread_self() 
      << " will check to see if the c_count is zero."  << endl ;
      pthread_mutex_unlock(&stdoutLock) ; }

   if (checking)
   { pthread_mutex_lock(&stdoutLock) ;
     cout << "Mother #" << pthread_self() 
     << " will wait on semaphore \"countSem.\"" << endl ;
     pthread_mutex_unlock(&stdoutLock) ; }

    wait_sem(countSem) ;
    cnt_cpy = c_count ;

   if (checking)
   { pthread_mutex_lock(&stdoutLock) ;
     cout << "Mother #" << pthread_self() 
     << " will signal on semaphore \"countSem.\"" << endl ;
     pthread_mutex_unlock(&stdoutLock) ; }

    signal_sem(countSem) ;

    if (cnt_cpy != 0)
    {

      if (checking)
      { pthread_mutex_lock(&stdoutLock) ;
        cout << "Mother #" << pthread_self() 
        << " will wait on semaphore \"finished.\"" << endl ;
	pthread_mutex_unlock(&stdoutLock) ; }

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