Dining philosophers

We use locking mutexes for the forks, and a lollipop to keep the last person who finished eating from getting hungry until the next person finishes eating, which prevents a cycle of dependency from forming. The algorithm should scale to many philosophers, and no philosopher need be singled out to be left-handed, so it's fair in that sense.

class Fork {
    has $!lock = Lock.new;
    method grab($who, $which) {
	say "$who grabbing $which fork";
	$!lock.lock;
    }
    method drop($who, $which) {
	say "$who dropping $which fork";
	$!lock.unlock;
    }
}
 
class Lollipop {
    has $!channel = Channel.new;
    method mine($who) { $!channel.send($who) }
    method yours { $!channel.receive }
}
 
sub dally($sec) { sleep 0.01 + rand * $sec }
 
sub MAIN(*@names) {
    @names ||= <Aristotle Kant Spinoza Marx Russell>;
 
    my @lfork = Fork.new xx @names;
    my @rfork = @lfork.rotate;
 
    my $lollipop = Lollipop.new;
    start { $lollipop.yours; }
 
    my @philosophers = do for flat @names Z @lfork Z @rfork -> $n, $l, $r {
	start { 
	    sleep 1 + rand*4;
	    loop {
		$l.grab($n,'left');
		dally 1;  # give opportunity for deadlock
		$r.grab($n,'right');
		say "$n eating";
		dally 10;
		$l.drop($n,'left');
		$r.drop($n,'right');
 
		$lollipop.mine($n);
		sleep 1;  # lick at least once
		say "$n lost lollipop to $lollipop.yours(), now digesting";
 
		dally 20;
	    }
	}
    }
    sink await @philosophers;
}

Output:

Last updated

Was this helpful?