<> <> DIRECTORY ViewerInputQueue, Process USING [Detach, DisableTimeout, EnableAborts], Rope USING [ROPE], SafeStorage USING [NewZone], ViewerClasses USING [NotifyProc, Viewer]; ViewerQueueImpl: CEDAR MONITOR LOCKS q USING q: Queue IMPORTS Process, SafeStorage EXPORTS ViewerInputQueue = BEGIN Viewer: TYPE = ViewerClasses.Viewer; ROPE: TYPE = Rope.ROPE; Action: TYPE = ViewerInputQueue.Action; ClientProgrammingError: ERROR = CODE; InternalProgrammingError: ERROR = CODE; zone: ZONE = SafeStorage.NewZone[]; <> <<>> Queue: TYPE = REF QueueObj; QueueObj: PUBLIC TYPE = MONITORED RECORD [ firstEvent: Event _ NIL, pushModel: BOOL, newEvent: CONDITION, notifierRunning: BOOL _ FALSE ]; Event: TYPE = LIST OF ViewerInputQueue.Action; Create: PUBLIC PROC [pushModel: BOOL] RETURNS [queue: Queue] = TRUSTED { queue _ zone.NEW[QueueObj_ [pushModel: pushModel]]; Process.DisableTimeout[@queue.newEvent]; Process.EnableAborts[@queue.newEvent]; RETURN[queue]; }; EnQueue: PUBLIC PROC [q: Queue, action: ViewerInputQueue.Action] = { newEvent: Event _ zone.CONS[first: action, rest: NIL]; InternalEnqueue[q, newEvent]; }; DequeueAction: PUBLIC ENTRY PROC [q: Queue] RETURNS [ViewerInputQueue.Action] = { event: Event; IF q.pushModel THEN RETURN WITH ERROR ClientProgrammingError; WHILE q.firstEvent=NIL DO WAIT q.newEvent ENDLOOP; event _ q.firstEvent; q.firstEvent_ q.firstEvent.rest; RETURN [event.first]; }; Flush: PUBLIC ENTRY PROC [q: Queue, proc: PROC[Action]_ NIL] = { <> IF proc = NIL THEN q.firstEvent_ NIL ELSE FOR event: Event _ q.firstEvent, event.rest UNTIL event = NIL DO proc[event.first]; ENDLOOP; }; InternalEnqueue: ENTRY PROC [q: Queue, e: Event] = { <> qEnd: Event; IF q.firstEvent=NIL THEN q.firstEvent_ e ELSE { FOR qEnd_ q.firstEvent, qEnd.rest UNTIL qEnd.rest=NIL DO ENDLOOP; qEnd.rest_ e }; IF q.pushModel THEN { IF q.notifierRunning THEN RETURN; q.notifierRunning_ TRUE; TRUSTED {Process.Detach[FORK Notifier[q]]}; } ELSE NOTIFY q.newEvent }; Notifier: PROC [q: Queue] = { <> event: Event; UNTIL (event _ Dequeue[q]) = NIL DO ENABLE ABORTED => {Flush[q]; LOOP}; <> event.first.notify[event.first.viewer,event.first.list]; <> ENDLOOP; }; Dequeue: ENTRY PROC [q: Queue] RETURNS [event: Event] = { <> IF NOT q.pushModel THEN RETURN WITH ERROR InternalProgrammingError; IF q.firstEvent=NIL THEN {q.notifierRunning_ FALSE; RETURN[NIL]}; event_ q.firstEvent; q.firstEvent_ q.firstEvent.rest; }; END.