Simulating a Monitor

Recall that a monitor has some local variables, monitor procedures and an initialization part. When the monitor is created, the initialization part initializes the local variables. The boundary of the monitor serves as a fence for blocking threads that call one of the monitor procedures. More precisely, at any time, there can only be one thread executing within the monitor boundary. Therefore, the monitor boundary guarantees mutual exclusion.

To simulate a monitor using C, we need to address a three questions: (1) how do we make sure that the local variables will not be accessed by non-monitor procedures, (2) how do we make sure that the user can only ``see'' the interface without knowing the details of the monitor, and (3) how do we properly setup the monitor boundary so that mutual exclusion can be guaranteed.

Questions (1) and (2) have a simple solution. We could use two files, one header file that stores the prototypes of the monitor procedures and a implementation file in which the local variables and all monitor procedures that are not supposed to be used by the user directly are declared with static storage class. A name declared with static can only be accessed from the place after the declaration point in the ``same'' file. Question (3) has a natural answer: use a mutex lock!

To illustrate this idea, we shall design a monitor for maintaining a counter so that its value can be increased, decreased, reset and retrieved. In addition to this, we also need an initialization function. Thus, we need a header file in which the prototype of these five functions are defined. Let the increasing, decreasing, resetting and retrieving functions be INC(), DEC(), SET() and GET(), respectively. Function SET() has an argument for setting the counter to this new value. The initialization function is CounterInit() which takes the initial value for the counter as its only argument. Let the file that contains these information be inc-dec-m.h.

Click here to download a copy of this header file.

void  CounterInit(int  n);    /* initialize monitor       */
int   INC(void);              /* increase the counter     */
int   DEC(void);              /* decrease the counter     */
void  SET(int  n);            /* reset the counter        */
int   GET(void);              /* retrieve counter's value */

Next, we shall design all functions and local variables of the monitor and put them in an implementation file. Let this file be inc-dec-m.c.

Click here to download a copy of this file.

#include  <thread.h>
#include  <synch.h>

static  int  count;           /* the static counter       */

static  mutex_t  MonitorLock; /* the static mutex lock    */

void  CounterInit(int  n)
{
     count = n;
     mutex_init(&MonitorLock, USYNC_THREAD, (void *) NULL);
}

int  INC(void)
{
     int  value;

     mutex_lock(&MonitorLock);     /* lock the monitor         */
          value = (++count);       /* increase and save counter*/
     mutex_unlock(&MonitorLock);   /* release the monitor      */
     return  value;                /* return the new value     */
}

int  DEC(void)
{
     int  value;

     mutex_lock(&MonitorLock);
          value = (--count);
     mutex_unlock(&MonitorLock);
     return  value;
}

void  SET(int  n)
{
     mutex_lock(&MonitorLock);
          count = n;
     mutex_unlock(&MonitorLock);
}

int  GET(void)
{
     int  value;

     mutex_lock(&MonitorLock);
          value = count;
     mutex_unlock(&MonitorLock);
     return  value;
}

Here are further notes of this monitor.

Let us write a main program to use this monitor. In fact, this main program has no mystery at all. The monitor is initialized by a call like CounterInit(0) to put a zero into the monitor protected counter. In addition to a monitor, two mutex locks, Screen and RandomNumber are introduced. The former protects the screen and the latter guarantees that the random number generator can be accessed mutual exclusively.

In this main program, each ``increasing'' thread increases the counter value MAX_ITEMS times. On the other hand, each ``decreasing'' thread decreases the counter value MAX_ITEMS times. The ``resetting'' thread resets the counter value MAX_ITEMS/2 times using random numbers and the ``display'' thread displays the counter value MAX_ITEMS/2 times. The main program creates equal number of ``increasing'' and ``decreasing'' threads, one ``resetting'' thread and ``display'' thread. This is a busy program!

Click here to download a copy of this main program (counter-2.c).

#include  <stdio.h>
#include  <thread.h>
#include  <stdlib.h>
#include  <time.h>

#include  "inc-dec-m.h"

#define   MAX_ITEMS    20
#define   MAX_INC       5
#define   MAX_DEC       4
#define   MAX_THREADS   3

mutex_t   Screen;                       /* mutex lock for screen    */
mutex_t   RandomNumber;                 /* lock for random # gen.   */

void  *Increase(void *voidPTR)
{
     int  *intPTR = (int *) voidPTR;
     int  ID      = *intPTR;
     int  value, i;

     for (i = 0; i < MAX_ITEMS; i++) {  /* iterates MAX_ITEMS times */
          thr_yield();                  /* take some rest           */
          value = INC();                /* increase the value       */
          mutex_lock(&Screen);          /* display the new value    */
               printf("   (%d): Counter has been increased by 1 "
                      "(value = %d)\n", ID, value);
          mutex_unlock(&Screen);
     }
     thr_exit(0);
}


void  *Decrease(void *voidPTR)
{
     int  *intPTR = (int *) voidPTR;
     int  ID      = *intPTR;
     int  value, i;

     for (i = 0; i < MAX_ITEMS; i++) {
          thr_yield();
          value = DEC();
          mutex_lock(&Screen);
               printf("   (%d): Counter has been decreased by 1 "
                      "(value = %d)\n", ID, value);
          mutex_unlock(&Screen);
     }
     thr_exit(0);
}

void  *Reset(void *voidPTR)
{
     int  *intPTR = (int *) voidPTR;
     int  ID      = *intPTR;
     int  value, i;

     for (i = 0; i < MAX_ITEMS/2; i++) {
          thr_yield();
          mutex_lock(&RandomNumber);
               value = rand() % MAX_INC;
          mutex_unlock(&RandomNumber);
          SET(value);
          mutex_lock(&Screen);
               printf("*** (%d): The counter has been reset to %d ***\n",
                      ID, value);
          mutex_unlock(&Screen);
     }
     thr_exit(0);
}

void  *Display(void *voidPTR)
{
     int  *intPTR = (int *) voidPTR;
     int  ID      = *intPTR;
     int  value, i;

     for (i = 0; i < MAX_ITEMS/2; i++) {
          thr_yield();
          value = GET();
          mutex_lock(&Screen);
               printf("### (%d): The current counter value is %d ###\n",
                      ID, value);
          mutex_unlock(&Screen);
     }
     thr_exit(0);
}

void  main(void)
{
     thread_t  incID[MAX_THREADS], decID[MAX_THREADS];
     thread_t  ResetID, DisplayID;
     size_t    incStatus[MAX_THREADS], decStatus[MAX_THREADS];
     size_t    ResetStatus, DisplayStatus;
     int       incArg[MAX_THREADS], decArg[MAX_THREADS];
     int       ResetArg, DisplayArg;
     int       i;

     srand((unsigned) time(NULL));

     mutex_init(&Screen, USYNC_THREAD, (void *) NULL);
     mutex_init(&RandomNumber, USYNC_THREAD, (void *) NULL);
     CounterInit(0);

     ResetArg = 0;
     thr_create(NULL, 0, Reset, (void *) &ResetArg,
                0, (void *) &ResetID);
     DisplayArg = 1;
     thr_create(NULL, 0, Display, (void *) &DisplayArg,
                0, (void *) &DisplayID);

     for (i = 0; i < MAX_THREADS; i++) {
          incArg[i] = i + 2;
          decArg[i] = incArg[i] + MAX_THREADS;
          thr_create(NULL, 0, Increase, (void *) &(incArg[i]),
                     0, (void *) &(incID[i]));
          thr_create(NULL, 0, Decrease, (void *) &(decArg[i]),
                     0, (void *) &(decID[i]));
     }

     for (i = 0; i < MAX_THREADS; i++) {
          thr_join(incID[i], 0, (void *) &(incStatus[i]));
          thr_join(decID[i], 0, (void *) &(decStatus[i]));
     }
     thr_join(ResetID, 0, (void *) &ResetStatus);
     thr_join(DisplayID, 0, (void *) &DisplayStatus);

     printf("Parent exits ...\n");
}