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