Bridge Crossing

Consider a narrow bridge that can only allow three vehicles in the same direction to cross at the same time. If there are three vehicles on the bridge, any incoming vehicle must wait as shown in the following figure.

When a vehicle exits the bridge, we have two cases to consider. Case 1, there are other vehicles on the bridge; and Case 2 the exiting vehicle is the last one on bridge. Case 1 is shown below. In this case, one vehicle in the same direction should be allowed to proceed.

Case 2 is more complicated and has two subcases. In this case, the exiting vehicle is the last vehicle on the bridge. If there are vehicles waiting in the opposite direction, one of them can proceed. This is illustrated below:

Or, if there is no vehicle waiting in the opposite direction, then let the waiting vehicle in the same direction to proceed:

Problem Statement

Given the problem above, design a monitor to ``control'' this bridge. More precisely, design a monitor with procedures ArriveBridge() and ExitBridge(). When a vehicle arrives at the bridge, function ArriveBridge() is called and when it exits the bridge, function ExitBridge() is called. As always, an initialization function is required.

Implementation

Our implementation consists of two files, a header file and an implementation file. The following is the header file bridge-m.h. Both ArriveBridge() and ExitBridge() require one argument giving the direction of the vehicle.

Click here to download a copy of this file.

void  BridgeInit(void);
void  ArriveBridge(int Direction);
void  ExitBridge(int Direction);

The following is the implementation file bridge-m.c.

Click here to download a copy of this file.

#include  <thread.h>

#include  "bridge-m.h"

#define   MAX_VEHICLE  3                /* max vehicle on bridge    */
#define   TRUE         1
#define   FALSE        0
#define   EAST         0                /* east bound               */
#define   WEST         1                /* west bound               */

static int  CurrentDirection;           /* current direction of cars*/
static int  VehicleCount;               /* # of vehicle on bridge   */
static int  Waiting[2];                 /* # east/west bound waiting*/

static mutex_t  MonitorLock;            /* monitor lock             */
static cond_t   EastWest[2];            /* blocking east/west cars  */

void  BridgeInit(void)
{
     VehicleCount = 0;                  /* no vehicle on bridge     */
     Waiting[0] = Waiting[1] = 0;       /* no vehicle waiting       */

     mutex_init(&MonitorLock, USYNC_THREAD, (void *) NULL);
     cond_init(&EastWest[0]), USYNC_THREAD, (void *) NULL);
     cond_init(&EastWest[1]), USYNC_THREAD, (void *) NULL);
}

static int  isSafe(int Direction)
{
     if (VehicleCount == 0)             /* if no vehicle on bridge  */
          return  TRUE;                 /* safe to cross            */
     else if ((VehicleCount < MAX_VEHICLE) && (CurrentDirection == Direction))
          return  TRUE;                 /* if < 3 in same direction */
     else
          return  FALSE;                /* otherwise, do not procee */
}

void  ArriveBridge(int Direction)
{
     mutex_lock(&MonitorLock);          /* lock the monitor         */
          if (!isSafe(Direction)) {
               Waiting[Direction]++;    /* no, wait at the bridge   */
               while (!isSafe(Direction))    /* safe to cross?      */
                    cond_wait(&EastWest[Direction]), &MonitorLock);
               Waiting[Direction]--;    /* go back to test again    */
          }
          VehicleCount++;               /* can proceed              */
          CurrentDirection = Direction; /* set direction            */
     mutex_unlock(&MonitorLock);         /* release monitor          */
}

void  ExitBridge(int Direction)
{
     mutex_lock(&MonitorLock);          /* lock the monitor         */
          VehicleCount--;               /* one vehicle exits        */
          if (VehicleCount > 0)         /* have vehicles on bridge? */
               cond_signal(&EastWest[Direction]));/* yes,same dir*/
          else {                        /* bridge is empty          */
               if (Waiting[1-Direction] != 0)  /* any opposite wait?*/
                    cond_signal(&EastWest[1-Direction])); /* yes */
               else                     /* no, release the same dir */
                    cond_signal(&EastWest[Direction]));
          }
     mutex_unlock(&MonitorLock);        /* release the monitor      */
}

The Main Program

The following program generates a number of vehicles, each of which crosses the bridge several times. When a vehicle thread is created, a number (i.e., the vehicle number) is assigned to it. Function OneVehicle() takes this number and starts to simulate bridge crossing. Each vehicle crosses the bridge Max_Run times. For each time, a random number in the range of 0 and 1 is generated. If the random number is less than or equal to 0.5, it is east bound; otherwise, it is west bound. Then, it calls ArriveBridge() asking permission to cross the bridge. After this function returns, the vehicle crosses the bridge. Then, it rests for some time and calls ExitBridge() to exit the bridge. This completes one iteration. Note that the screen and random number generator are protected by mutex locks Screen and RandomNumber.

Click here to download a copy of this file.

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

#include  "bridge-m.h"

#define   MAX_CROSSING  20
#define   BIG           ((double) 0x7FFF)

#define   MAX_THREADS   20

mutex_t   Screen;
mutex_t   RandomNumber;

int       Max_Run;

void  Filler(char x[], int n)
{
     int  i;

     for (i = 0; i < n*2; i++)
          x[i] = ' ';
     x[i] = '\0';
}

void  *OneVehicle(void *voidPTR)
{
     int     *intPTR = (int *) voidPTR;
     int     ID = *intPTR;
     int     Direction;
     char    *Dir[2] = { "<--", "-->" };
     char    space[200];
     int     i;
     double  D;

     Filler(space, ID);
     mutex_lock(&Screen);
          printf("%sVehicle %d started ...\n", space, ID);
     mutex_unlock(&Screen);
     for (i = 1; i <= Max_Run; i++) {   /* for each crossing   */
          thr_yield();                  /* rest for a while         */
          mutex_lock(&RandomNumber);    /* lock random # generator  */
               D = rand() / BIG;        /* generate a random number */
          mutex_unlock(&RandomNumber);  /* release random # gen.    */
          Direction = (D <= 0.5) ? 0 : 1;    /* which direction?    */
          mutex_lock(&Screen);
               printf("%sVehicle %d (%d) arrives at the bridge in "
                      "direction %s\n", space, ID, i, Dir[Direction]);
          mutex_unlock(&Screen);
          ArriveBridge(Direction);      /* arrive at the bridge     */
          mutex_lock(&Screen);
               printf("%sVehicle %d (%d) crosses the bridge\n", space, ID, i);
          mutex_unlock(&Screen);
          thr_yield();                  /* crossing the bridge      */
          ExitBridge(Direction);        /* exit the bridge          */
          mutex_lock(&Screen);
               printf("%sVehicle %d (%d) is done\n", space, ID, i);
          mutex_unlock(&Screen);
     }
     mutex_lock(&Screen);
          printf("%sVehicle %d is gone forever ...\n", space, ID);
     mutex_unlock(&Screen);
     thr_exit(0);
}

void  main(int argc, char *argv[])
{
     thread_t   ID[MAX_THREADS];         /* vehicle ID               */
     size_t     Status[MAX_THREADS];     /* vehicle status           */
     int        Arg[MAX_THREADS];        /* vehicle argument         */
     int        Threads;                 /* # of vehicles            */
     int        i;

     if (argc != 3)  {
          printf("Use %s #-of-iterations #-of-vehicles\n", argv[0]);
          exit(0);
     }
     Max_Run = abs(atoi(argv[1]));
     Threads = abs(atoi(argv[2]));
     if (Threads > MAX_THREADS) {
          printf("The no. of vehicles is too large.  Reset to %d\n",
                 MAX_THREADS);
          Threads = MAX_THREADS;
     }

     printf("Parent started ...\n");

     mutex_init(&Screen, USYNC_THREAD, (void *) NULL);
     BridgeInit();
     srand((unsigned) time(NULL));

     for (i = 0; i < Threads; i++) {     /* start vehicles          */
          Arg[i] = i+1;
          thr_create(NULL, 0, OneVehicle, (void *) &(Arg[i]),
                     0, (void *) &(ID[i]));
     }
     for (i = 0; i < Threads; i++)       /* wait for vehicles       */
          thr_join(ID[i], 0, (void *) &(Status[i]));

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