next up previous
Next: Die Implementierung von Rendezvous Up: Rendezvous Previous: Rendezvous in Ada

Eine Schnittstelle für Rendezvous in Oberon

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;


next up previous
Next: Die Implementierung von Rendezvous Up: Rendezvous Previous: Rendezvous in Ada
Andreas Borchert
2/2/1998