/* ---------------------------------------------------------------- */
/* PROGRAM  count-2.c:                                              */
/*    This program uses a monitor (inc-dec-m.h and inc-dec-m.c) to  */
/* synchronize the increasing, decreasing, reseting and retrieving  */
/* the value of a shared counter.  Note that the counter is within  */
/* the monitor.                                                     */
/* ---------------------------------------------------------------- */

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

/* ---------------------------------------------------------------- */
/* FUNCTION  Increase():                                            */
/*    This function will be run as a thread.  It keeps increasing   */
/* the value of the shared counter.                                 */
/* ---------------------------------------------------------------- */

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


/* ---------------------------------------------------------------- */
/* FUNCTION  Decrease():                                            */
/*    This function will be run as a thread.  It keeps decreasing   */
/* the value of the shared counter.                                 */
/* ---------------------------------------------------------------- */

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

/* ---------------------------------------------------------------- */
/* FUNCTION  Reset():                                               */
/*    This function will be run as a thread.  It keeps reseting the */
/* value of the shared counter.                                     */
/* ---------------------------------------------------------------- */

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

/* ---------------------------------------------------------------- */
/* FUNCTION  Display():                                             */
/*   This function will be run as a thread.  It keeps displaying    */
/* the value of the shared counter.                                 */
/* ---------------------------------------------------------------- */

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

/* ---------------------------------------------------------------- */
/*                 The Main Program Starts Here                     */
/* ---------------------------------------------------------------- */

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