SOURCE FILE: conc.cpp
#include <iostream.h>
#include <stdlib.h>
#include <sched.h>
#include <time.h>
#include <pthread.h>
#include "sem.h"
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 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. */
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);
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 = count;
temp = temp - 1;
sched_yield();
count = temp ;
*/
if (checking)
{ pthread_mutex_lock(&stdoutLock) ;
cout << "Child #" << pthread_self()
<< " decrements shared variable \"count\" to: " << count << "." << endl ;
pthread_mutex_unlock(&stdoutLock) ; }
/* signal mother to exit if last thread is done */
if (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 count with the creation of each child thread. */
count += 1;
if (checking)
{ pthread_mutex_lock(&stdoutLock) ;
cout << "Mother #" << pthread_self() << " increments shared variable "
<< "\"count\" to: " << 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 count
for a value of zero. */
do
{
if (checking)
{ pthread_mutex_lock(&stdoutLock) ;
cout << "Mother #" << pthread_self()
<< " will check to see if the 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 = 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 ;
}