/* ---------------------------------------------------------------- */
/* MONITOR  bridge-m:                                               */
/*    This monitor solves the bridge-crossing problem.  It consists */
/* of the following procedures:                                     */
/*    (1) BridgeInit()     - initialize bridge                      */
/*    (2) ArriveBridge()   - called when a vehicle arrives at the   */
/*                           bridge                                 */
/*    (3) ExitBridge()     - called when a vehicle exits the bridge */
/* ---------------------------------------------------------------- */

#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  */

/* ---------------------------------------------------------------- */
/* FUNCTION  BridgeInit():                                          */
/*    This function initialize the bridge monitor.                  */
/* ---------------------------------------------------------------- */

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

/* ---------------------------------------------------------------- */
/* FUNCTION  isSafe():                                              */
/*    This function tests if a vehicle can proceed.  Since it is a  */
/* static function, it can only be accessed with this file.         */
/* ---------------------------------------------------------------- */

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

/* ---------------------------------------------------------------- */
/* FUNCTION  ArriveBridge():                                        */
/*    This function is called when a vehicle arrives at the bridge  */
/* ---------------------------------------------------------------- */

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

/* ---------------------------------------------------------------- */
/* FUNCTION  ExitBridge():                                          */
/*    This function is called when a vehicle exits the bridge.      */
/* ---------------------------------------------------------------- */

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