102 lines
3.0 KiB
Plaintext
102 lines
3.0 KiB
Plaintext
MODULE DiningPhilosophers EXPORTS Main;
|
|
|
|
IMPORT IO, Random, Thread;
|
|
|
|
CONST
|
|
|
|
PartySize = 5; (* modify for more/fewer philosophers *)
|
|
|
|
TYPE
|
|
|
|
Closure = Thread.Closure OBJECT
|
|
(* thread information *)
|
|
which: [1..PartySize]; (* identifies the thread *)
|
|
OVERRIDES
|
|
apply := Live; (* procedure to execute *)
|
|
END;
|
|
|
|
VAR
|
|
|
|
(* how long to eat/think *)
|
|
random: Random.T;
|
|
|
|
(* controls access to resources *)
|
|
test := NEW(MUTEX);
|
|
forks := NEW(Thread.Condition); (* condition variable, used for signaling *)
|
|
forkAvailable := ARRAY[1..PartySize] OF BOOLEAN {
|
|
TRUE, TRUE, TRUE, TRUE, TRUE
|
|
};
|
|
(* the philosophers/tasks *)
|
|
thread: ARRAY[1..PartySize] OF Thread.T;
|
|
name := ARRAY[1..PartySize] OF TEXT {
|
|
"Aristotle", "Kant", "Spinoza", "Marx", "Russell"
|
|
};
|
|
|
|
PROCEDURE PlaceAvailable(): CARDINAL =
|
|
(*
|
|
Determines whether a place is available at the table.
|
|
If so, returns the place number. Otherwise, returns 0.
|
|
We consider a place available if and only if *both* forks are free.
|
|
*)
|
|
BEGIN
|
|
FOR i := 1 TO PartySize DO
|
|
IF forkAvailable[i] AND forkAvailable[((i+1) MOD PartySize) + 1] THEN
|
|
RETURN i;
|
|
END;
|
|
END;
|
|
RETURN 0;
|
|
END PlaceAvailable;
|
|
|
|
PROCEDURE Live(philosopher: Closure): REFANY =
|
|
(* philosophers eat, sleep, ... and that's about it *)
|
|
VAR
|
|
place: CARDINAL;
|
|
BEGIN
|
|
WITH which = philosopher.which DO
|
|
WHILE TRUE DO
|
|
(* first make sure a place is available: both forks must be free! *)
|
|
LOCK test DO
|
|
place := PlaceAvailable();
|
|
(* if not, release mutex and use condition variable to wait for one *)
|
|
WHILE place = 0 DO
|
|
IO.Put(name[which]); IO.Put(" starving!\n");
|
|
Thread.Wait(test, forks);
|
|
(* in Modula-3 we arrive here only if we have the lock again *)
|
|
place := PlaceAvailable();
|
|
END;
|
|
(* a place has come available! seize the forks while mutex is locked *)
|
|
forkAvailable[place] := FALSE;
|
|
forkAvailable[(place MOD PartySize) + 1] := FALSE;
|
|
IO.Put(name[which]); IO.Put(" eating at place "); IO.PutInt(place);
|
|
IO.PutChar('\n');
|
|
END;
|
|
Thread.Pause(FLOAT(random.integer(1,3), LONGREAL));
|
|
(* put down the forks *)
|
|
forkAvailable[place] := TRUE;
|
|
forkAvailable[(place MOD PartySize) + 1] := TRUE;
|
|
Thread.Signal(forks); (* signal the condition variable *)
|
|
LOCK test DO
|
|
IO.Put(name[which]); IO.Put(" thinking\n");
|
|
END;
|
|
Thread.Pause(FLOAT(random.integer(1,3), LONGREAL));
|
|
END; (* WHILE *)
|
|
END; (* WITH *)
|
|
RETURN NIL;
|
|
END Live;
|
|
|
|
BEGIN
|
|
random := NEW(Random.Default).init();
|
|
(* bring philosophers to life *)
|
|
FOR i := 1 TO PartySize DO
|
|
thread[i] := Thread.Fork(NEW(Closure, apply := Live, which := i));
|
|
END;
|
|
(*
|
|
We need to wait, otherwise the program will terminate,
|
|
and the philosophers with it. Technically we could wait
|
|
for just one philosopher, but in the interest of symmetry...
|
|
*)
|
|
FOR i := 1 TO PartySize DO
|
|
EVAL Thread.Join(thread[i]);
|
|
END;
|
|
END DiningPhilosophers.
|