Conc.c Example Program


Latest Revision: 11/08/99
 11/08/99 -- correction to the include directives
 11/08/99 -- added casts to parameters of cthread_fork()

#include <stdio.h> 
#include <mach/cthreads.h>
#include "sem.h"

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

int  checking ;      /* Set this to 1 if you want lots of
			"debug printf's". */
		     
mutex_t stdoutLock ;    /* 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. */

int 		count;		/* number of child threads active */ 
sim_semaphore   countSem ;      /* For synchronization. */
sim_semaphore finished ; 	/* To provide a condition for the 
				   mother to wait on. */

extern long random(void);

void init() 
{ 

  checking = 0 ;    /* JUST FOR DEBUGGING: change to
                       1 if you want messages. */
  		       
  stdoutLock = mutex_alloc() ;

  count = 0;        /* counter:  current number
                       of children. */ 
  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. */
  srandom(time((int *) 0));       /* initialize random
                                     number generator */ 
}

/*
 * 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(int n) 
   {
	int i, temp ;

	if  (checking)
	{ mutex_lock (stdoutLock) ;
	  printf("Child %d is BORN ", cthread_self() ); 
	  printf("with workload = %d\n", n);
	  fflush(stdout) ;
	  mutex_unlock(stdoutLock) ; }

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

		if  (checking)
		{ mutex_lock (stdoutLock) ;
		  printf("Child %d executes ", cthread_self() ); 
		  printf("work unit # %d\n", i);
		  fflush(stdout) ;
		  mutex_unlock(stdoutLock) ; }
        }

	mutex_lock (stdoutLock) ;
        printf("Child %d has finished his work ", cthread_self());
        printf("of %d cycles.\n", n);
	fflush(stdout) ;
	mutex_unlock(stdoutLock) ;
	
 	/*
	 * If any thread wants to access the count variable, it
         * first waits on countSem to insure exclusive access.
	 */ 
	if  (checking)
	{ mutex_lock (stdoutLock) ;
	  printf("Child %d is about to wait on ", cthread_self());
	  printf("semaphore <countSem>.\n" );
	  fflush(stdout) ;
	  mutex_unlock(stdoutLock) ; }

	wait_sem(countSem); 

	if  (checking)
	{ mutex_lock (stdoutLock) ;
	  printf("Child %d completes ", cthread_self());
	  printf("wait on semaphore <countSem>\n");
	  printf("and starts to decrement count.\n");
	  fflush(stdout) ;
	  mutex_unlock(stdoutLock) ; }

	count -= 1 ;			

	/* Replace the previous line with the stuff below if you want to
	   make sure the decrementing is NOT done as a single
	   instruction. */
/*	temp = count;
	temp = temp - 1;
	cthread_yield() ;
	count = temp ;
*/
	
	if  (checking)
	{ mutex_lock (stdoutLock) ;
	  printf("Child %d has finished ", cthread_self() );
	  printf("decrementing <count> to %d. \n", count);
	  fflush(stdout) ;
          mutex_unlock(stdoutLock) ; }

        /* Start of code to delay the thread so the others will queue
	   up waiting for it.  Uncomment to do debugging. */
/*
	for (i = 0; i < 10000; i += 1)

	{ cthread_yield(); }
*/
	/*  End of delay code. */

	/* signal mother to exit if last thread is done */ 
	if (count == 0) 
	   {
              if (checking)
	      { mutex_lock (stdoutLock) ;
	        printf("The LAST child %d will now ", cthread_self());
	        printf(" signal on semaphore <finished>.\n" );
	        fflush(stdout) ;
                mutex_unlock(stdoutLock) ;  }

	      signal_sem(finished);	

	      if (checking)
	      {  mutex_lock (stdoutLock) ;
	         printf("LAST Child %d has now ", cthread_self()) ;
                 printf(" completed his signal on semaphore <finished>.\n") ;
	         fflush(stdout) ;
                 mutex_unlock(stdoutLock) ;  }
           }
        else
	   if (checking)
	   { mutex_lock (stdoutLock) ;
	     printf("This Child %d sees that she is not ", cthread_self());
	     printf("the last child to exit, and she decides\n" );
	     printf("not to signal on semaphore <finished>.\n");
             fflush(stdout) ;
             mutex_unlock(stdoutLock) ; }

	if (checking)
	{ mutex_lock (stdoutLock) ;
	  printf("Child %d starts a signal on ", cthread_self());
	  printf("semaphore <countSem>. \n", cthread_self());
          fflush(stdout) ;
          mutex_unlock(stdoutLock) ; 	}

	signal_sem(countSem); 

	if (checking)
	{ mutex_lock (stdoutLock) ;
	  printf("Child %d completes signal ", cthread_self());
	  printf("on semaphore <countSem>. \n");
          fflush(stdout) ;
	  mutex_unlock(stdoutLock) ; 	}

	mutex_lock (stdoutLock) ;
	printf("Child %d EXITS.\n", cthread_self());
        fflush(stdout) ;
	mutex_unlock(stdoutLock) ;

	cthread_exit(0); 
}

/*
 * The mother spawns a given number of children and then waits 
 * for them all to finish.  
 */ 
void mother(int nchildren) 
{ 
  int i, cnt_cpy;

  mutex_lock (stdoutLock) ;  
  printf("Mother will spawn %d children.\n", nchildren); 
  fflush(stdout) ;
  mutex_unlock(stdoutLock) ;
  
  for (i = 1; i <= nchildren; i += 1) 
  {
    if (checking)
    { mutex_lock (stdoutLock) ;
      printf("Mother %d is about to wait ", cthread_self());
      printf("on semaphore <countSem>.\n");
      fflush(stdout) ;
      mutex_unlock(stdoutLock) ;   }

    wait_sem(countSem); 

    if (checking)
    { mutex_lock (stdoutLock) ;
      printf("Mother %d now completes her wait ", cthread_self());
      printf("on semaphore <countSem> \n" );
      fflush(stdout) ;
      mutex_unlock(stdoutLock) ;  }

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

    if (checking)
    { mutex_lock (stdoutLock) ;
      printf("Mother %d changes shared ", cthread_self() );
      printf("variable <count> to %d. \n", count);
      fflush(stdout) ;
      mutex_unlock(stdoutLock) ;  }

		/* Fork a child and detach it, since the mother never
 		   joins it individually.  */ 

    if   (checking)
         cthread_detach(cthread_fork((cthread_fn_t) child, 
                                      (any_t) (random() % 3)));
    else cthread_detach(cthread_fork((cthread_fn_t) child, 
                                      (any_t) (random() % 1000)));

    if (checking)
    { mutex_lock (stdoutLock) ;
      printf("Mother %d has SPAWNED a child. \n", cthread_self());
      printf("Mother %d starts a signal on ", cthread_self());      
      printf("semaphore <countSem>\n");
      fflush(stdout) ;
      mutex_unlock (stdoutLock) ;   }

    signal_sem(countSem); 

    if (checking)
    { mutex_lock (stdoutLock) ;
      printf("Mother %d completes the signal on ", cthread_self());
      printf(" semaphore <countSem>.\n" );
      fflush(stdout) ;
      mutex_unlock(stdoutLock) ; }
  } 
	/*
	 * Mother thread loops waiting on the semaphore "finished".  
	 * When a child signals on "finished", the mother thread  
 	 * tests the count for a value of zero.  
	 */ 
  do   
  {

    if (checking)
    { mutex_lock (stdoutLock) ;
      printf("Mother %d is about to wait on ", cthread_self());
      printf(" semaphore <countSem>.\n");
      printf("She will check to see if the count is zero.\n");
      fflush(stdout) ;
      mutex_unlock(stdoutLock) ; }

    wait_sem(countSem) ;

    if (checking)
    { mutex_lock (stdoutLock) ;
      printf("Mother %d completes her wait on ", cthread_self());
      printf("semaphore <countSem>. \n" );
      fflush(stdout) ;	  
      mutex_unlock(stdoutLock) ; }

    cnt_cpy = count ;

    if (checking)
    { mutex_lock (stdoutLock) ;
      printf("Mother %d starts a signal ", cthread_self());
      printf("on semaphore <countSem>. \n");
      fflush(stdout) ;
      mutex_unlock(stdoutLock) ; }

    signal_sem(countSem) ;

    if (checking)
    { mutex_lock (stdoutLock) ;
      printf("Mother %d completes signal on ", cthread_self());
      printf("semaphore <countSem>. \n");
      fflush(stdout) ;
      mutex_unlock(stdoutLock) ; }

    if (cnt_cpy != 0)
    {

      if (checking)
      { mutex_lock (stdoutLock) ;
        printf("Mother %d is about to wait ", cthread_self());
        printf("on semaphore <finished>.\n");
        fflush(stdout) ;
	mutex_unlock(stdoutLock) ; }

      wait_sem(finished) ;

      if (checking)
      { mutex_lock (stdoutLock) ;
        printf("Mother %d completes wait ", cthread_self());
        printf("on semaphore <finished>.\n");
        fflush(stdout) ;
        mutex_unlock(stdoutLock) ; }
    }
  }
  while (cnt_cpy != 0) ;
  mutex_lock (stdoutLock) ;
  printf("All %d children have finished.\n", nchildren); 
  printf("Mother %d now EXITS.\n", cthread_self() ); 
  fflush(stdout) ;	  	
  mutex_unlock(stdoutLock) ; 
  cthread_exit(0); 
}

main() 
{ 
  init(); 

  if (checking)
  { mutex_lock (stdoutLock) ;
    printf("Init done & count is %d. \n", count);    
    fflush(stdout) ;	  	
    mutex_unlock(stdoutLock) ;   }
    
      /* create mother thread and up to 15 children */ 
  mother((int) random() % 16);	
}