(Latest Revision -- 08/18/2000)

sem.cpp

#include <iostream.h>
#include "pthread.h"
#include "sem.h"

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