Universidad de Costa Rica

Escuela de Ciencias de la Computación e Informática

CI-0122 Sistemas Operativos

Temas

CI0122 / Temas revisados / Semana-06 / ForkAndSems / DP


/**
 *  Dining Philosophers solution using simultaneous Wait and Signal on a Unix semaphore array
 *  This solution uses a global array of semaphores "table"
 *      main method, create, initializes, and destroy "table" array
 *      Philo method, uses the "table" array to access concurrently chopsticks and let philosophers to eat
 *
 *  Ubuntu: install "libncurses5" and "libncurses5-dev"
 *
 **/


#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <time.h>
#include <curses.h>

#include "Semaphore.h"

#define NUMWORKERS 5

Semaphore * table;
Semaphore * mutex;		// Need to be init to 1


int main ( int argc, char *argv[] );
void Philo ( int arg );

/*
 *  Curses display
 */
void displayTitle() {
   WINDOW * title;
   char title0[ 128 ] = "\t CI-0122 Sistemas Operativos\n";
   char title1[ 128 ] = "\t ------------------------------------------------------------\n";
   char title2[ 128 ] = "\t| Process | Philosopher | Status | Chopstick 1 | Chopstick 2 |\n";
   char title3[ 128 ] = "\t|------------------------------------------------------------|";

   title = newwin( 4, 80, 0, 1 );
   waddstr( title, title0 );
   waddstr( title, title1 );
   waddstr( title, title2 );
   waddstr( title, title3 );
   wrefresh( title );

}


/**
 *  Display philosopher information using curses
 */
void displayPhilosopher( int who, int state ) {
   WINDOW * philoWin;
   int posx, posy;
   char buffer[ 128 ];
   char bottomLine[ 128 ] = "\t|------------------------------------------------------------|\n";
   char lastLine[ 128 ] = "\t ------------------------------------------------------------";
   char chop1[ 8 ] = "--";
   char chop2[ 8 ] = "--";
   char displayState[ 8 ] = "";

   switch ( state ) {
      case 0:	// Thinking
         sprintf( displayState, "%6s", "Think " );
         break;
      case 1:	// Hungry
         sprintf( displayState, "%6s", "Hungry" );
         break;
      case 2:	// Eating
         sprintf( displayState, "%6s", "Eating" );
         sprintf( chop1, "%2d", who );
         sprintf( chop2, "%2d", (who + 1) % NUMWORKERS );
         break;
      case 3:	// Finished
         sprintf( displayState, "%6s", "Ended " );
         break;
   }

// Choose window position according to philospher number, depends on title
   posy = (who + 2) * 2;
   posx = 1;

   mutex->Wait();
   philoWin = newwin( 2, 80, posy, posx );	// nlines, ncols, beginy, beginx

   if ( 4 >= who ) {
      sprintf( buffer, "\t|  %5d  |      %1d      | %6s |     %2s      |     %2s      |\n", getpid(), who + 1, displayState, chop1, chop2 );
   } else {
      sprintf( buffer, "\n" );
   }
   waddstr( philoWin, buffer );
// Check last line to display
   if ( who + 1 == NUMWORKERS ) {
      waddstr( philoWin, lastLine );
   } else {
      waddstr( philoWin, bottomLine );
   }

   wrefresh( philoWin );

   delwin( philoWin );

   mutex->Signal();

}


int main ( int argc, char *argv[] ) {

   int i, pindex, worker, status;
   pid_t workers[ NUMWORKERS ];

// curses initialization
   initscr();
   cbreak();
   noecho();
   displayTitle();

   srand( time( NULL ) );
   table = new Semaphore( NUMWORKERS );
   mutex = new Semaphore( 1, 1 );
   for ( i = 0; i < NUMWORKERS; i++ ) {	// Start workers
      status = fork();
      if ( ! status ) {	// Child code
         Philo( i );
      } else {
         workers[ i ] = status;
      }
   }

   for ( i = 0; i < NUMWORKERS; i++ ) {
      worker = waitpid( -1, &status, 0 );	// Wait for any of the started processes
//     worker = waitpid( workers[ i ], &status, 0 );	// Wait for started processed in order

      pindex = -1;
      do {	// Find out which worker finished
         pindex++;
      } while ( pindex < NUMWORKERS && worker != workers[ pindex ] );

      displayPhilosopher( pindex, 3 );
      if ( -1 == worker ) {
         perror( "main" );
      }
   }


   delete table;

   echo();
   nocbreak();
   endwin();

   printf( "\nNormal end of execution.\n" );

   return 0;

}


/**
 *  Simulates the philosopher behaviour repeat for ever (sleep, eat)
 *  Each process do the cycle 10 times
 */
void Philo( int who ) {

    int eats, thinks;

    for ( int i = 0; i < 10; i++ ) {	// Each worker takes 10 rounds

// Initial think cycle (sleep)
        displayPhilosopher( who, 0 );
        thinks = rand() % 2000000;
        usleep( thinks );		// Initial sleep

        displayPhilosopher( who, 1 );
        table->SP( who, (who + 1) % NUMWORKERS );	// Do a simultaneous wait on both semaphores
// Eat time
        eats = rand() % 2000000;
        displayPhilosopher( who, 2 );
        usleep( eats );
// Return chopsticks
        table->SV( who, (who + 1) % NUMWORKERS );	// Do a simultaneous signal of both semaphores

// Second think cycle
        thinks = rand() % 2000000;
        displayPhilosopher( who, 0 );
        usleep( thinks );

    }

    _exit( 0 );

}