Wenn das Rendezvous-Konzept in Oberon realisiert werden soll, empfiehlt es sich, die Ein- und Ausgabeparameter in Aufträge zu verpacken. Verschiedene Sorten von Aufträgen werden in Oberon normalerweise mit Hilfe von Typentests unterschieden. Da eine Menge verschiedener Typen in Oberon nicht als Wert repräsentierbar ist, empfiehlt es sich, ein zusätzliches Unterscheidungsmerkmal einzuführen. Normalerweise offeriert eine Task keine sehr große Anzahl verschiedener Dienstleistungen, so daß es möglich ist, den unterschiedlichen Angeboten Nummern zwischen 0 und MAX(SET) zuzuordnen. Auf diese Weise kann eine Menge von Auftragstypen als SET angegeben werden:
DEFINITION Rendezvous; IMPORT Objects, Tasks; TYPE Message = POINTER TO MessageRec; MessageRec = RECORD (Objects.ObjectRec) END; Entry = SHORTINT; (* 0..MAX(SET) *) EntrySet = SET; TaskBody = PROCEDURE; PROCEDURE Accept(entry: Entry; VAR message: Message); PROCEDURE AcceptEnd; PROCEDURE Select(entries: EntrySet; VAR entry: Entry); PROCEDURE Connect(task: Tasks.Task; entry: Entry; message: Message); PROCEDURE Initiate(body: TaskBody; VAR task: Tasks.Task); END Rendezvous.
Das Entgegennehmen eines Auftrages erfolgt mit Accept, und mit AcceptEnd endet das Rendezvous. Select blockiert solange, bis ein Auftrag aus der gegebenen Menge vorliegt, und liefert einen dieser Aufträge zurück, der angenommen werden kann, ohne blockiert zu werden. Auf der anderen Seite kann ein Kunde mit Connect einen Auftrag aufgeben und wird dabei solange blockiert, bis der Partner des Rendezvous AcceptEnd aufruft.
Auf dieser Basis ließe sich das Beispiel des Ringpuffers in Ada direkt in Oberon übertragen:
DEFINITION RingBuffers; IMPORT Disciplines, Objects; TYPE Item = Objects.Object; RingBuffer = POINTER TO RingBufferRec; RingBufferRec = RECORD (Disciplines.ObjectRec) END; PROCEDURE Create(VAR ring: RingBuffer); PROCEDURE Read(ring: RingBuffer; VAR v: Item); PROCEDURE Write(ring: RingBuffer; e: Item); END RingBuffers.
Hierbei wird das Umsetzen von Prozeduraufrufen in Aufträge sinnvollerweise der Implementierung überlassen:
TYPE Item = Objects.Object; RingBuffer = POINTER TO RingBufferRec; RingBufferRec = RECORD (Disciplines.ObjectRec) task: Tasks.Task; END; CONST read = 0; write = 1; (* entries *) TYPE Message = POINTER TO MessageRec; MessageRec = RECORD (Rendezvous.MessageRec) item: Item; END; PROCEDURE Read(ring: RingBuffer; VAR v: Item); VAR message: Message; BEGIN NEW(message); Rendezvous.Connect(ring.task, read, message); v := message.item; END Read; PROCEDURE Write(ring: RingBuffer; e: Item); VAR message: Message; BEGIN NEW(message); message.item := e; Rendezvous.Connect(ring.task, write, message); END Write;
Die Task in Ada wird durch eine normale Prozedur repräsentiert. Als einziges fällt hier auf, daß die Entscheidung über die Akzeptanz der beiden Service-Angebote read und write vor dem Aufruf von Rendezvous.Select erfolgt. Dies entspricht aber genau der Ausführungsreihenfolge in Ada.
PROCEDURE Buffering; CONST size = 10; VAR buffer: ARRAY size OF Item; inx, outx: INTEGER; count: INTEGER; entries: Rendezvous.EntrySet; entry: Rendezvous.Entry; message: Message; BEGIN count := 0; inx := 0; outx := 0; LOOP entries := {}; IF count < size THEN INCL(entries, write); END; IF count > 0 THEN INCL(entries, read); END; Rendezvous.Select(entries, entry); CASE entry OF | write: Rendezvous.Accept(write, message); buffer[outx] := message.item; Rendezvous.AcceptEnd; outx := (outx + 1) MOD size; INC(count); | read: Rendezvous.Accept(read, message); message.item := buffer[inx]; Rendezvous.AcceptEnd; inx := (inx + 1) MOD size; DEC(count); END; END; END Buffering;
Im Gegensatz zu Ada können neue Tasks in Oberon jederzeit in jedem beliebigen Umfang (soweit der Speicher reicht) dynamisch erzeugt werden:
PROCEDURE Create(VAR ring: RingBuffer); BEGIN NEW(ring); Rendezvous.Initiate(Buffering, ring.task); END Create;