MODULE A2Sequencers;
IMPORT Machine, Streams, Objects, Kernel;
CONST
NoDelay* = 0;
MaxHandlers = 10;
TYPE
Property* = OBJECT
VAR
locks: LONGINT;
container*: OBJECT;
PROCEDURE &InitProperty;
BEGIN locks := 0; container := NIL;
END InitProperty;
PROCEDURE AcquireRead;
VAR locks: LONGINT;
BEGIN
LOOP
locks := SELF.locks;
IF (locks >= 0) & (Machine.AtomicCAS (SELF.locks, locks, locks + 1) = locks) THEN EXIT END;
Objects.Yield;
END;
END AcquireRead;
PROCEDURE ReleaseRead;
BEGIN Machine.AtomicDec (locks);
END ReleaseRead;
PROCEDURE AcquireWrite;
VAR locks: LONGINT;
BEGIN
LOOP
locks := SELF.locks;
IF (locks = 0) & (Machine.AtomicCAS (SELF.locks, locks, locks - 1) = locks) THEN EXIT END;
Objects.Yield;
END;
END AcquireWrite;
PROCEDURE ReleaseWrite;
BEGIN Machine.AtomicInc (locks);
END ReleaseWrite;
PROCEDURE ToStream*(w : Streams.Writer);
END ToStream;
PROCEDURE FromStream*(r : Streams.Reader);
END FromStream;
END Property;
Boolean* = OBJECT (Property)
VAR
value: BOOLEAN;
handlers: ARRAY MaxHandlers OF BooleanHandler;
PROCEDURE &InitBoolean* (value: BOOLEAN);
BEGIN InitProperty; SELF.value := value;
END InitBoolean;
PROCEDURE Get* (): BOOLEAN;
VAR value: BOOLEAN;
BEGIN AcquireRead; value := SELF.value; ReleaseRead; RETURN value;
END Get;
PROCEDURE Set* (value: BOOLEAN);
VAR changed: BOOLEAN;
BEGIN AcquireWrite; changed := SELF.value # value; SELF.value := value; ReleaseWrite; IF changed THEN Changed (value) END;
END Set;
PROCEDURE Changed (value: BOOLEAN);
VAR i: LONGINT;
BEGIN i := 0; WHILE handlers[i] # NIL DO handlers[i] (SELF, value); INC (i) END;
END Changed;
PROCEDURE AddHandler* (handler: BooleanHandler);
VAR i: LONGINT;
BEGIN i := 0; WHILE handlers[i] # NIL DO INC (i) END; handlers[i] := handler;
END AddHandler;
END Boolean;
Integer* = OBJECT (Property)
VAR
value: LONGINT;
handlers: ARRAY MaxHandlers OF IntegerHandler;
PROCEDURE &InitInteger* (value: LONGINT);
BEGIN InitProperty; SELF.value := value;
END InitInteger;
PROCEDURE Get* (): LONGINT;
VAR value: LONGINT;
BEGIN AcquireRead; value := SELF.value; ReleaseRead; RETURN value;
END Get;
PROCEDURE Set* (value: LONGINT);
VAR changed: BOOLEAN;
BEGIN AcquireWrite; changed := SELF.value # value; SELF.value := value; ReleaseWrite; IF changed THEN Changed (value) END;
END Set;
PROCEDURE Inc* (step: LONGINT);
VAR changed: BOOLEAN;
BEGIN AcquireWrite; changed := step # 0; INC (value, step); ReleaseWrite; IF changed THEN Changed (value) END;
END Inc;
PROCEDURE Dec* (step: LONGINT);
VAR changed: BOOLEAN;
BEGIN AcquireWrite; changed := step # 0; DEC (value, step); ReleaseWrite; IF changed THEN Changed (value) END;
END Dec;
PROCEDURE Changed (value: LONGINT);
VAR i: LONGINT;
BEGIN i := 0; WHILE handlers[i] # NIL DO handlers[i] (SELF, value); INC (i) END;
END Changed;
PROCEDURE AddHandler* (handler: IntegerHandler);
VAR i: LONGINT;
BEGIN i := 0; WHILE handlers[i] # NIL DO INC (i) END; handlers[i] := handler;
END AddHandler;
END Integer;
Real* = OBJECT (Property)
VAR
value: REAL;
handlers: ARRAY MaxHandlers OF RealHandler;
PROCEDURE &InitReal* (value: REAL);
BEGIN InitProperty; SELF.value := value;
END InitReal;
PROCEDURE Get* (): REAL;
VAR value: REAL;
BEGIN AcquireRead; value := SELF.value; ReleaseRead; RETURN value;
END Get;
PROCEDURE Set* (value: REAL);
VAR changed: BOOLEAN;
BEGIN AcquireWrite; changed := SELF.value # value; SELF.value := value; ReleaseWrite; IF changed THEN Changed (value) END;
END Set;
PROCEDURE Changed (value: REAL);
VAR i: LONGINT;
BEGIN i := 0; WHILE handlers[i] # NIL DO handlers[i] (SELF, value); INC (i) END;
END Changed;
PROCEDURE AddHandler* (handler: RealHandler);
VAR i: LONGINT;
BEGIN i := 0; WHILE handlers[i] # NIL DO INC (i) END; handlers[i] := handler;
END AddHandler;
END Real;
Set* = OBJECT (Property)
VAR
value: SET;
handlers: ARRAY MaxHandlers OF SetHandler;
PROCEDURE &InitSet* (value: SET);
BEGIN InitProperty; SELF.value := value;
END InitSet;
PROCEDURE Get* (): SET;
VAR value: SET;
BEGIN AcquireRead; value := SELF.value; ReleaseRead; RETURN value;
END Get;
PROCEDURE Set* (value: SET);
VAR changed: BOOLEAN;
BEGIN AcquireWrite; changed := SELF.value # value; SELF.value := value; ReleaseWrite; IF changed THEN Changed (value) END;
END Set;
PROCEDURE Incl* (element: LONGINT);
VAR changed: BOOLEAN;
BEGIN AcquireWrite; changed := ~(element IN value); INCL (value, element); ReleaseWrite; IF changed THEN Changed (value) END;
END Incl;
PROCEDURE Excl* (element: LONGINT);
VAR changed: BOOLEAN;
BEGIN AcquireWrite; changed := element IN value; EXCL (value, element); ReleaseWrite; IF changed THEN Changed (value) END;
END Excl;
PROCEDURE Changed (value: SET);
VAR i: LONGINT;
BEGIN i := 0; WHILE handlers[i] # NIL DO handlers[i] (SELF, value); INC (i) END;
END Changed;
PROCEDURE AddHandler* (handler: SetHandler);
VAR i: LONGINT;
BEGIN i := 0; WHILE handlers[i] # NIL DO INC (i) END; handlers[i] := handler;
END AddHandler;
END Set;
String* = OBJECT (Property)
VAR
value: POINTER TO ARRAY OF CHAR;
handlers: ARRAY MaxHandlers OF StringHandler;
PROCEDURE &InitString* (CONST value: ARRAY OF CHAR; length: LONGINT);
BEGIN InitProperty; NEW (SELF.value, length); COPY (value, SELF.value^);
END InitString;
PROCEDURE Get* (VAR value: ARRAY OF CHAR);
BEGIN AcquireRead; COPY (SELF.value^, value); ReleaseRead;
END Get;
PROCEDURE Set* (CONST value: ARRAY OF CHAR);
VAR changed: BOOLEAN;
BEGIN AcquireWrite; changed := SELF.value^ # value; COPY (value, SELF.value^); ReleaseWrite; IF changed THEN Changed (value) END;
END Set;
PROCEDURE Changed (CONST value: ARRAY OF CHAR);
VAR i: LONGINT;
BEGIN i := 0; WHILE handlers[i] # NIL DO handlers[i] (SELF, value); INC (i) END;
END Changed;
PROCEDURE AddHandler* (handler: StringHandler);
VAR i: LONGINT;
BEGIN i := 0; WHILE handlers[i] # NIL DO INC (i) END; handlers[i] := handler;
END AddHandler;
END String;
Message* = OBJECT
VAR
next: Message; time: LONGINT;
PROCEDURE &InitMessage*;
BEGIN SELF.next := NIL; time := NoDelay;
END InitMessage;
PROCEDURE Handle*;
END Handle;
END Message;
TYPE ProcedureMessage* = OBJECT (Message)
VAR
procedure: Procedure;
PROCEDURE &InitProcedureMessage* (procedure: Procedure);
BEGIN InitMessage; SELF.procedure := procedure;
END InitProcedureMessage;
PROCEDURE Handle;
BEGIN procedure;
END Handle;
END ProcedureMessage;
TYPE BooleanMessage* = OBJECT (Message)
VAR
value: BOOLEAN;
procedure: BooleanProcedure;
PROCEDURE &InitBooleanMessage* (value: BOOLEAN; procedure: BooleanProcedure);
BEGIN InitMessage; SELF.value := value; SELF.procedure := procedure;
END InitBooleanMessage;
PROCEDURE Handle;
BEGIN procedure (value);
END Handle;
END BooleanMessage;
TYPE IntegerMessage* = OBJECT (Message)
VAR
value: LONGINT;
procedure: IntegerProcedure;
PROCEDURE &InitIntegerMessage* (value: LONGINT; procedure: IntegerProcedure);
BEGIN InitMessage; SELF.value := value; SELF.procedure := procedure;
END InitIntegerMessage;
PROCEDURE Handle;
BEGIN procedure (value);
END Handle;
END IntegerMessage;
TYPE IntegerIntegerMessage* = OBJECT (Message)
VAR
value0, value1: LONGINT;
procedure: IntegerIntegerProcedure;
PROCEDURE &InitIntegerIntegerMessage* (value0, value1: LONGINT; procedure: IntegerIntegerProcedure);
BEGIN InitMessage; SELF.value0 := value0; SELF.value1 := value1; SELF.procedure := procedure;
END InitIntegerIntegerMessage;
PROCEDURE Handle;
BEGIN procedure (value0, value1);
END Handle;
END IntegerIntegerMessage;
TYPE RealMessage* = OBJECT (Message)
VAR
value: REAL;
procedure: RealProcedure;
PROCEDURE &InitRealMessage* (value: REAL; procedure: RealProcedure);
BEGIN InitMessage; SELF.value := value; SELF.procedure := procedure;
END InitRealMessage;
PROCEDURE Handle;
BEGIN procedure (value);
END Handle;
END RealMessage;
TYPE SetMessage* = OBJECT (Message)
VAR
value: SET;
procedure: SetProcedure;
PROCEDURE &InitSetMessage* (value: SET; procedure: SetProcedure);
BEGIN InitMessage; SELF.value := value; SELF.procedure := procedure;
END InitSetMessage;
PROCEDURE Handle;
BEGIN procedure (value);
END Handle;
END SetMessage;
TYPE StringMessage* = OBJECT (Message)
VAR
value: POINTER TO ARRAY OF CHAR;
procedure: StringProcedure;
PROCEDURE &InitStringMessage* (CONST value: ARRAY OF CHAR; procedure: StringProcedure);
VAR length: LONGINT;
BEGIN
InitMessage; length := 0;
WHILE value[length] # 0X DO INC (length); END;
NEW (SELF.value, length); COPY (value, SELF.value^); SELF.procedure := procedure;
END InitStringMessage;
PROCEDURE Handle;
BEGIN procedure (value^);
END Handle;
END StringMessage;
Request* = OBJECT (Message)
VAR
handled: BOOLEAN;
PROCEDURE &InitRequest*;
BEGIN InitMessage; handled := FALSE;
END InitRequest;
PROCEDURE Handle;
BEGIN {EXCLUSIVE} handled := TRUE
END Handle;
PROCEDURE Await;
BEGIN {EXCLUSIVE} AWAIT (handled);
END Await;
END Request;
IntegerRequest* = OBJECT (Request)
VAR
value: LONGINT;
procedure: IntegerProcedure;
PROCEDURE &InitIntegerRequest* (value: LONGINT; procedure: IntegerProcedure);
BEGIN InitRequest; SELF.value := value; SELF.procedure := procedure;
END InitIntegerRequest;
PROCEDURE Handle;
BEGIN procedure (value); Handle^;
END Handle;
END IntegerRequest;
RequestBoolean* = OBJECT (Request)
VAR
procedure: ProcedureBoolean;
result-: BOOLEAN;
PROCEDURE &InitRequestBoolean* (procedure: ProcedureBoolean);
BEGIN InitRequest; SELF.procedure := procedure;
END InitRequestBoolean;
PROCEDURE Handle;
BEGIN result := procedure (); Handle^;
END Handle;
END RequestBoolean;
RequestInteger* = OBJECT (Request)
VAR
procedure: ProcedureInteger;
result-: LONGINT;
PROCEDURE &InitRequestInteger* (procedure: ProcedureInteger);
BEGIN InitRequest; SELF.procedure := procedure;
END InitRequestInteger;
PROCEDURE Handle;
BEGIN result := procedure (); Handle^;
END Handle;
END RequestInteger;
RequestReal* = OBJECT (Request)
VAR
procedure: ProcedureReal;
result-: REAL;
PROCEDURE &InitRequestReal* (procedure: ProcedureReal);
BEGIN InitRequest; SELF.procedure := procedure;
END InitRequestReal;
PROCEDURE Handle;
BEGIN result := procedure (); Handle^;
END Handle;
END RequestReal;
IntegerRequestBoolean* = OBJECT (Request)
VAR
value: LONGINT;
procedure: IntegerProcedureBoolean;
result-: BOOLEAN;
PROCEDURE &InitIntegerRequestBoolean* (value: LONGINT; procedure: IntegerProcedureBoolean);
BEGIN InitRequest; SELF.value := value; SELF.procedure := procedure;
END InitIntegerRequestBoolean;
PROCEDURE Handle;
BEGIN result := procedure (value); Handle^;
END Handle;
END IntegerRequestBoolean;
RealRequestInteger* = OBJECT (Request)
VAR
value: REAL;
procedure: RealProcedureInteger;
result-: LONGINT;
PROCEDURE &InitRealRequestInteger* (value: REAL; procedure: RealProcedureInteger);
BEGIN InitRequest; SELF.value := value; SELF.procedure := procedure;
END InitRealRequestInteger;
PROCEDURE Handle;
BEGIN result := procedure (value); Handle^;
END Handle;
END RealRequestInteger;
Sequencer* = OBJECT
VAR
handling, woken: BOOLEAN; first: Message; timer: Objects.Timer;
PROCEDURE &InitSequencer*;
BEGIN handling := TRUE; woken := FALSE; first := NIL; NEW (timer);
END InitSequencer;
PROCEDURE SequencerCalledThis* (): BOOLEAN;
BEGIN RETURN Objects.ActiveObject() = SELF;
END SequencerCalledThis;
PROCEDURE HandleMessages;
VAR message: Message;
BEGIN {EXCLUSIVE}
WHILE first # NIL DO
IF (first.time # NoDelay) & (first.time - Kernel.GetTicks () > 0) THEN RETURN END;
message := first; first := message.next; message.next := NIL; message.Handle;
END;
END HandleMessages;
PROCEDURE Add*(message: Message; time: LONGINT);
VAR prev, next: Message;
BEGIN
BEGIN {EXCLUSIVE}
ASSERT (~SequencerCalledThis ());
ASSERT (message.next = NIL);
prev := NIL; next := first;
WHILE (next # NIL) & (next.time <= time) DO prev := next; next := next.next END;
IF prev = NIL THEN first := message; woken := time # NoDelay; ELSE prev.next := message END;
message.next := next; message.time := time;
END;
IF message IS Request THEN message(Request).Await END;
END Add;
PROCEDURE AddMessage* (procedure: Procedure);
VAR message: ProcedureMessage;
BEGIN NEW (message, procedure); Add (message, NoDelay);
END AddMessage;
PROCEDURE AddBooleanMessage* (value: BOOLEAN; procedure: BooleanProcedure);
VAR message: BooleanMessage;
BEGIN NEW (message, value, procedure); Add (message, NoDelay);
END AddBooleanMessage;
PROCEDURE AddIntegerMessage* (value: LONGINT; procedure: IntegerProcedure);
VAR message: IntegerMessage;
BEGIN NEW (message, value, procedure); Add (message, NoDelay);
END AddIntegerMessage;
PROCEDURE AddRealMessage* (value: REAL; procedure: RealProcedure);
VAR message: RealMessage;
BEGIN NEW (message, value, procedure); Add (message, NoDelay);
END AddRealMessage;
PROCEDURE AddSetMessage* (value: SET; procedure: SetProcedure);
VAR message: SetMessage;
BEGIN NEW (message, value, procedure); Add (message, NoDelay);
END AddSetMessage;
PROCEDURE AddStringMessage* (CONST value: ARRAY OF CHAR; procedure: StringProcedure);
VAR message: StringMessage;
BEGIN NEW (message, value, procedure); Add (message, NoDelay);
END AddStringMessage;
PROCEDURE AddIntegerIntegerMessage* (value0, value1: LONGINT; procedure: IntegerIntegerProcedure);
VAR message: IntegerIntegerMessage;
BEGIN NEW (message, value0, value1, procedure); Add (message, NoDelay);
END AddIntegerIntegerMessage;
PROCEDURE AddIntegerRequest* (value: LONGINT; procedure: IntegerProcedure);
VAR request: IntegerRequest;
BEGIN NEW (request, value, procedure); Add (request, NoDelay);
END AddIntegerRequest;
PROCEDURE AddRequestBoolean* (procedure: ProcedureBoolean): BOOLEAN;
VAR request: RequestBoolean;
BEGIN NEW (request, procedure); Add (request, NoDelay); RETURN request.result;
END AddRequestBoolean;
PROCEDURE AddRequestInteger* (procedure: ProcedureInteger): LONGINT;
VAR request: RequestInteger;
BEGIN NEW (request, procedure); Add (request, NoDelay); RETURN request.result;
END AddRequestInteger;
PROCEDURE AddRequestReal* (procedure: ProcedureReal): REAL;
VAR request: RequestReal;
BEGIN NEW (request, procedure); Add (request, NoDelay); RETURN request.result;
END AddRequestReal;
PROCEDURE AddIntegerRequestBoolean* (value: LONGINT; procedure: IntegerProcedureBoolean): BOOLEAN;
VAR request: IntegerRequestBoolean;
BEGIN NEW (request, value, procedure); Add (request, NoDelay); RETURN request.result;
END AddIntegerRequestBoolean;
PROCEDURE AddRealRequestInteger* (value: REAL; procedure: RealProcedureInteger): LONGINT;
VAR request: RealRequestInteger;
BEGIN NEW (request, value, procedure); Add (request, NoDelay); RETURN request.result;
END AddRealRequestInteger;
PROCEDURE Remove*(message: Message);
VAR prev, next: Message;
BEGIN
ASSERT (SequencerCalledThis ());
IF message = NIL THEN RETURN END;
prev := NIL; next := first;
WHILE (next # NIL) & (next # message) DO prev := next; next := next.next END;
IF next = message THEN
IF prev = NIL THEN first := message.next; woken := TRUE; ELSE prev.next := message.next END;
END;
message.next := NIL;
END Remove;
PROCEDURE Handle*;
BEGIN
IF (first # NIL) & (first.time # NoDelay) THEN Objects.SetTimeoutAt (timer, Wakeup, first.time) END;
AWAIT ((first # NIL) & (first.time = NoDelay) OR ~handling OR woken);
Objects.CancelTimeout (timer); woken := FALSE;
END Handle;
PROCEDURE Wakeup;
BEGIN {EXCLUSIVE} woken := TRUE;
END Wakeup;
PROCEDURE Stop*;
BEGIN {EXCLUSIVE} handling := FALSE;
END Stop;
BEGIN {ACTIVE}
WHILE handling DO HandleMessages; BEGIN {EXCLUSIVE} Handle END END;
END Sequencer;
BooleanHandler = PROCEDURE {DELEGATE} (property: Boolean; value: BOOLEAN);
IntegerHandler = PROCEDURE {DELEGATE} (property: Integer; value: LONGINT);
RealHandler = PROCEDURE {DELEGATE} (property: Real; value: REAL);
SetHandler = PROCEDURE {DELEGATE} (property: Set; value: SET);
StringHandler = PROCEDURE {DELEGATE} (property: String; CONST value: ARRAY OF CHAR);
Procedure = PROCEDURE {DELEGATE};
BooleanProcedure = PROCEDURE {DELEGATE} (value: BOOLEAN);
IntegerProcedure = PROCEDURE {DELEGATE} (value: LONGINT);
IntegerIntegerProcedure = PROCEDURE {DELEGATE} (value0, value1: LONGINT);
RealProcedure = PROCEDURE {DELEGATE} (value: REAL);
SetProcedure = PROCEDURE {DELEGATE} (value: SET);
StringProcedure = PROCEDURE {DELEGATE} (CONST value: ARRAY OF CHAR);
ProcedureBoolean = PROCEDURE {DELEGATE} (): BOOLEAN;
ProcedureInteger = PROCEDURE {DELEGATE} (): LONGINT;
ProcedureReal = PROCEDURE {DELEGATE} (): REAL;
IntegerProcedureBoolean = PROCEDURE {DELEGATE} (value: LONGINT): BOOLEAN;
RealProcedureInteger = PROCEDURE {DELEGATE} (value: REAL): LONGINT;
PROCEDURE Delay* (delay: LONGINT): LONGINT;
BEGIN RETURN delay + Kernel.GetTicks ();
END Delay;
END A2Sequencers.
Open issues:
- first parameter of event procedures concrete or abstract property type?
- no concrete request types implemented
- serializable interface of properties not implemented