SOURCE FILE: conc.cpp
#include <iostream>
#include <sched.h>
#include <time.h>
#include <pthread.h>
#include "sem.h"
using namespace std ;
/* Global declarations: These are shared by all threads. */
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] ;
/* A structured data type with two int fields
to represent the identity and the workload
of a child thread. . */
struct attribType
{
int id ;
int value ;
};
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 simulates doing work. It does this by iterating a
loop body "value" times. "Value" is a number stored in the
child's parameter. The child offers to yield the processor on
each iteration. This increases the probability that
iterations of this child will be interleaved with iterations
of other child threads. When the child finishes, it announces
that fact, after it first decrements the global c_count
variable. */
void * child(void * valPtr)
{
int value = ( (attribType *) (valPtr) )->value ;
int id = ( (attribType *) (valPtr) )->id ;
int i, temp;
for (i = 0; i < value; i += 1)
{ sched_yield();
}
pthread_mutex_lock(&stdoutLock) ;
cout << "Child #" << id << " has finished " ;
if (id%2) cout << "his"; else cout << "her";
cout << " work of " << value << " cycle" ;
if (1 != value) 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 #" << id << " 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 ;
attribType * valPtr ;
pthread_mutex_lock(&stdoutLock) ;
cout << "Mother will spawn "
<< nchildren << " child" ;
if (nchildren != 1) cout << "ren";
cout << "." << endl ;
pthread_mutex_unlock(&stdoutLock) ;
for (i = 0; i < nchildren; i += 1)
{
/* Increment c_count with the creation of each child thread. */
wait_sem(countSem);
c_count += 1;
signal_sem(countSem);
/* create a random workload to assign to a child */
valPtr = new attribType ;
valPtr->value = random()%100 ;
valPtr->id = i ; // record the child's sequence number
/* Fork the child and detach it, since the mother never joins it. */
if ( 0!=pthread_create( &thr[i], NULL, child, (void *) valPtr ) )
{ cout << "THREAD CREATION FAILURE!" << endl ;
exit(-1) ; }
if (0!=pthread_detach(thr[i]))
{ cout << "THREAD DETACHMENT FAILURE!" << endl ;
exit(-1) ;}
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 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 now EXITS." << endl ;
pthread_mutex_unlock(&stdoutLock) ;
pthread_exit ((void *) 0) ;
}
/* ################################################## */
/* main */
/* ################################################## */
int main()
{
init();
/* create mother thread and up to 15 children */
mother((int) random() % 16);
return 0 ;
}