123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642 |
- MODULE A2Sequencers; (** AUTHOR "negelef"; PURPOSE "Generic A2 Sequencer"; *)
- (*
- This module provides a generic sequencer base class that allows deriving active objects to communicate sequentially over messages.
- Messages are handled sequentially and provide atomic and exclusive access to the state of a sequencer. Requests are special messages
- which allow the caller to block and wait for the sequencer to handle the request. This is useful to retrieve a set of states of the sequencer.
- Code in procedures of a sequencer must make sure that they are called by their own sequencer object (using the SequencerCalledThis
- procedure) and have to add a corresponding message otherwise. If sequencers share variables, they can also put it into property objects
- which support atomic access to their values and a registration mechanism for notification handlers.
- *)
- IMPORT Machine, Streams, Objects, Kernel;
- CONST
- NoDelay* = 0;
- MaxHandlers = 10;
- TYPE
- (* generic property object that provides lock-free access to its value *)
- Property* = OBJECT
- VAR
- locks: WORD;
- container*: OBJECT;
- PROCEDURE &InitProperty;
- BEGIN locks := 0; container := NIL;
- END InitProperty;
- PROCEDURE AcquireRead;
- VAR locks: WORD;
- 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: WORD;
- 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; (* abstract *)
- PROCEDURE FromStream*(r : Streams.Reader);
- END FromStream; (* abstract *)
- 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: SIZE;
- BEGIN i := 0; WHILE handlers[i] # NIL DO handlers[i] (SELF, value); INC (i) END;
- END Changed;
- PROCEDURE AddHandler* (handler: BooleanHandler);
- VAR i: SIZE;
- BEGIN i := 0; WHILE handlers[i] # NIL DO INC (i) END; handlers[i] := handler;
- END AddHandler;
- END Boolean;
- Integer* = OBJECT (Property)
- VAR
- value: WORD;
- handlers: ARRAY MaxHandlers OF IntegerHandler;
- PROCEDURE &InitInteger* (value: WORD);
- BEGIN InitProperty; SELF.value := value;
- END InitInteger;
- PROCEDURE Get* (): WORD;
- VAR value: WORD;
- BEGIN AcquireRead; value := SELF.value; ReleaseRead; RETURN value;
- END Get;
- PROCEDURE Set* (value: WORD);
- VAR changed: BOOLEAN;
- BEGIN AcquireWrite; changed := SELF.value # value; SELF.value := value; ReleaseWrite; IF changed THEN Changed (value) END;
- END Set;
- PROCEDURE Inc* (step: WORD);
- VAR changed: BOOLEAN;
- BEGIN AcquireWrite; changed := step # 0; INC (value, step); ReleaseWrite; IF changed THEN Changed (value) END;
- END Inc;
- PROCEDURE Dec* (step: WORD);
- VAR changed: BOOLEAN;
- BEGIN AcquireWrite; changed := step # 0; DEC (value, step); ReleaseWrite; IF changed THEN Changed (value) END;
- END Dec;
- PROCEDURE Changed (value: WORD);
- VAR i: SIZE;
- BEGIN i := 0; WHILE handlers[i] # NIL DO handlers[i] (SELF, value); INC (i) END;
- END Changed;
- PROCEDURE AddHandler* (handler: IntegerHandler);
- VAR i: SIZE;
- 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: SIZE;
- BEGIN i := 0; WHILE handlers[i] # NIL DO handlers[i] (SELF, value); INC (i) END;
- END Changed;
- PROCEDURE AddHandler* (handler: RealHandler);
- VAR i: SIZE;
- 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: WORD);
- VAR changed: BOOLEAN;
- BEGIN AcquireWrite; changed := ~(element IN value); INCL (value, element); ReleaseWrite; IF changed THEN Changed (value) END;
- END Incl;
- PROCEDURE Excl* (element: WORD);
- 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: SIZE;
- BEGIN i := 0; WHILE handlers[i] # NIL DO handlers[i] (SELF, value); INC (i) END;
- END Changed;
- PROCEDURE AddHandler* (handler: SetHandler);
- VAR i: SIZE;
- 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: SIZE);
- 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: SIZE;
- BEGIN i := 0; WHILE handlers[i] # NIL DO handlers[i] (SELF, value); INC (i) END;
- END Changed;
- PROCEDURE AddHandler* (handler: StringHandler);
- VAR i: SIZE;
- BEGIN i := 0; WHILE handlers[i] # NIL DO INC (i) END; handlers[i] := handler;
- END AddHandler;
- END String;
- (* generic message to be handled by the sequencer *)
- Message* = OBJECT
- VAR
- next: Message; time: LONGINT;
- PROCEDURE &InitMessage*;
- BEGIN SELF.next := NIL; time := NoDelay;
- END InitMessage;
- PROCEDURE Handle*;
- END Handle; (* abstract *)
- 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: WORD;
- procedure: IntegerProcedure;
- PROCEDURE &InitIntegerMessage* (value: WORD; 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: WORD;
- procedure: IntegerIntegerProcedure;
- PROCEDURE &InitIntegerIntegerMessage* (value0, value1: WORD; 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: SIZE;
- 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;
- (* generic request that allows to wait for the message to be handled*)
- Request* = OBJECT (Message)
- VAR
- handled: BOOLEAN;
- PROCEDURE &InitRequest*;
- BEGIN InitMessage; handled := FALSE;
- END InitRequest;
- (* IMPORTANT: to be called at the end of overriding procedures *)
- PROCEDURE Handle*;
- BEGIN {EXCLUSIVE} handled := TRUE
- END Handle;
- (* awaits handling by sequencer *)
- PROCEDURE Await;
- BEGIN {EXCLUSIVE} AWAIT (handled);
- END Await;
- END Request;
- IntegerRequest* = OBJECT (Request)
- VAR
- value: WORD;
- procedure: IntegerProcedure;
- PROCEDURE &InitIntegerRequest* (value: WORD; 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-: WORD;
- 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: WORD;
- procedure: IntegerProcedureBoolean;
- result-: BOOLEAN;
- PROCEDURE &InitIntegerRequestBoolean* (value: WORD; 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-: WORD;
- 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;
- (* generic base message sequencer class *)
- Sequencer* = OBJECT
- VAR
- handling, woken: BOOLEAN; first: Message; timer: Objects.Timer;
- PROCEDURE &InitSequencer*;
- BEGIN handling := TRUE; woken := FALSE; first := NIL; NEW (timer);
- END InitSequencer;
- (* check wether current procedure was called by sequencer or by other active objects *)
- 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: WORD; 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: WORD; procedure: IntegerIntegerProcedure);
- VAR message: IntegerIntegerMessage;
- BEGIN NEW (message, value0, value1, procedure); Add (message, NoDelay);
- END AddIntegerIntegerMessage;
- PROCEDURE AddIntegerRequest* (value: WORD; 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): WORD;
- 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: WORD; 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): WORD;
- 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;
- (* this procedure is called sequentially and can be overridden in order to do contiguous work *)
- 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;
- (* helper types *)
- BooleanHandler = PROCEDURE {DELEGATE} (property: Boolean; value: BOOLEAN);
- IntegerHandler = PROCEDURE {DELEGATE} (property: Integer; value: WORD);
- 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: WORD);
- IntegerIntegerProcedure = PROCEDURE {DELEGATE} (value0, value1: WORD);
- 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} (): WORD;
- ProcedureReal = PROCEDURE {DELEGATE} (): REAL;
- IntegerProcedureBoolean = PROCEDURE {DELEGATE} (value: WORD): BOOLEAN;
- RealProcedureInteger = PROCEDURE {DELEGATE} (value: REAL): WORD;
- (* helper function for delayed execution *)
- 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
|