language-Modula2-0.1: examples/Modula-2_Libraries/PMOS/sources/general/circular.mod
IMPLEMENTATION MODULE CircularBuffers;
(********************************************************)
(* *)
(* Circular Buffers for passing character data *)
(* between a pair of tasks. *)
(* *)
(* Author: P. Moylan *)
(* Last edited: 21 February 1995 *)
(* *)
(* Status: OK *)
(* *)
(********************************************************)
FROM Storage1 IMPORT
(* proc *) ALLOCATE;
FROM Timer IMPORT
(* proc *) TimedWait;
FROM Semaphores IMPORT
(* type *) Semaphore,
(* proc *) CreateSemaphore, Wait, Signal;
FROM TaskControl IMPORT
(* type *) Lock,
(* proc *) CreateLock, Obtain, Release;
(************************************************************************)
TYPE
BufferTemplate = RECORD
SlotAvailable, DataPresent: Semaphore;
lastlocation: CARDINAL;
mutex: Lock;
GetPlace, PutPlace: CARDINAL;
data: ARRAY [0..0] OF CHAR
END;
(* Remark: we do something slightly nonstandard here, because *)
(* we want circular buffers of various sizes and Modula-2 does *)
(* not support variable-length arrays. The trick is to define *)
(* an array of one element, but then to extend it by calling *)
(* ALLOCATE to give us the array size we really need. Of *)
(* course, this works only if we suppress "out of range" error *)
(* checking while compiling this module. This is, admittedly, *)
(* an example of a programming style which probably should be *)
(* outlawed, but I couldn't think of a legal way to achieve the *)
(* desired result. *)
CircularBuffer = POINTER TO BufferTemplate;
(************************************************************************)
PROCEDURE CreateBuffer (VAR (*OUT*) B: CircularBuffer; size: CARDINAL);
(* Allocates space for a circular buffer, and initializes it. The *)
(* caller specifies how many characters the buffer will hold. *)
BEGIN
ALLOCATE (B, SIZE(BufferTemplate) + size - 1);
WITH B^ DO
lastlocation := size - 1;
CreateSemaphore (SlotAvailable, size);
CreateSemaphore (DataPresent, 0);
CreateLock (mutex);
GetPlace := 0; PutPlace := 0;
END (*WITH*);
END CreateBuffer;
(************************************************************************)
PROCEDURE PutBuffer (B: CircularBuffer; item: CHAR);
(* Puts item into the circular buffer, waiting for space available *)
(* if necessary. *)
BEGIN
WITH B^ DO
Wait (SlotAvailable);
(*# save, check(index => off) *)
data[PutPlace] := item;
(*# restore *)
IF PutPlace = lastlocation THEN PutPlace := 0
ELSE INC (PutPlace);
END (*IF*);
Signal (DataPresent);
END (*WITH*);
END PutBuffer;
(************************************************************************)
PROCEDURE PutBufferImpatient (B: CircularBuffer; item: CHAR;
TimeLimit: CARDINAL);
(* Like PutBuffer, but waits no longer than TimeLimit milliseconds *)
(* for a buffer slot to become available. If the time limit *)
(* expires, the oldest item in the buffer is overwritten by the *)
(* new data. *)
VAR TimedOut: BOOLEAN;
BEGIN
WITH B^ DO
TimedWait (SlotAvailable, TimeLimit, TimedOut);
IF TimedOut THEN
Obtain (mutex);
IF GetPlace = lastlocation THEN GetPlace := 0
ELSE INC (GetPlace);
END (*IF*);
Release (mutex);
END (*IF*);
(*# save, check(index => off) *)
data[PutPlace] := item;
(*# restore *)
IF PutPlace = lastlocation THEN PutPlace := 0
ELSE INC (PutPlace);
END (*IF*);
IF NOT TimedOut THEN
Signal (DataPresent);
END (*IF*);
END (*WITH*);
END PutBufferImpatient;
(************************************************************************)
PROCEDURE GetBuffer (B: CircularBuffer) : CHAR;
(* Gets one character from the circular buffer, waiting when *)
(* necessary for a character to become available. *)
VAR result: CHAR;
BEGIN
WITH B^ DO
Wait (DataPresent);
Obtain (mutex);
(*# save, check(index => off) *)
result := data[GetPlace];
(*# restore *)
IF GetPlace = lastlocation THEN GetPlace := 0
ELSE INC (GetPlace);
END (*IF*);
Release (mutex);
Signal (SlotAvailable);
END (*WITH*);
RETURN result;
END GetBuffer;
(************************************************************************)
PROCEDURE BufferEmpty (B: CircularBuffer): BOOLEAN;
(* Returns TRUE iff the buffer is empty. *)
VAR TimedOut: BOOLEAN;
BEGIN
WITH B^ DO
TimedWait (DataPresent, 0, TimedOut);
IF TimedOut THEN RETURN TRUE;
ELSE
Signal (DataPresent);
RETURN FALSE;
END (*IF*);
END (*WITH*);
END BufferEmpty;
(************************************************************************)
END CircularBuffers.