language-Modula2-0.1: examples/Modula-2_Libraries/PMOS/sources/general/semaphor.mod
IMPLEMENTATION MODULE Semaphores;
(********************************************************)
(* *)
(* Implements the Wait and Signal operations on *)
(* semaphores. *)
(* *)
(* Programmer: P. Moylan *)
(* Last edited: 27 February 1995 *)
(* Status: Working *)
(* *)
(********************************************************)
FROM TaskControl IMPORT
(* type *) TaskQueue,
(* proc *) CreateQueue, MarkAsReady, QueueAndSwitchTasks,
QueueWithTimeout;
FROM InnerKernel IMPORT
(* proc *) EnterKernel, LeaveKernel;
FROM Storage IMPORT
(* proc *) ALLOCATE, DEALLOCATE;
FROM TerminationControl IMPORT
(* proc *) Crash;
(************************************************************************)
TYPE Semaphore = POINTER TO RECORD
value : INTEGER;
blockedlist : TaskQueue
END;
(************************************************************************)
PROCEDURE CreateSemaphore (VAR (*OUT*) s: Semaphore;
InitialValue: CARDINAL);
(* Creates semaphore s, with the given initial value and an empty *)
(* queue. *)
VAR savedPSW: CARDINAL;
BEGIN
savedPSW := EnterKernel();
NEW(s);
s^.value := InitialValue;
CreateQueue (s^.blockedlist);
LeaveKernel (savedPSW);
END CreateSemaphore;
(************************************************************************)
PROCEDURE DestroySemaphore (VAR (*INOUT*) s: Semaphore);
(* Reclaims any space used by semaphore s. Remark: It is not at *)
(* all obvious what should be done with any tasks which happen to *)
(* be blocked on this semaphore (should they be unblocked, or *)
(* killed?). At present we take the easy way out and assume that *)
(* there are no pending operations on s at the time that it is *)
(* destroyed. *)
VAR savedPSW: CARDINAL;
BEGIN
savedPSW := EnterKernel();
DISPOSE (s);
LeaveKernel (savedPSW);
END DestroySemaphore;
(************************************************************************)
PROCEDURE Wait (VAR (*INOUT*) s: Semaphore);
(* Decrements the semaphore value. If the value goes negative, the *)
(* calling task is blocked and there is a task switch. *)
VAR savedPSW: CARDINAL;
BEGIN
IF s = NIL THEN Crash ("Wait on nonexistent semaphore"); END(*IF*);
savedPSW := EnterKernel();
DEC (s^.value);
IF s^.value < 0 THEN
QueueAndSwitchTasks (s^.blockedlist)
END (*IF*);
LeaveKernel (savedPSW);
END Wait;
(************************************************************************)
PROCEDURE TimedWaitT (VAR (*INOUT*) s: Semaphore; TimeLimit: INTEGER;
VAR (*OUT*) TimedOut: BOOLEAN);
(* Like procedure Wait, except that it returns with TimedOut TRUE *)
(* if the corresponding Signal does not occur within TimeLimit *)
(* clock ticks. *)
VAR savedPSW: CARDINAL;
BEGIN
IF s = NIL THEN
Crash ("Timed Wait on nonexistent semaphore");
END(*IF*);
savedPSW := EnterKernel();
TimedOut := FALSE;
DEC (s^.value);
IF s^.value < 0 THEN
IF TimeLimit <= 0 THEN
TimedOut := TRUE;
ELSE
TimedOut := QueueWithTimeout (s^.blockedlist, TimeLimit);
END (*IF*);
IF TimedOut THEN
INC (s^.value);
END (*IF*);
END (*IF s^.value < 0 *);
LeaveKernel (savedPSW);
END TimedWaitT;
(************************************************************************)
PROCEDURE Signal (VAR (*INOUT*) s: Semaphore);
(* Increments the semaphore value. Unblocks one task, if there was *)
(* one waiting on this semaphore. *)
VAR savedPSW: CARDINAL;
BEGIN
IF s = NIL THEN Crash ("Signal on nonexistent semaphore"); END(*IF*);
savedPSW := EnterKernel();
INC (s^.value);
IF s^.value <= 0 THEN
(* If the semaphore initial value is nonnegative, the test *)
(* (s^.value<=0) is equivalent to the more obvious test *)
(* for nonempty blocked list. Negative initial values are *)
(* not permitted in this implementation. *)
MarkAsReady (s^.blockedlist);
END (*IF*);
LeaveKernel (savedPSW);
END Signal;
END Semaphores.