SOURCE FILE: dmnd.cpp
/*
------------1-----------------
--------0-------3-------------
------------2-----------------
This program synchronizes four threads with id numbers 0, 1, 2, and 3.
Each thread prints a message on the screen. We want the message from thread 0
to appear first. The messages of threads 1 and 2 must appear on the screen
after the message of thread 0, and before the message of thread 3.
Those are the only constraints. We don't want to require that thread 1
write its message before thread 2 writes its message, and we don't want to
require thread 2 to write its message before thread 1. We want the relative order
that threads 1 and 2 write their messages to be left to chance.
The program below synchronizes the threads so that they obey the constraints
described above. The program uses queueing semaphores to implement the
synchronization.
This program also has a mother thread that creates threads 0, 1, 2, and 3.
The program synchronizes the mother with the child threads, using a semaphore.
After it creates the child threads, the mother process blocks by waiting
on the semaphore. After all the threads have finished writing their messages,
thread 3 wakes up the mother process. The mother process in this program
has little to do after this. It just writes a message of its own, and then
terminates.
*/
/* Diamond Formation Program */
#include <iostream>
#include <sched.h>
#include <time.h>
#include <pthread.h>
#include "sem.h"
#define THREAD_COUNT 4
using namespace std;
extern sim_semaphore create_sim_sem(int) ;
extern void wait_sem (sim_semaphore) ;
extern void signal_sem (sim_semaphore) ;
/* For technical reasons, we use the pthread mutex below,
instead of a semaphore, to lock the screen for output. Don't
change this. */
pthread_mutex_t stdoutLock ;
/* Here declare whatever shared variables you need for
synchronization. Variables declared here will be visible
to (shared by) all the threads in the task. */
/* HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH */
sim_semaphore support_ready[THREAD_COUNT] ;
sim_semaphore finished ;
/* HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH */
/* These are global variable to represent threads created
dynamically. */
pthread_t thr[THREAD_COUNT] ;
/* This is included to facilitate adding random delays in the code
-- as a debugging aid. */
extern long random(void);
/* This can be changed to 1, but the resulting diagnostic output
will probably seem excessive. */
int checking = 0 ;
/* A data type - a struct (class) with an int field to represent
a thread ID. */
struct threadIdType
{
int id ;
};
/* ################################################## */
/* init */
/* ################################################## */
void init()
{
/* This code initializes special mutex lock for screen output.
Just leave this alone. */
if ( 0!=pthread_mutex_init(&stdoutLock, NULL) )
{ cout << "MUTEX INITIALIZATION FAILURE!" << endl ;
exit(-1) ;}
/* Here insert the code that you want to intialize
the shared variables that you are using
for synchronization. */
/* HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH */
finished = create_sim_sem(0) ;
int count ;
for (count = 0; count < THREAD_COUNT ; count++)
{
support_ready[count] = create_sim_sem(0) ;
}
/* HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH */
/* This initializes a random number generator */
srandom(time((time_t *) 0));
}
/* ################################################## */
/* rand_delay */
/* ################################################## */
void rand_delay(int max)
{
int m_delay, j ;
m_delay = (int) random()%max ;
for (j=0; j<m_delay; j++) sched_yield();
}
/* ################################################## */
/* childMessage */
/* ################################################## */
void childMessage (int ID)
{
pthread_mutex_lock(&stdoutLock) ;
cout << "Child # " << ID
<< " is now in position.\n" ;
pthread_mutex_unlock(&stdoutLock) ;
}
/* ################################################## */
/* child */
/* ################################################## */
void * child(void * idPtr)
{
/* This is just a change of data type for convenience. Now
'me' is the number of the child. Children have numbers ranging
from 0 to whatever the value of THREAD_COUNT is. */
int me = ((threadIdType *) (idPtr))->id, temp ;
/* Delay code has been inserted here to magnify the chances that
child threads will delay each other. The student
completing the program should think of more ways to insert
random delays that are likely to 'stress test' the program. */
rand_delay(100) ;
/* Here put whatever synchronization code is required prior
to the code where the child process declares that it has
put itself into position. */
/* HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH */
switch(me)
{
case 0: break ; // thread 0 just 'goes'
case 1: wait_sem (support_ready[0]) ; break ; // thread 1 waits for thread 0
case 2: wait_sem (support_ready[0]) ; break ; // thread 2 waits for thread 0
case 3: wait_sem (support_ready[1]) ;
wait_sem (support_ready[2]) ; // thread 3 waits for both 1 and 2
break ;
default: pthread_mutex_lock(&stdoutLock) ;
cout << "\n\nBAD VALUE TO CASE STATMENT!!\n\n" ;
pthread_mutex_unlock(&stdoutLock) ;
exit (-1); break ;
}
/* HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH */
childMessage(me) ; // The child declares that it is in position
/* Here put whatever synchronization code is required AFTER
the code where the child process declares that it has
put itself into position. */
/* HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH */
switch(me)
{
case 0: signal_sem(support_ready[0]) ;
signal_sem(support_ready[0]) ; // thread 0 signals for threads 1 and 2
break ;
case 1: signal_sem(support_ready[1]) ; break ; // thread 1 signals for 3
case 2: signal_sem(support_ready[2]) ; break ; // thread 2 signals for 3
case 3: signal_sem(finished) ; break ; // thread 3 signals to the mother
default: pthread_mutex_lock(&stdoutLock) ;
cout << "\n\nBAD VALUE TO CASE STATMENT!!\n\n" ;
pthread_mutex_unlock(&stdoutLock) ;
exit (-1); break ;
}
/* HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH */
pthread_exit ((void *)0) ;
}
/* ################################################## */
/* mother */
/* ################################################## */
/* The mother spawns child threads and then waits for
them all to finish. The mother's waiting has to be
implemented as part of the overall protocol - using
the sim_semaphore data type. */
void mother()
{
int i;
/* This is a pointer to a struct (class) that contains an int
field - it is a convenient data type to use as the parameter
to the child function. */
threadIdType * idPtr ;
/* Create child threads numbered 1 through 8. */
pthread_mutex_lock(&stdoutLock) ;
cout << "Mother Begins Spawning Threads.\n" << endl ;
pthread_mutex_unlock(&stdoutLock) ;
for (i = 0; i < THREAD_COUNT ; i++)
{
idPtr = new threadIdType ; /* allocate memory for struct */
idPtr->id = i ; /* records current index as the child's ID */
/* The call below is what actually creates the child thread
and passes a pointer to the struct 'idPtr' as the
parameter to the child function. */
if ( 0!=pthread_create(&thr[i], NULL, child, (void *) idPtr) )
{ pthread_mutex_lock(&stdoutLock) ; /* 'error out' code here */
cout << "THREAD CREATION FAILURE!" << endl ;
pthread_mutex_unlock(&stdoutLock) ;
exit(-1) ; }
/* The call to pthread_detach() below marks a child
thread as 'detached' so that the system will not
expect the parent to 'join' (wait for) it. */
if (0!=pthread_detach(thr[i]))
{ pthread_mutex_lock(&stdoutLock) ;/* 'error out' code here */
cout << "THREAD DETACHMENT FAILURE!" << endl ;
pthread_mutex_unlock(&stdoutLock) ;
exit(-1) ;}
}
pthread_mutex_lock(&stdoutLock) ;
cout << "Mother Finishes Spawning Threads.\n" << endl ;
pthread_mutex_unlock(&stdoutLock) ;
/* Some synchronization code is needed here to make the mother
behave correctly - she is not supposed to exit until after
all the children have finished putting themselves into position. */
/* HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH */
wait_sem (finished) ;
/* HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH */
pthread_mutex_lock(&stdoutLock) ;
cout << "\nAll children are now in position.\n" ;
cout << "Mother exiting ... \n\n" ;
pthread_mutex_unlock(&stdoutLock) ;
return ;
}
/* ################################################## */
/* main */
/* ################################################## */
int main()
{
cout << "\nWelcome to The Trivial Diamond-Making Program!\n\n" ;
/* This calls the function that performs initializations. */
init();
/* Execute the mother() function */
mother();
return 0 ;
}