master-degree-notes/Concurrent Systems/notes/4c - Dining Philosophers.md

2.9 KiB
Raw Blame History

The first real practical example of a concurrent system.

PROBLEM: Devise a deadlock-free algorithm for allocating these limited resources (chopsticks) among several processes (philosophers).

A wrong solution

each chopstick is governed by a mutual exclusion semaphore that prevents any other philosopher from picking up the chopstick when it is already in use by another philosopher

semaphore chopstick[5] initialized to 1
Philosopher(i) :=
	while(1) do
		chopstick[i].down()
		chopstick[(i+1)%N].down()
		// eat
		chopstick[(i+1)%N].up()
		chopstick[i].up()

No two neighbours can eat simultaneously, but we can have a deadlock if all philosophers grab their right chopstick simultaneously.

Solution 1

Give a number to all forks and always try with the smaller. All philosophers must first pick left and then right, except for the last one that first picks right and then left.

So there will be a contention on one fork, so this way a process is automatically excluded.

semaphores fork[N] all initialized at 1;
Philosopher(i) :=
	Repeat
		think;
		if (i < N-1) then
			fork[i].down();
			fork[i+1].down();
		else
			fork[0].down();
			fork[N-1].down();
		eat;
		fork[(i+1)%N].up();
		fork[i].up();

This is deadlock free. But not much efficient as it may happen that only a philosopher per time will have both the forks (but that is another problem).

Solution 3

Allow at most N-1 philosophers at a time sitting at the table

semaphores fork[N] all initialized at 1
semaphore table initialized at N-1

Philosopher(i) :=
	Repeat
	think;
	table.down();
	fork[i].down();
	fork[(i+1)%N].down();
	eat;
	fork[(i+1)%N].up();
	fork[i].up();
	table.up()

In this case we break the symmetry by letting an odd number of philosophers sit at the table at most.

Solution 4 - Monitors!

Pick up 2 chopsticks only if both are free

  • a philosopher moves to his/her eating state only if both neighbors are not in their eating states
    • need to define a state for each philosopher
  • if one of my neighbors is eating, and Im hungry, ask them to signal me when theyre done
  • thus, states of each philosopher are: thinking, hungry, eating
    • need condition variables to signal waiting hungry philosopher(s)
monitor DP
	status state[N] all initialized at thinking;
	condition self[N];

Pickup(i) :=
	state[i] = hungry;
	test(i);
	if (state[i] != eating) then
		self[i].wait;

Putdown(i) :=
	state[i] = thinking;
	test((i+1)%N);
	test((i-1)%N);

test(i) :=
	if (state[(i+1)%N] != eating
		&& state[(i-1)%N] != eating
		&& state[i] == hungry) then
			state[i] = eating;
			self[i].signal();

N.B.: pickup picks both forks simultaneously