mirror of
https://github.com/vishapoberon/compiler.git
synced 2026-04-06 22:42:24 +00:00
566 lines
16 KiB
Modula-2
566 lines
16 KiB
Modula-2
(* Ulm's Oberon Library
|
|
Copyright (C) 1989-1994 by University of Ulm, SAI, D-89069 Ulm, Germany
|
|
----------------------------------------------------------------------------
|
|
Ulm's Oberon Library is free software; you can redistribute it
|
|
and/or modify it under the terms of the GNU Library General Public
|
|
License as published by the Free Software Foundation; either version
|
|
2 of the License, or (at your option) any later version.
|
|
|
|
Ulm's Oberon Library 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
|
|
Library General Public License for more details.
|
|
|
|
You should have received a copy of the GNU Library General Public
|
|
License along with this library; if not, write to the Free Software
|
|
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
|
----------------------------------------------------------------------------
|
|
E-mail contact: oberon@mathematik.uni-ulm.de
|
|
----------------------------------------------------------------------------
|
|
$Id: Events.om,v 1.4 2004/03/30 17:48:14 borchert Exp $
|
|
----------------------------------------------------------------------------
|
|
$Log: Events.om,v $
|
|
Revision 1.4 2004/03/30 17:48:14 borchert
|
|
support of external queue handling added
|
|
|
|
Revision 1.3 1996/01/04 17:07:20 borchert
|
|
event types are now an extension of Services.Object
|
|
|
|
Revision 1.2 1994/07/18 14:17:17 borchert
|
|
unused variables of Raise (oldevent + newevent) removed
|
|
|
|
Revision 1.1 1994/02/22 20:07:41 borchert
|
|
Initial revision
|
|
|
|
----------------------------------------------------------------------------
|
|
AFB 8/89
|
|
----------------------------------------------------------------------------
|
|
*)
|
|
|
|
MODULE ulmEvents;
|
|
|
|
IMPORT Objects := ulmObjects, Priorities := ulmPriorities, Services := ulmServices, SYS := ulmSYSTEM, SYSTEM;
|
|
|
|
TYPE
|
|
EventType* = POINTER TO EventTypeRec;
|
|
|
|
CONST
|
|
(* possibilities on receipt of an event: *)
|
|
default* = 0; (* causes abortion *)
|
|
ignore* = 1; (* ignore event *)
|
|
funcs* = 2; (* call associated event handlers *)
|
|
|
|
TYPE
|
|
Reaction* = INTEGER; (* one of default, ignore, or funcs *)
|
|
Message* = ARRAY 80 OF CHAR;
|
|
Event* = POINTER TO EventRec;
|
|
EventRec* =
|
|
RECORD
|
|
(Objects.ObjectRec)
|
|
type*: EventType;
|
|
message*: Message;
|
|
(* private part *)
|
|
next: Event; (* queue *)
|
|
END;
|
|
EventHandler = PROCEDURE (event: Event);
|
|
|
|
(* event managers are needed if there is any action necessary
|
|
on changing the kind of reaction
|
|
*)
|
|
EventManager = PROCEDURE (type: EventType; reaction: Reaction);
|
|
|
|
Priority = INTEGER; (* must be non-negative *)
|
|
|
|
(* every event with reaction `funcs' has a handler list;
|
|
the list is in calling order which is reverse to
|
|
the order of `Handler'-calls
|
|
*)
|
|
HandlerList = POINTER TO HandlerRec;
|
|
HandlerRec* =
|
|
RECORD
|
|
(Objects.ObjectRec)
|
|
handler*: EventHandler;
|
|
next*: HandlerList;
|
|
END;
|
|
SaveList = POINTER TO SaveRec;
|
|
SaveRec =
|
|
RECORD
|
|
reaction: Reaction;
|
|
handlers: HandlerList;
|
|
next: SaveList;
|
|
END;
|
|
|
|
EventTypeRec* =
|
|
RECORD
|
|
(Services.ObjectRec)
|
|
(* private components *)
|
|
handlers: HandlerList;
|
|
priority: Priority;
|
|
reaction: Reaction;
|
|
manager: EventManager;
|
|
savelist: SaveList;
|
|
END;
|
|
|
|
Queue = POINTER TO QueueRec;
|
|
QueueRec =
|
|
RECORD
|
|
priority: INTEGER; (* queue for this priority *)
|
|
head, tail: Event;
|
|
next: Queue; (* queue with lower priority *)
|
|
END;
|
|
|
|
VAR
|
|
eventTypeType: Services.Type;
|
|
|
|
CONST
|
|
priotabsize = 256; (* size of a priority table *)
|
|
maxnestlevel = 1024; (* of Raise-calls (avoids endless recursion) *)
|
|
|
|
TYPE
|
|
(* in some cases coroutines uses local priority systems *)
|
|
PrioritySystem* = POINTER TO PrioritySystemRec;
|
|
PrioritySystemRec* =
|
|
RECORD
|
|
(Objects.ObjectRec)
|
|
(* private part *)
|
|
currentPriority: Priority;
|
|
priotab: ARRAY priotabsize OF Priority;
|
|
priotop: INTEGER;
|
|
overflow: INTEGER; (* of priority table *)
|
|
END;
|
|
|
|
CONST
|
|
priorityViolation* = 0; (* priority violation (EnterPriority *)
|
|
unbalancedExitPriority* = 1; (* unbalanced call of ExitPriority *)
|
|
unbalancedRestoreReaction* = 2; (* unbalanced call of RestoreReaction *)
|
|
negPriority* = 3; (* negative priority given to SetPriority *)
|
|
errorcodes* = 4;
|
|
|
|
TYPE
|
|
ErrorEvent* = POINTER TO ErrorEventRec;
|
|
ErrorEventRec* =
|
|
RECORD
|
|
(EventRec)
|
|
errorcode*: SHORTINT;
|
|
END;
|
|
|
|
VAR
|
|
errormsg*: ARRAY errorcodes OF Message;
|
|
error*: EventType;
|
|
|
|
VAR
|
|
(* private part *)
|
|
abort, log, queueHandler: EventHandler;
|
|
nestlevel: INTEGER; (* of Raise calls *)
|
|
queue: Queue;
|
|
lock: BOOLEAN; (* lock critical operations *)
|
|
psys: PrioritySystem; (* current priority system *)
|
|
|
|
PROCEDURE ^ Define*(VAR type: EventType);
|
|
PROCEDURE ^ SetPriority*(type: EventType; priority: Priority);
|
|
PROCEDURE ^ Raise*(event: Event);
|
|
|
|
PROCEDURE InitErrorHandling;
|
|
BEGIN
|
|
Define(error); SetPriority(error, Priorities.liberrors);
|
|
errormsg[priorityViolation] :=
|
|
"priority violation (Events.EnterPriority)";
|
|
errormsg[unbalancedExitPriority] :=
|
|
"unbalanced call of Events.ExitPriority";
|
|
errormsg[unbalancedRestoreReaction] :=
|
|
"unbalanced call of Events.RestoreReaction";
|
|
errormsg[negPriority] :=
|
|
"negative priority given to Events.SetPriority";
|
|
END InitErrorHandling;
|
|
|
|
PROCEDURE Error(code: SHORTINT);
|
|
VAR event: ErrorEvent;
|
|
BEGIN
|
|
NEW(event); event.type := error;
|
|
event.message := errormsg[code];
|
|
event.errorcode := code;
|
|
Raise(event);
|
|
END Error;
|
|
|
|
PROCEDURE NilEventManager(type: EventType; reaction: Reaction);
|
|
END NilEventManager;
|
|
|
|
PROCEDURE Init*(type: EventType);
|
|
VAR
|
|
stype: Services.Type;
|
|
BEGIN
|
|
Services.GetType(type, stype); ASSERT(stype # NIL);
|
|
type.handlers := NIL;
|
|
type.priority := Priorities.default;
|
|
type.reaction := default;
|
|
type.manager := NilEventManager;
|
|
type.savelist := NIL;
|
|
END Init;
|
|
|
|
PROCEDURE Define*(VAR type: EventType);
|
|
(* definition of a new event;
|
|
an unique event number is returned;
|
|
the reaction on receipt of `type' is defined to be `default'
|
|
*)
|
|
BEGIN
|
|
NEW(type);
|
|
Services.Init(type, eventTypeType);
|
|
Init(type);
|
|
END Define;
|
|
|
|
PROCEDURE GetReaction*(type: EventType) : Reaction;
|
|
(* returns either `default', `ignore', or `funcs' *)
|
|
BEGIN
|
|
RETURN type.reaction
|
|
END GetReaction;
|
|
|
|
PROCEDURE SetPriority*(type: EventType; priority: Priority);
|
|
(* (re-)defines the priority of an event *)
|
|
BEGIN
|
|
IF priority <= 0 THEN
|
|
Error(negPriority);
|
|
ELSE
|
|
type.priority := priority;
|
|
END;
|
|
END SetPriority;
|
|
|
|
PROCEDURE GetEventPriority*(type: EventType) : Priority;
|
|
(* return the priority of the given event *)
|
|
BEGIN
|
|
RETURN type.priority
|
|
END GetEventPriority;
|
|
|
|
PROCEDURE Manager*(type: EventType; manager: EventManager);
|
|
BEGIN
|
|
type.manager := manager;
|
|
END Manager;
|
|
|
|
PROCEDURE Handler*(type: EventType; handler: EventHandler);
|
|
(* add `handler' to the list of handlers for event `type' *)
|
|
VAR
|
|
newhandler: HandlerList;
|
|
BEGIN
|
|
NEW(newhandler);
|
|
newhandler.handler := handler; newhandler.next := type.handlers;
|
|
type.handlers := newhandler;
|
|
IF type.reaction # funcs THEN
|
|
type.reaction := funcs; type.manager(type, funcs);
|
|
END;
|
|
END Handler;
|
|
|
|
PROCEDURE RemoveHandlers*(type: EventType);
|
|
(* remove list of handlers for event `type';
|
|
implies default reaction (abortion) on
|
|
receipt of `type'
|
|
*)
|
|
BEGIN
|
|
type.handlers := NIL;
|
|
IF type.reaction # default THEN
|
|
type.reaction := default; type.manager(type, default);
|
|
END;
|
|
END RemoveHandlers;
|
|
|
|
PROCEDURE Ignore*(type: EventType);
|
|
(* implies RemoveHandlers(type) and causes receipt
|
|
of `type' to be ignored
|
|
*)
|
|
BEGIN
|
|
type.handlers := NIL;
|
|
IF type.reaction # ignore THEN
|
|
type.reaction := ignore; type.manager(type, ignore);
|
|
END;
|
|
END Ignore;
|
|
|
|
PROCEDURE GetHandlers*(type: EventType; handlers: HandlerList);
|
|
(* returns the list of handlers in `handlers';
|
|
the reaction of `type' must be `funcs'
|
|
*)
|
|
BEGIN
|
|
handlers := type.handlers;
|
|
END GetHandlers;
|
|
|
|
PROCEDURE Log*(loghandler: EventHandler);
|
|
(* call `loghandler' for every event;
|
|
subsequent calls of `Log' replace the loghandler;
|
|
the loghandler is not called on default and ignore
|
|
*)
|
|
BEGIN
|
|
log := loghandler;
|
|
END Log;
|
|
|
|
PROCEDURE GetLog*(VAR loghandler: EventHandler);
|
|
(* returns the loghandler set by `Log' *)
|
|
BEGIN
|
|
loghandler := log;
|
|
END GetLog;
|
|
|
|
PROCEDURE NilHandler*(event: Event);
|
|
(* an empty event handler *)
|
|
END NilHandler;
|
|
|
|
(* now QueueHandler will translate partly like
|
|
BOOLEAN b;
|
|
handler_EventHandler tmphandler;
|
|
LONGINT i, j;
|
|
i = (LONGINT)handler;
|
|
tmphandler = handler_NilHandler;
|
|
j = (LONGINT)tmphandler;
|
|
b = i != j;
|
|
*)
|
|
(* changed because voc cannot compara handler and NilHandler -- noch *)
|
|
|
|
PROCEDURE QueueHandler*(handler: EventHandler);
|
|
(* setup an alternative handler of events
|
|
that cannot be processed now because
|
|
of their unsufficient priority
|
|
*)
|
|
VAR b : BOOLEAN; (* noch *)
|
|
tmphandler : EventHandler;
|
|
i,j : LONGINT;
|
|
BEGIN
|
|
i := SYSTEM.VAL(LONGINT, handler);
|
|
tmphandler := NilHandler;
|
|
j := SYSTEM.VAL(LONGINT, tmphandler);
|
|
b := i # j;
|
|
b := handler # tmphandler;
|
|
(*ASSERT (handler # NilHandler);*)
|
|
ASSERT(b);
|
|
queueHandler := handler;
|
|
END QueueHandler;
|
|
|
|
PROCEDURE AbortHandler*(handler: EventHandler);
|
|
(* defines the handler to be called on abortion *)
|
|
BEGIN
|
|
abort := handler;
|
|
END AbortHandler;
|
|
|
|
PROCEDURE GetAbortHandler*(VAR handler: EventHandler);
|
|
(* returns the handler set by `AbortHandler' *)
|
|
BEGIN
|
|
handler := abort;
|
|
END GetAbortHandler;
|
|
|
|
PROCEDURE ^ CallHandlers(event: Event);
|
|
|
|
PROCEDURE WorkupQueue;
|
|
VAR
|
|
ptr: Event;
|
|
BEGIN
|
|
WHILE (queue # NIL) & (queue.priority > psys.currentPriority) DO
|
|
IF SYS.TAS(lock) THEN RETURN END;
|
|
ptr := queue.head; queue := queue.next;
|
|
lock := FALSE;
|
|
WHILE ptr # NIL DO
|
|
CallHandlers(ptr);
|
|
ptr := ptr.next;
|
|
END;
|
|
END;
|
|
END WorkupQueue;
|
|
|
|
PROCEDURE CallHandlers(event: Event);
|
|
VAR
|
|
ptr: HandlerList;
|
|
oldPriority: Priority;
|
|
BEGIN
|
|
CASE event.type.reaction OF
|
|
| default: abort(event);
|
|
| ignore:
|
|
| funcs: oldPriority := psys.currentPriority;
|
|
psys.currentPriority := event.type.priority;
|
|
log(event);
|
|
ptr := event.type.handlers;
|
|
WHILE ptr # NIL DO
|
|
ptr.handler(event);
|
|
ptr := ptr.next;
|
|
END;
|
|
psys.currentPriority := oldPriority;
|
|
END;
|
|
END CallHandlers;
|
|
|
|
PROCEDURE Raise*(event: Event);
|
|
(* call all event handlers (in reverse order)
|
|
associated with event.type;
|
|
abort if there are none;
|
|
some system events may abort in another way
|
|
(i.e. they do not cause the abortion handler to be called)
|
|
*)
|
|
VAR
|
|
priority: Priority;
|
|
|
|
PROCEDURE AddToQueue(event: Event);
|
|
VAR
|
|
prev, ptr: Queue;
|
|
BEGIN
|
|
event.next := NIL;
|
|
ptr := queue; prev := NIL;
|
|
WHILE (ptr # NIL) & (ptr.priority > priority) DO
|
|
prev := ptr;
|
|
ptr := ptr.next;
|
|
END;
|
|
IF (ptr # NIL) & (ptr.priority = priority) THEN
|
|
ptr.tail.next := event;
|
|
ptr.tail := event;
|
|
ELSE
|
|
NEW(ptr);
|
|
ptr.priority := priority;
|
|
ptr.head := event; ptr.tail := event;
|
|
IF prev = NIL THEN
|
|
ptr.next := queue;
|
|
queue := ptr;
|
|
ELSE
|
|
ptr.next := prev.next;
|
|
prev.next := ptr;
|
|
END;
|
|
END;
|
|
END AddToQueue;
|
|
|
|
BEGIN (* Raise *)
|
|
INC(nestlevel);
|
|
IF nestlevel >= maxnestlevel THEN
|
|
abort(event);
|
|
ELSE
|
|
IF event.type.reaction # ignore THEN
|
|
priority := event.type.priority;
|
|
IF psys.currentPriority < priority THEN
|
|
CallHandlers(event); WorkupQueue;
|
|
ELSIF queueHandler # NIL THEN
|
|
queueHandler(event);
|
|
ELSIF ~SYS.TAS(lock) THEN
|
|
AddToQueue(event);
|
|
lock := FALSE;
|
|
END;
|
|
END;
|
|
END;
|
|
DEC(nestlevel);
|
|
END Raise;
|
|
|
|
PROCEDURE CreatePrioritySystem*(VAR prioritySystem: PrioritySystem);
|
|
(* create and initialize a new priority system *)
|
|
BEGIN
|
|
NEW(prioritySystem);
|
|
prioritySystem.currentPriority := Priorities.base;
|
|
prioritySystem.priotop := 0;
|
|
END CreatePrioritySystem;
|
|
|
|
PROCEDURE CurrentPrioritySystem*() : PrioritySystem;
|
|
(* return the priority system currently active *)
|
|
BEGIN
|
|
RETURN psys
|
|
END CurrentPrioritySystem;
|
|
|
|
PROCEDURE SwitchPrioritySystem*(prioritySystem: PrioritySystem);
|
|
(* switch to another priority system; this is typically
|
|
done in case of task switches
|
|
*)
|
|
BEGIN
|
|
psys := prioritySystem;
|
|
END SwitchPrioritySystem;
|
|
|
|
PROCEDURE EnterPriority*(priority: Priority);
|
|
(* sets the current priority to `priority';
|
|
it is an error to give a priority less than
|
|
the current priority (event `badpriority')
|
|
*)
|
|
BEGIN
|
|
IF psys.currentPriority <= priority THEN
|
|
IF (psys.overflow = 0) & (psys.priotop < priotabsize) THEN
|
|
psys.priotab[psys.priotop] := psys.currentPriority;
|
|
INC(psys.priotop);
|
|
psys.currentPriority := priority;
|
|
ELSE
|
|
INC(psys.overflow);
|
|
END;
|
|
ELSE
|
|
Error(priorityViolation);
|
|
INC(psys.overflow);
|
|
END;
|
|
END EnterPriority;
|
|
|
|
PROCEDURE AssertPriority*(priority: Priority);
|
|
(* current priority
|
|
< priority: set the current priority to `priority'
|
|
>= priority: the current priority remains unchanged
|
|
*)
|
|
BEGIN
|
|
IF (psys.overflow = 0) & (psys.priotop < priotabsize) THEN
|
|
psys.priotab[psys.priotop] := psys.currentPriority; INC(psys.priotop);
|
|
IF psys.currentPriority < priority THEN
|
|
psys.currentPriority := priority;
|
|
END;
|
|
ELSE
|
|
INC(psys.overflow);
|
|
END;
|
|
END AssertPriority;
|
|
|
|
PROCEDURE ExitPriority*;
|
|
(* causes the priority before the last effective call
|
|
of SetPriority or AssertPriority to be restored
|
|
*)
|
|
BEGIN
|
|
IF psys.overflow > 0 THEN
|
|
DEC(psys.overflow);
|
|
ELSIF psys.priotop = 0 THEN
|
|
Error(unbalancedExitPriority);
|
|
ELSE
|
|
DEC(psys.priotop); psys.currentPriority := psys.priotab[psys.priotop];
|
|
WorkupQueue;
|
|
END;
|
|
END ExitPriority;
|
|
|
|
PROCEDURE GetPriority*() : Priority;
|
|
(* returns the current priority *)
|
|
BEGIN
|
|
RETURN psys.currentPriority
|
|
END GetPriority;
|
|
|
|
PROCEDURE SaveReaction*(type: EventType);
|
|
(* saves current reaction until call of RestoreReaction;
|
|
the new reaction of `type' is defined to be `ignore'
|
|
but can be changed by Events.Handler or Events.RemoveHandlers
|
|
*)
|
|
VAR
|
|
savelist: SaveList;
|
|
BEGIN
|
|
NEW(savelist);
|
|
savelist.reaction := type.reaction;
|
|
savelist.handlers := type.handlers;
|
|
savelist.next := type.savelist;
|
|
type.savelist := savelist;
|
|
type.handlers := NIL;
|
|
IF type.reaction # ignore THEN
|
|
type.reaction := ignore; type.manager(type, ignore);
|
|
END;
|
|
END SaveReaction;
|
|
|
|
PROCEDURE RestoreReaction*(type: EventType);
|
|
(* restores old reaction;
|
|
must be properly nested
|
|
*)
|
|
VAR
|
|
savelist: SaveList;
|
|
BEGIN
|
|
IF type.savelist = NIL THEN
|
|
Error(unbalancedRestoreReaction);
|
|
ELSE
|
|
savelist := type.savelist;
|
|
type.savelist := savelist.next;
|
|
type.handlers := savelist.handlers;
|
|
IF type.reaction # savelist.reaction THEN
|
|
type.reaction := savelist.reaction;
|
|
type.manager(type, savelist.reaction);
|
|
END;
|
|
END;
|
|
END RestoreReaction;
|
|
|
|
BEGIN
|
|
CreatePrioritySystem(psys);
|
|
|
|
Services.CreateType(eventTypeType, "Events.EventType", "");
|
|
|
|
abort := NilHandler; log := NilHandler; queueHandler := NIL;
|
|
nestlevel := 0;
|
|
queue := NIL;
|
|
lock := FALSE;
|
|
|
|
InitErrorHandling;
|
|
END ulmEvents.
|