MODULE Events;
IMPORT
KernelLog, Modules, Clock;
CONST
Unknown* = -1;
Undefined* = 0;
Information* = 1;
Warning* = 2;
Error* = 3;
Critical* = 4;
Alert* = 5;
Failure* = 6;
Running = 1;
Terminating = 2;
Terminated = 3;
QueueSize= 256;
ModuleName = "Events";
Verbose = TRUE;
Debug = FALSE;
TYPE
Name* = Modules.Name;
Message* = ARRAY 468 OF CHAR;
Event* = RECORD
originator* : Name;
type*, class*, subclass*, code* : SHORTINT;
message* : Message;
date*, time* : LONGINT;
END;
TYPE
Sink* = OBJECT
VAR
name* : Name;
next : Sink;
filters : Filter;
PROCEDURE AddFilter*(filter : Filter);
BEGIN {EXCLUSIVE}
IF filters = NIL THEN
filters := filter;
ELSE
filter.next := filters;
filters := filter;
END;
END AddFilter;
PROCEDURE RemoveFilter*(filter : Filter);
VAR f : Filter;
BEGIN {EXCLUSIVE}
IF filters = filter THEN
filters := filters.next;
ELSE
f := filters; WHILE(f.next # NIL) & (f.next # filter) DO f := f.next; END;
IF f.next # NIL THEN
f.next := f.next.next;
END;
END;
END RemoveFilter;
PROCEDURE HandleInternal(event : Event);
VAR f : Filter; discard : BOOLEAN;
BEGIN
f := filters; WHILE ((f # NIL) & ~discard) DO f.Filter(event, discard); END;
IF ~discard THEN
Handle(event);
END;
END HandleInternal;
PROCEDURE Handle*(event : Event);
BEGIN HALT(301); END Handle;
END Sink;
TYPE
FilterO* = OBJECT
VAR
next : FilterO;
PROCEDURE Filter*(event : Event; VAR discard : BOOLEAN);
BEGIN HALT(301); END Filter;
END FilterO;
Filter*= FilterO;
TYPE
EventQueue = OBJECT
VAR
size : LONGINT;
head, len: LONGINT;
ringbuffer : POINTER TO ARRAY OF Event;
running : BOOLEAN;
PROCEDURE Enqueue(event : Event) : BOOLEAN;
BEGIN {EXCLUSIVE}
IF ~IsFull() THEN
Clock.Get(event.time, event.date);
ringbuffer[(head + len) MOD size] := event;
INC(len);
RETURN TRUE;
ELSE
RETURN FALSE;
END;
END Enqueue;
PROCEDURE Dequeue(VAR event : Event);
BEGIN {EXCLUSIVE}
AWAIT(len > 0);
event := ringbuffer[head];
head := (head + 1) MOD size;
DEC(len);
END Dequeue;
PROCEDURE IsFull() : BOOLEAN;
BEGIN
RETURN len = QueueSize;
END IsFull;
PROCEDURE IsEmpty() : BOOLEAN;
BEGIN {EXCLUSIVE}
RETURN len = 0;
END IsEmpty;
PROCEDURE AwaitEvents;
BEGIN {EXCLUSIVE}
AWAIT((len > 0) OR (~running));
END AwaitEvents;
PROCEDURE Stop;
BEGIN {EXCLUSIVE}
running := FALSE;
END Stop;
PROCEDURE &Init*(size : LONGINT);
BEGIN
running := TRUE;
head := 0; len := 0;
SELF.size := size;
NEW(ringbuffer, size);
END Init;
END EventQueue;
TYPE
EventDispatcher = OBJECT
VAR
state : LONGINT;
queue : EventQueue;
sinks : Sink;
PROCEDURE Register(sink : Sink);
BEGIN {EXCLUSIVE}
ASSERT(sink # NIL);
sink.next := sinks.next;
sinks.next := sink;
INC(NnofListeners);
END Register;
PROCEDURE Unregister(sink : Sink);
VAR s : Sink;
BEGIN {EXCLUSIVE}
s := sinks; WHILE (s.next # NIL) & (s.next # sink) DO s := s.next; END;
IF s.next # NIL THEN
s.next := s.next.next;
DEC(NnofListeners);
END;
END Unregister;
PROCEDURE Dispatch(event : Event);
VAR sink : Sink;
BEGIN {EXCLUSIVE}
sink := sinks.next;
IF sink # NIL THEN
WHILE (sink # NIL) DO
sink.HandleInternal(event);
sink := sink.next;
END;
INC(NnofEventsHandled);
ELSE
INC(NnofEventsNotHandled);
END;
END Dispatch;
PROCEDURE DispatchEvents;
VAR event : Event;
BEGIN
WHILE (state = Running) & ~queue.IsEmpty() DO
queue.Dequeue(event);
Dispatch(event);
END;
END DispatchEvents;
PROCEDURE Stop;
BEGIN
BEGIN {EXCLUSIVE} state := Terminating; END;
queue.Stop;
BEGIN {EXCLUSIVE} AWAIT(state = Terminated); END;
END Stop;
PROCEDURE &Init*;
BEGIN
NEW(sinks);
NEW(queue, QueueSize);
state := Running;
END Init;
BEGIN {ACTIVE}
WHILE state = Running DO
DispatchEvents;
queue.AwaitEvents();
END;
BEGIN {EXCLUSIVE} state := Terminated; END;
END EventDispatcher;
VAR
dispatcher : EventDispatcher;
NnofEvents-, NnofDiscarded-, NnofEventsHandled-, NnofEventsNotHandled-,
NnofUnknown-, NnofUndefined-,
NnofInformation-, NnofWarning-, NnofError-, NnofCritical-, NnofFailure-,
NnofListeners- : LONGINT;
PROCEDURE Register*(sink : Sink);
BEGIN {EXCLUSIVE}
ASSERT(sink # NIL);
dispatcher.Register(sink);
INC(NnofListeners);
END Register;
PROCEDURE Unregister*(sink : Sink);
BEGIN {EXCLUSIVE}
ASSERT(sink # NIL);
dispatcher.Unregister(sink);
DEC(NnofListeners);
END Unregister;
PROCEDURE Add*(event : Event; showOnKernelLog : BOOLEAN);
VAR discarded : BOOLEAN;
BEGIN
IF Debug OR showOnKernelLog THEN
ShowOnKernelLog(event);
END;
discarded := ~dispatcher.queue.Enqueue(event);
UpdateStats(event, discarded);
END Add;
PROCEDURE AddEvent*(CONST originator : Name; type, class, subclass, code : SHORTINT; CONST message : Message; showOnKernelLog : BOOLEAN);
BEGIN
Add(NewEvent(originator, type, class, subclass, code, message), showOnKernelLog)
END AddEvent;
PROCEDURE NewEvent*(CONST originator : Name; type, class, subclass, code : SHORTINT; CONST message : Message) : Event;
VAR event : Event;
BEGIN
event.originator := originator;
event.type := type;
event.class := class;
event.subclass := subclass;
event.code := code;
event.message := message;
RETURN event;
END NewEvent;
PROCEDURE ShowOnKernelLog(event : Event);
BEGIN
KernelLog.Enter; KernelLog.String(event.originator); KernelLog.String(": "); KernelLog.String(event.message); KernelLog.Exit;
END ShowOnKernelLog;
PROCEDURE UpdateStats(event : Event; discarded : BOOLEAN);
BEGIN {EXCLUSIVE}
INC(NnofEvents);
IF discarded THEN
INC(NnofDiscarded);
ELSE
CASE event.type OF
|Undefined: INC(NnofUndefined);
|Information: INC(NnofInformation);
|Warning : INC(NnofWarning);
|Error: INC(NnofError);
|Critical: INC(NnofCritical);
|Failure: INC(NnofFailure);
ELSE
INC(NnofUnknown);
END;
END;
END UpdateStats;
PROCEDURE ClearStats*;
BEGIN {EXCLUSIVE}
NnofEvents := 0; NnofDiscarded := 0;
NnofUnknown := 0; NnofUndefined := 0;
NnofInformation := 0; NnofWarning := 0; NnofError := 0; NnofCritical := 0; NnofFailure := 0;
END ClearStats;
PROCEDURE Install*;
END Install;
PROCEDURE Cleanup;
BEGIN
dispatcher.Stop;
IF Verbose THEN KernelLog.Enter; KernelLog.String(ModuleName); KernelLog.String(": System event log shut down."); KernelLog.Exit; END;
END Cleanup;
BEGIN
NEW(dispatcher);
Modules.InstallTermHandler(Cleanup);
IF Verbose THEN KernelLog.Enter; KernelLog.String(ModuleName); KernelLog.String(": System event log started."); KernelLog.Exit; END;
END Events.
PC.Compile \s
Events.Mod EventsUtils.Mod EventsMemoryLog.Mod EventsKernelLog.Mod EventsFileLog.Mod WMEventLog.Mod
~
SystemTools.Free
EventsKernelLog WMEventLog EventsFileLog EventsMemoryLog EventsUtils Events
~
Events.Install ~
EventsKernelLog.Install ~
EventsFileLog.Start test.log ~ EventsFileLog.Stop ~
EventsMemoryLog.Install ~
WMEventLog.Open ~
WMEventLog.OpenFile test.log ~
EventsUtils.GenerateEvent Tester 6 0 0 0 "This is a test event" ~