SOURCE FILE: sem.cpp
#include <iostream>
#include "pthread.h"
#include "sem.h"
using namespace std ;
/*
These directives make the two variables "checking" and
"stdoutLock" visible to the functions in this file. The
variables "checking" and "stdoutLock" are supposed to be
declared in the main program file.
*/
extern int checking ;
extern pthread_mutex_t stdoutLock ;
/* ################################################## */
/* create_sim_sem */
/* ################################################## */
/* Function to allocate a semaphore and
initialize it. */
sim_semaphore create_sim_sem(int init_val)
{
sim_semaphore ptr ;
ptr = new sim_sem_data ;
if (ptr == NULL)
cout << "ERROR: Out of space for semaphore." << endl ;
else
{ if ( 0!=pthread_mutex_init(&(ptr->lock), NULL) )
{ cout << "MUTEX INITIALIZATION FAILURE!" << endl;
exit(-1) ;}
if ( 0!=pthread_cond_init(&(ptr->cond), NULL) )
{ cout << "CONDITION INITIALIZATION FAILURE!" << endl ;
exit(-1) ;}
(ptr->value) = init_val ;
(ptr->Qfront) = NULL ;
(ptr->Qrear) = NULL ;
}
return(ptr) ;
}
/* ################################################## */
/* empty_sem_Q */
/* ################################################## */
/* Test to check if the semaphore queue is
empty. */
int empty_sem_Q ( sim_sem_data sem_d)
{
return (sem_d.Qfront == NULL) ;
}
/* ################################################## */
/* print_sem_Q */
/* ################################################## */
/* For printing the id_num's of the processes
waiting in the semaphore queue -- for debugging
& checking.
-- WARNING! Don't add code in this procedure to
lock the stdoutLock. Callers of print_sem_Q
will lock printing before making the call.
Thus, it is an error for this procedure to
try to gain the lock.
*/
void print_sem_Q (sim_sem_data sem_d)
{
sim_PCB *temp ;
temp = sem_d.Qfront ;
cout << "Printing Q .... " << endl ;
if (temp == NULL) cout << "Q is empty." << endl ;
while (temp != NULL)
{
cout << "Thread #" << temp->id_num << endl ;
temp = temp->next ;
}
}
/* ################################################## */
/* enq_sem_Q */
/* ################################################## */
/* For placing a process on the semaphore
queue. */
void enq_sem_Q (sim_semaphore sem, sim_PCB * proc)
{ /* Place the unique id of the thread into
a field of the sim_PCB. */
(proc->id_num) = (int) pthread_self() ;
(proc->next) = NULL ;
(proc->in_Q) = 1 ;
if (empty_sem_Q(*sem)) sem->Qfront = proc ;
else sem->Qrear->next = proc ;
sem->Qrear = proc ;
}
/* ################################################## */
/* serve_sem_Q */
/* ################################################## */
/* For removing a process from the
semaphore queue. */
void serve_sem_Q(sim_semaphore sem)
{
sim_PCB *temp ;
if (checking)
{ pthread_mutex_lock (&stdoutLock) ;
cout << "State of Q in semaphore #"<< (int) sem
<< " prior to Q serve: " << endl ;
print_sem_Q (*sem) ;
pthread_mutex_unlock(&stdoutLock) ; }
temp = sem->Qfront ;
sem->Qfront = sem->Qfront->next ;
/* Provide a means by which an enqueued
thread can tell if it has been dequeued. */
(temp->in_Q) = 0 ;
if (sem->Qfront == NULL) sem->Qrear = NULL ;
if (checking)
{ pthread_mutex_lock(&stdoutLock) ;
cout << "State of Q in semaphore #"<< (int) sem
<< " after Q serve: " << endl ;
print_sem_Q (*sem) ;
pthread_mutex_unlock(&stdoutLock) ; }
}
/* ################################################## */
/* wait_sem */
/* ################################################## */
/* The wait operation on the simulated
semaphore. */
void wait_sem (sim_semaphore sem )
{
pthread_mutex_lock( &(sem->lock) ) ;
sem->value = sem->value - 1 ;
if (checking)
{ pthread_mutex_lock(&stdoutLock) ;
cout << "Thread #" << pthread_self()
<< " begins a wait and"
<< " decrements the value of semaphore #"
<< (int) sem << " to: " << sem->value << endl ;
pthread_mutex_unlock(&stdoutLock) ; }
if (sem->value < 0)
/* START (sem->value < 0) */
{
sim_PCB * proc = new sim_PCB ;
if (proc == NULL)
cout << "ERROR: Out of space for sim_PCB." << endl ;
else
/* START (proc != NULL) */
{
if (checking)
{ pthread_mutex_lock(&stdoutLock) ;
cout << "Thread #" << pthread_self()
<< " queues up on semaphore #" << (int) sem
<< "." << endl ;
pthread_mutex_unlock(&stdoutLock) ; }
/* Put a sim_PCB for this thread on the
queue.*/
enq_sem_Q(sem, proc) ;
/* The thread "dozes" until it awakens
and finds that it has been taken off
the queue. */
do
{
if (checking)
{ pthread_mutex_lock(&stdoutLock) ;
cout << "Thread #" << pthread_self()
<< " now BLOCKS on semaphore #" << (int) sem
<< " by doing a pthread_cond_wait." << endl ;
pthread_mutex_unlock(&stdoutLock) ; }
if ( 0!= pthread_cond_wait (&(sem->cond),&(sem->lock) ) )
{ cout << "COND BROADCAST FAILURE!" << endl ;
exit(-1) ;}
if (checking)
{ pthread_mutex_lock(&stdoutLock) ;
cout << "Thread #" << pthread_self()
<< " on semaphore #" << (int) sem
<< " wakes up, and checks to" << endl
<< "\t" << "see if a signal_sem OP has "
<< "taken it off the queue." << endl
<< "\t" << "It IS" ;
if (!proc->in_Q) cout << " NOT" ;
cout << " in the queue." << endl ;
pthread_mutex_unlock(&stdoutLock) ; }
} /* END OF DO-PART */
while (proc->in_Q) ;
delete proc ;
} /* END else (proc != NULL) */
} /* END if (sem->value < 0) */
if (checking)
{ pthread_mutex_lock(&stdoutLock) ;
cout << "Thread #" << pthread_self()
<< " completes a wait on semaphore #"
<< (int) sem << "." << endl ;
pthread_mutex_unlock(&stdoutLock) ; }
pthread_mutex_unlock( &(sem->lock) ) ;
}
/* ################################################## */
/* signal_sem */
/* ################################################## */
/* The signal operation on the simulated
semaphore. */
void signal_sem (sim_semaphore sem)
{
pthread_mutex_lock(&(sem->lock)) ;
sem->value = sem->value + 1 ;
if (checking)
{ pthread_mutex_lock(&stdoutLock) ;
cout << "Thread #" << pthread_self()
<< " begins a signal and"
<< " increments the value of semaphore #"
<< (int) sem << " to: " << sem->value << endl ;
pthread_mutex_unlock(&stdoutLock) ; }
if (sem->value <= 0)
{
if (checking)
{ pthread_mutex_lock(&stdoutLock) ;
cout << "Thread #" << pthread_self()
<< " removes a PCB from the queue of"
<< " semaphore #" << (int) sem << "." << endl ;
pthread_mutex_unlock(&stdoutLock) ; }
serve_sem_Q(sem) ; /* Remove next sim_PCB from queue. */
/* Tell each dozing thread to check
to see if it is the one removed. */
if (checking)
{ pthread_mutex_lock(&stdoutLock) ;
cout << "Thread #" << pthread_self()
<< " broadcasts to the thread(s) waiting on semaphore #"
<< (int) sem << "." << endl ;
pthread_mutex_unlock(&stdoutLock) ; }
if ( 0!=pthread_cond_broadcast(&(sem->cond)) )
{ cout << "COND BROADCAST FAILURE!" << endl ;
exit(-1) ;}
}
if (checking)
{ pthread_mutex_lock(&stdoutLock) ;
cout << "Thread #" << pthread_self()
<< " completes a signal on semaphore #"
<< (int) sem << "." << endl ;
pthread_mutex_unlock(&stdoutLock) ; }
pthread_mutex_unlock( &(sem->lock) ) ;
}