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.
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.
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.