blob: a65a3e38620eed077745300dff3e42fa38c4f79e [file] [log] [blame]
(* Semaphores.mod implement the ISO Semaphores specification.
Copyright (C) 2010-2025 Free Software Foundation, Inc.
Contributed by Gaius Mulley <gaius.mulley@southwales.ac.uk>.
This file is part of GNU Modula-2.
GNU Modula-2 is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 3, or (at your option)
any later version.
GNU Modula-2 is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.
Under Section 7 of GPL version 3, you are granted additional
permissions described in the GCC Runtime Library Exception, version
3.1, as published by the Free Software Foundation.
You should have received a copy of the GNU General Public License and
a copy of the GCC Runtime Library Exception along with this program;
see the files COPYING3 and COPYING.RUNTIME respectively. If not, see
<http://www.gnu.org/licenses/>. *)
IMPLEMENTATION MODULE Semaphores ;
(* Provides mutual exclusion facilities for use by processes. *)
FROM Storage IMPORT ALLOCATE ;
FROM Processes IMPORT ProcessId, Me, SuspendMe, Activate, UrgencyOf ;
TYPE
SEMAPHORE = POINTER TO RECORD
value: CARDINAL ;
next : SEMAPHORE ;
head : ProcessList ;
END ;
ProcessList = POINTER TO RECORD
waiting: ProcessId ;
right,
left : ProcessList ;
END ;
VAR
freeSem : SEMAPHORE ;
freeProcessList: ProcessList ;
(*
Create - creates and returns s as the identity of a new
semaphore that has its associated count initialized
to initialCount, and has no processes yet waiting on it.
*)
PROCEDURE Create (VAR s: SEMAPHORE; initialCount: CARDINAL) ;
BEGIN
s := newSemaphore () ;
WITH s^ DO
value := initialCount ;
next := NIL ;
head := NIL
END
END Create ;
(*
Destroy - recovers the resources used to implement the semaphore s,
provided that no process is waiting for s to become free.
*)
PROCEDURE Destroy (VAR s: SEMAPHORE) ;
BEGIN
WITH s^ DO
IF head=NIL
THEN
next := freeSem ;
freeSem := s
ELSE
(* raise exception? *)
END
END
END Destroy ;
(*
newSemaphore -
*)
PROCEDURE newSemaphore () : SEMAPHORE ;
VAR
s: SEMAPHORE ;
BEGIN
IF freeSem=NIL
THEN
NEW (s)
ELSE
s := freeSem ;
freeSem := freeSem^.next
END ;
RETURN s
END newSemaphore ;
(*
newProcessList - returns a new ProcessList.
*)
PROCEDURE newProcessList () : ProcessList ;
VAR
l: ProcessList ;
BEGIN
IF freeProcessList=NIL
THEN
NEW (l)
ELSE
l := freeProcessList ;
freeProcessList := freeProcessList^.right
END ;
RETURN l
END newProcessList ;
(*
add - adds process, p, to queue, head.
*)
PROCEDURE add (VAR head: ProcessList; p: ProcessList) ;
BEGIN
IF head=NIL
THEN
head := p ;
p^.left := p ;
p^.right := p
ELSE
p^.right := head ;
p^.left := head^.left ;
head^.left^.right := p ;
head^.left := p
END
END add ;
(*
sub - subtracts process, p, from queue, head.
*)
PROCEDURE sub (VAR head: ProcessList; p: ProcessList) ;
BEGIN
IF (p^.left=head) AND (p=head)
THEN
head := NIL
ELSE
IF head=p
THEN
head := head^.right
END ;
p^.left^.right := p^.right ;
p^.right^.left := p^.left
END
END sub ;
(*
addProcess - adds the current process to the semaphore list.
Remove the current process from the ready queue.
*)
PROCEDURE addProcess (VAR head: ProcessList) ;
VAR
l: ProcessList ;
BEGIN
l := newProcessList() ;
WITH l^ DO
waiting := Me () ;
right := NIL ;
left := NIL
END ;
add (head, l) ;
SuspendMe
END addProcess ;
(*
chooseProcess -
*)
PROCEDURE chooseProcess (head: ProcessList) : ProcessList ;
VAR
best, l: ProcessList ;
BEGIN
best := head ;
l := head^.right ;
WHILE l#head DO
IF UrgencyOf (l^.waiting) > UrgencyOf (best^.waiting)
THEN
best := l
END ;
l := l^.right
END ;
RETURN best
END chooseProcess ;
(*
removeProcess - removes process, l, from the list and adds it to the
ready queue.
*)
PROCEDURE removeProcess (VAR head: ProcessList; l: ProcessList) ;
BEGIN
sub (head, l) ;
WITH l^ DO
right := freeProcessList ;
freeProcessList := l ;
Activate (waiting)
END
END removeProcess ;
(*
Claim - if the count associated with the semaphore s is non-zero,
decrements this count and allows the calling process to
continue; otherwise suspends the calling process until
s is released.
*)
PROCEDURE Claim (s: SEMAPHORE) ;
BEGIN
WITH s^ DO
IF value>0
THEN
DEC (value)
ELSE
addProcess (head)
END
END
END Claim ;
(*
Release - if there are any processes waiting on the semaphore s,
allows one of them to enter the ready state; otherwise
increments the count associated with s.
*)
PROCEDURE Release (s: SEMAPHORE) ;
BEGIN
WITH s^ DO
IF head=NIL
THEN
INC (value)
ELSE
removeProcess (head, chooseProcess (head))
END
END
END Release ;
(*
CondClaim - returns FALSE if the call Claim(s) would cause the calling
process to be suspended; in this case the count associated
with s is not changed. Otherwise returns TRUE and the
associated count is decremented.
*)
PROCEDURE CondClaim (s: SEMAPHORE) : BOOLEAN ;
BEGIN
WITH s^ DO
IF value>0
THEN
DEC (value) ;
RETURN TRUE
ELSE
RETURN FALSE
END
END
END CondClaim ;
BEGIN
freeSem := NIL ;
freeProcessList := NIL
END Semaphores.