Weiter Zurück Inhalt

5. Implementierung

Concurrent Haskell ist als Spracherweiterung von Haskell in jedem gängigen Haskell-Compiler integriert, wie z.B. dem Glasgow Haskell Compiler (ghc) oder HUGS.

Concurrent Haskell an sich läuft auf eigener Prozess im Betriebssystem. forkIO erzeugt innerhalb dieses Prozesses einen neuen Prozess mit eigenem Stack zur Verwaltung aller zum Ablauf des Prozesses benötigten Daten. Der interne Scheduler, der die blockierten bzw. wartenden Prozesse auswählt und so zur Fortführung bringt stellt verschiedene Umschaltstrategien bereit. Er kann einerseits preemtiv mit zyklischen Strategien wie z.B. dem timeslicing-Verfahren arbeiten oder kooperativ, so dass Prozesse so lange laufen, bis sie sich selbst blockieren. Der Scheduler stellt nur von einem Prozess auf einen anderen an wohldefinierten Punkten zu Beginn eines sog. grundlegenden Blocks um. Diese Punkte sind dadurch charakterisiert, dass keine zur Hälfte bearbeiteten Stack-Objekte verbleiben.

Eingangs wurde bereits angesprochen, dass die Haskell-Implementierung Interprozesskommunikation unterstützen muss, damit es nicht zu Kollisionen kommt, wenn zwei Prozesse denselben (Teil-)Ausdruck auswerten wollen. Die interne Realisation wird im Folgenden beschrieben. Ein Ausdruck wird durch einen im Heap (Speicherbereich für Variablenwerte) allozierten Speicherbereich repräsentiert, in dem ein Zeiger auf den Code und Werte der Variablen des Ausdrucks gespeichert sind. Er wird ausgewertet, indem der Zeiger in ein Register geladen und zum entsprechenden Code gesprungen wird.Wenn ein Prozess einen Ausdruck auswertet, ersetzt er den Zeiger des Codes für den Ausdruck mit einem Zeiger auf einen anderen Codebereich. In diesem Code wird das Einreihen der Prozesse in eine Warteschlange organisiert, die ebenfalls diesen Ausdruck auswerten wollen, während ein anderes Prozess dies bereits tut. Hat der Prozess den Ausdruck ausgewertet, so überschreibt er den Ausdruck mit seinem ursprünglichen Wert und deblockiert wartende Prozesse wenn vorhanden. So wird eine Kollision verhindert.

Eine MVar wird repräsentiert durch einen Zeiger auf einen veränderbaren Heap-Bereich. In diesem Bereich befindet sich auch ein Flag, das anzeigt, ob eine MVar leer oder voll ist, zusammen mit einem Wert oder einer Liste der wartenden Prozesse.

5.1 Garbage Collection

Obwohl das Konzept der automatischen Speicherwiederfreigabe meist in Zusammenhang mit Datenobjekten auftaucht, ist es unter einer speziellen Randbedingung auch für Prozesse gültig. Auf Prozesse besitzt Garbage Collection nur Gültigkeit, wenn der Prozess keine weiteren Seiteneffekte auslösen kann, also beispielsweise keine weiteren EA-Operationen durchführt. Ein laufender Prozess darf also von der Garbage Collection nicht beseitigt werden, da er weiterhin EA-Operationen ausführen könnte. Ein Prozess hingegen, der bei einer MVar blockiert ist, kann vom Garbage Collector aufgenommen werden, unter der Voraussetzung, dass die MVar nicht mehr von einem non-garbage-Prozess zugänglich ist. Dies ist zulässig, da kein anderer Prozess einen Wert in die MVar schreiben würde um somit den wartenden Prozess deblockieren zu können. So kann die Garbage Collection für Prozesse auch Deadlocks aufheben.

5.2 Distributed Implementation

Die bisher besprochenen Konzepte zur MVar, die lokal innerhalb eines Haskell Concurrent-Prozesses lokal auf einem Rechner laufen, funktionieren in der Haskell-Implementierung für verteilte Umgebungen mit Nachrichtenaustausch über ein Netzwerk.

Jede MVar befindet sich ausschließlich an einer Stelle. putMVar- und getMVar-Operationen auf eine remote MVar sind durch Nachrichtenaustausch realisiert.

Bei einer getMVar -Operation trägt die Nachricht eine Identifikation des sendenden Prozesses mit sich, der entprechend blockiert wird, wenn kein Wert in der MVar enthalten ist. Sobald ein Wert in die MVar geschrieben wurde, wird eine Nachricht mit dem Wert an den Absender der getMVar-Nachricht zurückgesendet. Aufgrund der Identifikation kann der wartende Prozess wieder zur Fortführung gebracht werden.

Eine putMVar-Operation macht lediglich das Senden einer Nachricht mit dem zu schreibenden Wert vom Absender erforderlich. Dieser Wert wird in die MVar unter der Vorausssetzung geschrieben, dass die MVar leer ist. Wenn sie voll ist, wird nach der heutigen Konzeption ein Laufzeitfehler generiert.


Weiter Zurück Inhalt