packages feed

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.