RosettaCodeData/Task/Dining-philosophers/Modula-3/dining-philosophers.mod3

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.