#!/usr/bin/perl -w
use strict ;

use POSIX ;
#use IPC::SysV qw(IPC_PRIVATE S_IRWXU IPC_CREAT);
use IPC::SysV qw(IPC_PRIVATE IPC_CREAT);
use IPC::Semaphore;

my $chops = new IPC::Semaphore(IPC_PRIVATE, 6, S_IRWXU | IPC_CREAT);
my $phils = new IPC::Semaphore(IPC_PRIVATE, 6, S_IRWXU | IPC_CREAT);
$chops->setall( 0, 0, 0, 0, 0, 0 ); # 0 = on table, $p = in use by philosopher $p
$phils->setall( 0, 0, 0, 0, 0, 0 ); # 0 = thinking, 1 = eating

sub delay {
	select( undef, undef, undef, rand() ) ;
}

sub short_delay {
	select( undef, undef, undef, 0.01*rand() ) ;
}

sub philosopher {
	my ( $p ) =  @_ ;

	my $sl = $p ;
	my $sr = $p + 1 ;
	if( $sr == 6 ) { $sr = 1 ; }

	for(;;) {

		#SUBTLE RACE - between setting @chop and @phil, the semiphore might change!
		#We'll hope it doesn't mess things up ;)
		
		my @chop = $chops->getall;
		my @phil = $phils->getall;
		# If not eating and left chop is available, pick it up
		if( $phil[$p] == 0 && $chop[$sl] == 0 ) {
			$chop[$sl] = $p ;
			$chops->setall( @chop ) ;
			short_delay() ; # Delay until going for right chop
		}

		@chop = $chops->getall;
		@phil = $phils->getall;
		# If not eating and has left chops and right is available, simultaneously pick up right and start eating
		if( $phil[$p] == 0 && $chop[$sl] == $p && $chop[$sr] == 0 ) {
			$phil[$p]  = 1 ;
			$chop[$sr] = $p ;
			$chops->setall( @chop ) ;
			$phils->setall( @phil ) ;
			delay() ; # Eating
		} else {
			#print "Philosopher $p cant eat! Chops: @chop   Phils: @phil\n" ;
			delay() ; #Thinking
		}

		@chop = $chops->getall;
		@phil = $phils->getall;
		# If eating, simultaneously stops eating and puts down chopsticks
		if( $phil[$p] == 1 ) {
			$phil[$p]  = 0 ;
			$chop[$sl] = 0 ;
			$chop[$sr] = 0 ;
			$chops->setall( @chop ) ;
			$phils->setall( @phil ) ;
			delay() ; # Thinking
		}
	}
}

my @c ;

for( my $i = 1 ; $i <= 5 ; $i++ ) {
	if( !($c[$i]=fork()) ) { philosopher($i) ; delay() }
}

for(;;){
	my @chop = $chops->getall;
	my @phil = $phils->getall;
	print "  Chops: "; for( my $i = 1 ; $i <= 5 ; $i++ ) { print $chop[$i], " " }
	print "  Phils: "; for( my $i = 1 ; $i <= 5 ; $i++ ) { print $phil[$i], " " }
	print "\n" ;
	select( undef, undef, undef, 0.01 ) ;
}


$SIG{TERM} = sub {
	for( my $i = 1 ; $i <= 5 ; $i++ ) { kill(9,$c[$i]) }
	$chops->remove;
	$phils->remove;
} ;


