packages feed

language-Modula2-0.1: examples/Modula-2_Libraries/PMOS/sources/general/lossyque.mod

IMPLEMENTATION MODULE LossyQueues;

	(********************************************************)
	(*							*)
	(*	Implementation of lossy queue structures	*)
	(*							*)
	(*	Author:		P. Moylan			*)
	(*	Last edited:	27 February 1995		*)
	(*	Status:		Working				*)
	(*							*)
	(********************************************************)

FROM SYSTEM IMPORT
    (* proc *)	ADR;

FROM Storage1 IMPORT
    (* proc *)	ALLOCATE;

FROM Semaphores IMPORT
    (* type *)	Semaphore,
    (* proc *)	CreateSemaphore, Wait, Signal;

FROM TaskControl IMPORT
    (* type *)	Lock,
    (* proc *)	CreateLock, Obtain, Release;

FROM LowLevel IMPORT
    (* proc *)	Copy;

(************************************************************************)

TYPE
    (* Most of the fields below are self-explanatory, but note that:	*)
    (* (a) lastlocation, GetPlace, and PutPlace are measured as byte	*)
    (*	   offsets from the beginning of the data array;		*)
    (* (b) EltSize is the size, in bytes, of one element.		*)

    QueueTemplate = RECORD
			BufferFull: BOOLEAN;
			DataPresent: Semaphore;
			update: Lock;
			lastlocation, EltSize: CARDINAL;
			GetPlace, PutPlace: CARDINAL;
			data: ARRAY [0..0] OF BYTE;
		    END;

	(* Remark: we do something slightly nonstandard here, because	*)
	(* we want circular queues 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.						*)

    LossyQueue = POINTER TO QueueTemplate;

(************************************************************************)

PROCEDURE CreateQueue (VAR (*OUT*) B: LossyQueue;
					capacity, elementsize: CARDINAL);

    (* Allocates space for a lossy queue, and initializes it.  The	*)
    (* caller specifies how many elements (assumed to be of equal size)	*)
    (* the queue will hold, and the size in bytes of each element.	*)

    VAR size: CARDINAL;

    BEGIN
	size := capacity*elementsize;
	ALLOCATE (B, SIZE(QueueTemplate) + size - 1);
	WITH B^ DO
	    BufferFull := FALSE;
	    lastlocation := size - 1;
	    EltSize := elementsize;
	    CreateSemaphore (DataPresent, 0);
	    CreateLock (update);
	    GetPlace := 0;  PutPlace := 0;
	END (*WITH*);
    END CreateQueue;

(************************************************************************)

PROCEDURE PutQueue (B: LossyQueue;  item: ARRAY OF BYTE): BOOLEAN;

    (* If space is available, puts item at the tail of the queue and	*)
    (* returns TRUE.  Returns FALSE if the new item would not fit.	*)
    (* Note: it is assumed that SIZE(item) matches the element size	*)
    (* declared when the queue was created.				*)

    BEGIN
	WITH B^ DO
	    IF BufferFull THEN
		RETURN FALSE;
	    END (*IF*);
	    (*# save, check(index=>off) *)
	    Copy (ADR(item), ADR(data[PutPlace]), EltSize);
	    (*# restore *)

	    Obtain (update);
	    INC (PutPlace, EltSize);
	    IF PutPlace > lastlocation THEN PutPlace := 0; END(*IF*);
	    BufferFull := PutPlace = GetPlace;
	    Release (update);

	    Signal (DataPresent);
	    RETURN TRUE;
	END (*WITH*);
    END PutQueue;

(************************************************************************)

PROCEDURE GetQueue (B: LossyQueue;  VAR (*OUT*) item: ARRAY OF BYTE);

    (* Takes one item from the head of the queue, waiting if necessary. *)

    BEGIN
	WITH B^ DO
	    Wait (DataPresent);
	    (*# save, check(index=>off) *)
	    Copy (ADR(data[GetPlace]), ADR(item), EltSize);
	    (*# restore *)

	    Obtain (update);
	    INC (GetPlace, EltSize);
	    IF GetPlace > lastlocation THEN GetPlace := 0 END (*IF*);
	    BufferFull := FALSE;
	    Release (update);

	END (*WITH*);
    END GetQueue;

(************************************************************************)

END LossyQueues.