\section{Realisierung weiterer Synchronisationsmechanismen}
\label{HIGHER}
In den vorangegangenen Abschnitten werden der Aufbau und die
Implementation der Module {\em semaphore} und {\em mutex} sowie die
zur Realisierung von Monitoren benutzten Funktionen im Modul {\em
  condition} beschrieben. In diesem Abschnitt wird unter Benutzung der
in den genannten Modulen verwirklichten Synchronisationsprimitive eine Reihe
weiterer, in der Hierarchie der Synchronisationsmethoden höher
angesiedelten Synchronisationsverfahren beschrieben (vgl.\ 
Abbildung~\ref{synch}).  Die in den folgenden Beispielen
implementierten Verfahren dienen der Synchronisation durch die
Übertragung von Nachrichten über Kommunikationskanäle ohne Benutzung
weiterer gemeinsamer Variablen.
%und Synchronisationsmechanismen.


Es sind verschiedene Möglichkeiten der Synchronisation durch die
Übertragung von Nachrichten denkbar.  Folgende Unterschiede sind im
Verhalten beim Senden und Empfangen der Nachrichten möglich:
\begin{description}
\item[Blockierendes Senden und Empfangen:] Sowohl Sender als auch
  Empfänger werden blockiert, bis die Nachricht übertragen wurde.
  Dieses entspricht den in Abschnitt~\ref{KommRend} besprochenen
  Eigenschaften der Kommunikationskanäle von CSP und der Rendezvous.
  Der Empfang und das Senden einer Nachricht sind eng gekoppelt.
\item[Nichtblockierendes Senden, blockierendes Empfangen:] Der
  sendende Prozeß arbeitet sofort weiter, und gesendete Nachrichten
  werden bis zum Lesen der Nachricht zwischengespeichert. Ein
  Empfänger wird demgegenüber blockiert, bis eine Nachricht
  eingetroffen ist. Diese Methode der Implementation erlaubt es einem
  sendenden Prozeß, sehr schnell eine Anzahl von Empfangsprozessen mit
  Nachrichten zu versorgen. Es besteht jedoch die Gefahr der
  übermäßigen Beschlagnahme von Systemressourcen, falls die
  Aufnahmekapazität eines Kommunikationskanals nicht beschränkt wird.
\item[Nichtblockierendes Senden und Empfangen:] Empfangene und
  sendende Prozesse werden in keinem Fall blockiert. Die Methode weist
  den Nachteil des aktiven Wartens auf der Seite des Empfängers auf.
\end{description}

In den folgenden Abschnitten werden beispielhaft
Kommunikationsmechanismen für zwei der angeführten Möglichkeiten
implementiert: In Abschnitt~\ref{EventKomm} wird die Implementation
von Funktionen zur direkten Kommunikation von Threads über
Kommunikationskanäle zur Übermittlung von Nachrichten beschrieben
(nichtblockierendes Senden, blockierendes Empfangen), daran
anschließend die Implementation eines den Ada-Rendezvous ähnlichen
Mechanismus (Abschnitt~\ref{Rendezvous}). Der Einsatz von
Syntaxerweiterungen (Abschnitt~\ref{Syntaxerweiterungen}) erlaubt eine
sichere und einfachere Benutzung dieser Synchronisationsmechanismen.


%Die dort beschriebenen Mechanismen dienen der Implementation eines den
%Ada-Rendezvous ähnlichen Mechanismus mittels Syntaxerweiterungen
%(Abschnitt~\ref{Syntaxerweiterungen}).
%\section{Höhere Synchronisationskonzepte}

\subsection{Kommunikationskanäle}
\label{EventKomm}
Kommunikationskanäle dienen dem Austausch von Nachrichten zwischen
Threads, ohne daß eine Benutzung expliziter Synchronisationsverfahren,
wie Semaphore oder Mutexe, notwendig wird.  Die für eine derartige
Kommunikation notwendigen Funktionen sind im Modul {\em event}
realisiert. Der Nachrichtenaustausch über Funktionen des Moduls {\em
  event} erlaubt ein nichtblockierendes Senden von Nachrichten.  Falls
keine Nachrichten vorliegen, führt demgegenüber der Versuch, eine
Nachricht zu empfangen, zur Blockierung eines empfangsbereiten
Threads.

Der Nachrichtenaustausch erfolgt über typisierte
Kommunikationsendpunkte. Vom Modul {\em event} werden der
parametrisierte, abstrakte Datentyp {\em event.T(R)} sowie die auf
diesem Datentyp definierten Funktionen exportiert. Ein Element des
Typs {\em event.T(R)} wird mittels der Operation
\begin{xple}
  new(R $<:$\k{Ok}) :T(R)
\end{xple}
erzeugt. Diese Funktion erwartet als Parameter den Typ {\em R} der zu
übermittelnden Nachrichten. Eine Nachricht des Typs {\em R} wird
mittels der Funktion
\begin{xple}
  put(R $<:$\k{Ok}  ev :T(R)  value :R) :\k{Ok}
\end{xple}
übertragen. Die Parameter dieser Funktionen bestehen wiederum aus dem
Typ der Nachricht sowie aus der Angabe der Ereignisvariablen {\em ev}
und dem Wert des zu übertragenden Ereignisses {\em value}.  Der
Empfang von Nachrichten erfolgt mit der Funktion
\begin{xple}
  get(R $<:$\k{Ok}  ev :T(R)) :R.
\end{xple}
Falls keine ungelesenen Nachrichten vorliegen, wird ein diese Funktion
aufrufender Thread bis zum Eintreffen einer Nachricht blockiert.

Das gleichzeitige Warten auf mehrere Nachrichten erfolgt unter
Verwendung der Funktion
\begin{xple}
  select(alt :Array(SelectT)) :\k{Ok}.
\end{xple}

%Der Rückgabewert der Funktion {\em select} ist der Index des für die
%Kommunikation ausgewählten Arrayelements.  
Die Benutzung dieser Funktion erfordert die Übergabe eines Feldes von
Elementen des Typs {\em event.SelectT}.  Die direkte Übergabe mehrerer
Elemente des Typs {\em event.T(R)} ist nicht möglich, da alle Elemente
eines Feldes vom gleichen Typ sein müssen. Um dennoch die Verwendung
von Ereignisvariablen unterschiedlichen Typs in der Funktionen {\em
  select} zu ermöglichen, werden die Typen dieser Elemente unter
Verwendung der Operation
\begin{xple}
  makeEntry(R$<:$\k{Ok}  ev :T(R)  func :\k{Fun}(:R) :\k{Ok}) :SelectT
\end{xple}
verborgen. Die Funktion {\em makeEntry} erwartet die Angabe einer
Ereignisvariablen {\em ev} sowie die Angabe einer Funktion {\em func}.
Die Funktion {\em func} dient der eigentlichen Übermittlung des Werts
der eingetroffenen Nachricht. Sie wird während der
Nachrichtenübermittlung mit dem Wert der Nachricht aufgerufen. Diese
Vorgehensweise ist notwendig, da nach Verbergen des Nachrichtentyps
durch die Funktion {\em makeEntry} der Typ der Nachricht nicht mehr
unmittelbar zur Verfügung steht.

Die Implementation des Moduls {\em event} erfolgt mittels der im
Modul {\em semaphore} vorhandenen Funktionen. Jedem Element des Typs
{\em event.T(R)} sind zwei Semaphore sowie eine Warteschlange zur
Aufnahme der Werte zugeordnet. Ein binäres Semaphor {\em lock}, das
beim Erzeugen einer Ereignisvariable mit dem Wert $1$ initialisiert
wird, dient dem gegenseitigen Ausschluß von Prozessen beim Zugriff auf
die in der Ereignisvariablen vorhandenen internen Strukturen. Werte
werden beim Aufruf der Funktion {\em event.put} in der internen
Warteschlange gespeichert. Das Vorhandensein eines Werts wird mittels
eines weiteren, im Typ {\em event.T(R)} verwendeten, allgemeinen
Semaphors {\em isval} angezeigt. Dieses Semaphor wird beim Erzeugen
einer Ereignisvariablen mit dem Wert $0$ initialisiert und nach dem
Schreiben in die Warteschlange durch Aufruf der {\em
  semaphore.signal}--Operation inkrementiert. Ein Aufruf der Funktion
{\em event.get} führt beim Nichtvorhandensein von Werten zu einer
Blockierung lesender Threads durch Ausführung der {\em
  semaphore.wait}--Operation auf dem Semaphor {\em isval}.  Die
Funktion {\em select} wird mittels der Funktion {\em
  semaphore.waitMultipleOr} implementiert, die ein gleichzeitiges
Warten auf mehrere Semaphore ermöglicht.  Das folgende Beispiel zeigt
die Erzeugung zweier Kommunikationsendpunkte und deren Benutzung:
\begin{xple}
\k{Let} Person = \k{Tuple} name :String  age :Int \k{end}
\k{let} e1 = event.new(:Person)
\k{let} e2 = event.new(:\k{Ok})

\k{let} funcA(self :thread.T(\k{Ok})) = 
  \k{begin}
    \k{let} processPerson(x :Person) = \k{begin} \ldots \k{end}
    \k{let} entry1 = event.makeEntry(e1  processPerson)
    \k{let} entry2 = event.makeEntry(e2  \k{fun}(x :\k{Ok}) \k{ok})
    
    \ldots
    \k{loop}
      \ldots
      event.select(\k{array} entry1  entry2 \k{end})
      \ldots
    \k{end}
  \k{end} 

\k{let} funcB(self :thread.T(\k{Ok})) =
  \k{begin}
    \k{loop}
      \ldots
      \k{let} value = \k{tuple} \ldots \k{end}
      event.put(e1 value)
      \ldots
      event.put(e2 \k{ok})
    \k{end}
  \k{end}

\k{let} threadA = thread.fork(funcA)  \k{and}  threadB = thread.fork(funcB)
\end{xple}
In diesem Beispiel kommunizieren {\em threadA} sowie {\em threadB} über
den Austausch von Nachrichten der Typen {\em Person} und {\bf Ok}.
Der Austausch von Nachrichten des Typs {\bf Ok} dient  lediglich
der Signalisierung eines Ereignisses, der tatsächliche Wert der
Nachricht, hier immer der konstante Wert {\bf ok}, wird ignoriert. 


%\comment{Noch besser beschreiben} 
%\comment{Beispiel !}

\subsection{Rendezvous}
\label{Rendezvous}
Im Modul {\em rendezvous} sind Kommunikationsmechanismen für
blockierendes Senden und Empfangen von Nachrichten implementiert: Der
Versuch, eine Nachricht zu senden, führt zur Blockierung des Senders, bis
ein Empfänger versucht, eine Nachricht zu empfangen. Der Versuch, eine
Nachricht zu empfangen, blockiert den Empfänger, bis ein Sender eine
Nachrichtenübermittlung anstößt.

Die Implementation von Rendezvous ähnelt der des im vorigen Abschnitt
eingeführten Moduls {\em event}. Ein Rendezvous entspricht einem
Kommunikationkanal mit blockierendem Senden und Empfangen. Im
Gegensatz zu den in CSP und Ada verwirklichten Rendezvous werden
Prozesse nicht direkt, sondern indirekt über Rendezvousvariablen
adressiert.

%Ein Element des Typs {\em rendezvous.T(R)~~R~$<:$\k{Ok}}
%wird dabei unter Verwendung eines Elements des Typs {\em event.T(R)}
%und eines weiteren Elements des Typs {\em event.T(\k{Ok}}
%implementiert. Die erste Ereignisvariable dient dem Übertragen der
%eigentliche Werte, während die zweite der Bestätigung des
%Lesevorganges dient. 

Das Modul {\em rendezvous} besteht aus den folgenden Funktionen: Eine
neue Rendezvousvariable wird durch die Funktion
\begin{xple}
  new(R $<:$\k{Ok}) :T(R)
\end{xple}
erzeugt, die als Parameter den Typ {\em R} der zu übermittelnden
Nachrichten erwartet.  

Ein Rendezvous  wird mittels der  Funktionen
\begin{xple}
  put(R $<:$\k{Ok}  rend :T(R)  value :R) :\k{Ok}
\end{xple}
und
\begin{xple}
  get(R $<:$\k{Ok}  rend :T(R)) :R
\end{xple}
eingeleitet. Hierbei wird der in der Funktion {\em put} angegebene
Wert {\em value} einem die Funktion {\em get} aufrufenden Empfänger
übermittelt. Der Austausch einer Nachricht findet erst statt, wenn
sowohl Empfänger als auch Sender für einen Nachrichtenaustausch über
die als Argument für {\em put} und {\em get} angegebene
Rendezvousvariable {\em rend} bereit sind.

Für das Warten auf mehrere Alternativen für ein Rendezvous steht die
Funktion {\em select} zur Verfügung.  Für die Benutzung dieser
Funktion ist es, analog zur Funktion {\em event.select}, notwendig, die
Typen der zu benutzenden Rendezvous-Elemente durch die Funktion
\begin{xple}
makeEntry(R$<:$\k{Ok}  rend :T(R)  func :\k{Fun}(:R) :\k{Ok}  guard :\k{Fun}() :Bool) :SelectT
\end{xple}
zu verbergen. Die Argumente der Funktion {\em select} bestehen aus
einer Variable des Typs {\em rendezvous.T(R)} sowie einer Funktion
{\em func}, die als Argument den empfangenen Wert erhält. Weiterhin ist
die Angabe einer Funktion {\em guard} notwendig, die der Auswahl
möglicher Alternativen für ein Rendezvous über den Aufruf von {\em
  select} dient.

Die Funktion {\em select} erwartet als Argumente ein Feld von
Elementen des Typs {\em rendezvous.SelectT} sowie die Angabe einer
(optionalen) Funktion {\em elseCase}\footnote{Optionale Werte können
  durch Funktionen des Moduls {\em optional} realisiert werden~\cite{Matt93}.}:
\begin{xple}
  select(alt :Array(SelectT)   elseCase :optional.T(\k{Fun}():\k{Ok})) :\k{Ok}.
\end{xple}

Die Implementation der Funktion {\em select} lehnt sich an das
gleichnamige Konstrukt der Programmiersprache Ada an~\cite{BULIWE87}.
Jedoch verfügt das in Ada vorhandene Konstrukt noch über weitergehende
Möglichkeiten, wie beispielsweise die Angabe von Zeitschranken,
innerhalb derer ein Rendezvous eingeleitet werden muß.

Die der Funktion {\em select} übergebenen Argumente werden wie folgt
ausgewertet:
\begin{itemize}
\item Zuerst werden alle Wächteranweisungen der im Feld {\em alt}
  übergebenen Rendezvousvariablen ausgewertet. Rendezvousvariablen,
  deren Wächter den boolschen Wert {\em true} ergeben, werden als offen
  registriert.  Damit stehen sie für ein mögliches Rendezvous zur
  Verfügung. Wächter, deren Auswertung {\em false} ergibt, werden als
  geschlossen bezeichnet und für ein Rendezvous nicht berücksichtigt.
\item Existieren keine offenen Wächter und existiert keine Funktion
  {\em elseCase}, so wird der Aufruf von {\em select} mit einer
  Ausnahme abgebrochen.
\item Aus der Menge der Alternativen mit offenen Wächtern werden
  diejenigen ausgewählt, für die bereits ein Kommunikationspartner für
  ein Rendezvous vorhanden ist. Aus diesen zur Verfügung stehenden
  Rendezvouspartnern wird ein Partner ausgewählt und das Rendezvous
  vollzogen. Die Auswahl eines Rendezvouspartners wird über einen
  Zufallszahlengenerator gesteuert.
\item Existieren keine Kommunikationspartner für ein Rendezvous und
  wurde eine Funktion {\em elseCase} übergeben, so wird diese
  ausgeführt.
\item Existieren keine offenen Wächter und wurde eine Funktion {\em
    elseCase} übergeben, so wird diese ausgeführt.
\item Existieren keine potentiellen Rendezvouspartner und wurde keine
  Funktion {\em elseCase} übergeben, wird der die Funktion {\em
    select} aufrufende Thread bis zum Eintreffen eines
  Rendezvouspartners blockiert.
\end{itemize}

Das folgende Beispiel zeigt die Implementation eines einfachen Puffers
für Datenelemente des Typs {\em Int}. Der Puffer besteht aus einem
eigenständigen Thread, der die Entnahme und das Hinzufügen von
Datenelementen über Rendezvous gestattet. In der Funktion {\em select}
wird auf Rendezvous für das Hinzufügen von Datenelementen {\em
  (append)} sowie für die Entnahme von Datenelementen {\em (take)}
gewartet.  Als Erzeuger und Verbraucher von Pufferelementen fungieren
zwei weitere Threads {\em producer} und {\em consumer}, die über die
Funktionen {\em put} und {\em get} Rendezvous mit dem Puffer-Thread
einleiten:
%\newpage
\begin{xple}
(* Erzeugen der Rendezvous-Datenstrukturen *)
\k{let} append = rendezvous.new(:Int)
\k{let} take   = rendezvous.new(\k{:Tuple} \k{var} x :Int \k{end})

(* Definition des Datenpuffers *)
\k{let} buffer(size :Int) = \k{begin}
\begin{tabular}{ll}
  \k{let} \k{var} b = arrayOp.new(size 0) & (* Pufferarray *) \\
  \k{let} \k{var} inptr  = 0 & (* Index für nächsten Schreibzugriff *) \\
  \k{let} \k{var} outptr = 0 & (* Index für nächsten Lesezugriff *) \\
  \k{let} \k{var} n = 0 & (* Anzahl der gültigen Elemente *) \\
\end{tabular}
\end{xple}
\begin{minipage}[t]{2.7in}
\begin{xple}
(* Anfügen eines Elementes *)
  \k{let} appendFun(x :Int) =                 
    \k{begin}
      b[inptr] := x
      n := n + 1
      inptr := \{inptr + 1\} \% size
    \k{end}
  \end{xple}
\end{minipage}
\begin{minipage}[t]{3.3in}
\begin{xple}
(* Entfernen eines Elementes *)
  \k{let} takeFun(x :\k{Tuple} \k{var} x :Int \k{end}) =
    \k{begin}
      x.x := b[outptr]
      n := n -- 1
      outptr:= \{outptr + 1\} \% size
    \k{end}
  \end{xple}
\end{minipage}
\begin{xple}
  (* Einträge für den select-Aufruf erstellen *)
  \k{let} append = rendezvous.makeEntry(append  appendFun  \k{fun}() n $<$ size)
  \k{let} take = rendezvous.makeEntry(take  takeFun  \k{fun}() n $>$ 0 )
  \k{let} elseCase = optional.nil(:\k{Fun}():\k{Ok})

  (* Akzeptieren von Rendezvous *)
  \k{loop}
    rendezvous.select(\k{array} append take \k{end}  elseCase)
  \k{end}
\k{end} (* buffer *)
\end{xple}

\begin{minipage}[t]{2.7in}
\begin{xple}
(* Rendezvousaufrufe *)
\k{let} producer(self :thread.T(\k{Ok})) = 
  \k{begin}
    \k{loop}
      \k{let} val = \ldots
      rendezvous.put(append val)
    \k{end}
  \k{end}
\end{xple}
\end{minipage}
\begin{minipage}[t]{3.0in}
\begin{xple}

\k{let} consumer(self :thread.T(\k{Ok})) = 
  \k{begin}
    \k{let} val = \k{tuple} \k{let} \k{var} x = 0 \k{end}
    \k{loop}
      rendezvous.put(take val)
      \ldots
    \k{end}
  \k{end}
\end{xple}
\end{minipage}
\begin{xple}
\k{let} bufferThread = thread.fork(\k{fun}(self :thread.T(\k{Ok})) buffer(100))
\k{let} consumerThread = thread.fork(consumer)
\k{let} producerThread = thread.fork(producer)
\end{xple}


%\newpage
\subsection{Syntaxerweiterungen}
\label{Syntaxerweiterungen}
Die Syntax der Programmiersprache TL ist erweiterbar. Hierbei ist es
möglich, schon bestehende Syntaxregeln um neue Alternativen zu
erweitern und neue Regeln zur Sprache hinzuzufügen~\cite{Schr94}. Eine
Erweiterung der Sprache TL ist dabei dynamisch, das heißt zur Laufzeit
des Tycoon-Systems möglich. Durch Angabe eines speziellen Konstruktors {\bf
  grammar} und der Aufzählung zusätzlicher Regeln wird der
 Parser des Systems dynamisch erweitert. 

Der Mechanismus zur Definition von Syntaxerweiterungen wird in diesem
Abschnitt zur Erweiterung von Synchronisationsmechanismen benutzt. Die
beispielhaft eingeführten Erweiterungen dienen der einfacheren und
sicheren Benutzung von Synchronisationsmechanismen.

Als erstes Beispiel für eine Syntaxerweiterung dient der in
Abschnitt~\ref{MONITOR} vorgestellte Mechanismus der Sprache Modula-3
für den Zugriff auf ein Mutex.  Durch die Klammerung eines kritischen
Abschnitts durch die Schlüsselwörter {\em LOCK(mutex)} und {\em END}
können in Modula-3 durch ein Mutex geschützte Programmabschnitte
definiert werden.  Der Vorteil dieser Notation liegt in der durch den
Übersetzter sichergestellten Freigabe des Mutex am Blockende. Die
Definition der entsprechenden Syntaxerweiterung für die
Programmiersprache TL lautet wie folgt:
\begin{xple}
\k{grammar}
  value3:Value $|==$
        p=``\k{lock}'' v=value ``\k{do}'' b=bnds ``\k{end}''  $=>$
        value$<<|$
          \k{begin}
            mutex.lock(v)
            \k{try}
              \k{let} result = \k{begin} b \k{end}
              mutex.unlock(v)
              result
            \k{else}
              mutex.unlock(v)
              \k{reraise}
            \k{end}
          \k{end} $|>>$
\k{end}
\end{xple}
Es wird ein neues Schlüsselwort {\bf lock} eingeführt. Der auf {\bf
  lock} folgende Wert wird als Argument für die Funktionen {\em
  mutex.lock} und {\em mutex.unlock} benutzt. Während der
Typüberprüfungsphase des Übersetzungsvorgangs wird die Kompatibilität
des {\bf lock} folgenden Werts mit den Signaturen der
Zugriffsfunktionen {\em mutex.lock} und {\em mutex.unlock} überprüft.
Die dem Schlüsselwort {\bf do} folgenden Programmanweisungen werden
nach Akquirierung des Mutex durchgeführt.  Anschließend erfolgt die
Freigabe des Mutex. Ausnahmebedingungen, die während der Auswertung
des geschützten Abschnitts auftreten, werden abgefangen und nach
Freigabe des Mutex an den aufrufenden Thread weitergegeben.

Das neue Schlüsselwort {\bf lock} kann dann, wie im nächsten Beispiel
gezeigt, für den Zugriff auf ein Mutex benutzt werden.
\begin{xple}
\k{let} m = mutex.new()

\k{lock} m \k{do}
 (* kritischer Abschnitt *)
\k{end}
\end{xple}



Ein weiteres Beispiel für eine Syntaxerweiterung dient der einfachen
Benutzung der Funktion {\em rendezvous.select}.  Die in
Anhang~\ref{Rendezvoussyntax} angegebene Syntaxerweiterung bietet
gegenüber der direkten Benutzung der Funktion {\em rendezvous.select}
die folgenden Vorteile:
\begin{itemize}
\item Durch die unmittelbare Angabe der im Falle eines Rendezvous
  auszuführenden Programmteile steigt die Übersichtlichkeit des
  Programms.
\item Die Funktion {\em rendezvous.select} erwartet als Argumente ein
  Feld, dessen Elemente die Alternativen für ein Rendezvous angeben.
  Elemente dieses Feldes sind durch Aufruf der Funktion {\em
    rendezvous.makeEntry} zu bilden.  Durch die Syntaxerweiterung wird
  dieser Vorgang automatisiert.
\item Die Angabe einer Wächteranweisung ist optional. Wird keine
  Wächteranweisung angegeben, ist die entsprechende Alternative immer
  offen.
\end{itemize}


Das folgende Beispiel illustriert die Verwendung des neuen
Select-Konstrukts.  Das Programmbeispiel entspricht dem in
Abschnitt~\ref{Rendezvous} angegebenen Puffer für ganze Zahlen:

\begin{xple}
(* Erzeugen der Rendezvous-Datenstrukturen *)
\k{let} append = rendezvous.new(:Int)
\k{let} take   = rendezvous.new(\k{:Tuple} \k{var} x :Int \k{end})

(* Definition des Datenpuffers *)
\k{let} buffer(size :Int) = \k{begin}
\begin{tabular}{ll}
  \k{let} \k{var} b = arrayOp.new(size 0) & (* Pufferarray *) \\
  \k{let} \k{var} inptr  = 0 & (* Index für nächsten Schreibzugriff *) \\
  \k{let} \k{var} outptr = 0 & (* Index für nächsten Lesezugriff *) \\
  \k{let} \k{var} n = 0 & (* Anzahl der gültigen Elemente *) \\
\end{tabular}
 
  \k{loop}
    \k{select}
    \k{when} n $<$ size \mbox{\boldmath $\Rightarrow$}
      \k{accept} append(x :Int) \k{do}
        (* -- Anfügen eines Elementes *)
        b[inptr] := x
        n := n + 1
        inptr := \{inptr + 1\} \% size
      \k{end}
    \k{or} \k{when} n $>$ 0 \mbox{\boldmath $\Rightarrow$}
      \k{accept} take(x :\k{Tuple} \k{var} x :Int \k{end}) \k{do}
       (* -- Entfernen eines Elementes *)
        x.x := b[outptr]
        n := n - 1
        outptr:= \{outptr + 1\} \% size 
      \k{end} (* accept *)
    \k{end} (* select *)
  \k{end} (* loop *)
\k{end} (* buffer *)
\end{xple}
\newpage
\begin{minipage}[t]{2.7in}
\begin{xple}
(* Rendezvousaufrufe *)
\k{let} producer(s, n :Int) = 
  \k{begin}
    \k{for} i=s \k{upto} n \k{do}
      \k{let} val=\ldots
      rendezvous.put(append i)
    \k{end}
  \k{end}
\end{xple}
\end{minipage}
\begin{minipage}[t]{3.0in}
\begin{xple}

\k{let} consumer() = 
  \k{begin}
    \k{let} val = \k{tuple} \k{let} \k{var} x = 0 \k{end}
    \k{loop}
      rendezvous.put(take val)
      \ldots
    \k{end}
  \k{end}
\end{xple}
\end{minipage}
\vskip 0.5cm Die angegebenen, exemplarischen Syntaxerweiterungen
ermöglichen eine einfache und sichere Benutzung der Funktionen der
Module {\em mutex} und {\em rendezvous}. Weitere Syntaxerweiterungen,
beispielsweise zur Einführung von Pfadausdrücken (vgl.
Abschnitt~\ref{Weitere}) sind denkbar. Dieser Mechanismus kann durch
bekannte Algorithmen unter Verwendung von Semaphoren implementiert
werden~\cite{Ha75}.



% Local Variables:
% TeX-master: "da"
% TeX-parse-self: t
% TeX-auto-save: t
% End:
