Emulation der "Linda"-Anweisungen in Ada

Das Klassendiagramm

Abb. 1: Klassendiagramm Teil A


Ein Tupelraum beinhaltet und verwaltet eine Menge von Tupeln.
Jedes Tupel besteht aus mehreren Parametern. Es stehen Funktionen zur Verfügung, um Tupeln miteinander zu vergleichen und zu verschmelzen (binden), welche die Parameter einzeln miteinander verknüpfen.
Ein Parameter ist vorerst ein abstraktes Objekt, von dem zumindest bekannt sein muß, ob es einen formalen oder aktuellen Parameter darstellt. Von dieser Klasse müssen für jeden Parametertyp spezielle Klassen abgeleitet werden (z.B. ein float-Parameter).
Blocker werden später genauer behandelt.


Abb. 2: Klassendiagramm Teil B


Linda besteht wie später genauer beschrieben aus zwei Tupelräumen und stellt die Linda-Primitive zur Verfügung.
Aus Vereinfachungsgründen wurden hier die Funktionen Try_Input, Try_Read nicht implementiert. Diese sind zur Benutzung von Linda nicht unbedingt erforderlich.

Implementierung der Linda Primitives INPUT und OUTPUT in ADA95

Mit Hilfe von protected Objects werden die Input und Output Funktionen vor gleichzeitigem Zugriff geschützt.

protected Linda_Env is
        entry Output (T: in Tuple_Type);
        entry Input  (T: in out Tuple_Type);
        entry Read   (T: in out Tuple_Type);
private
        Tuplespace, Waiting: Tuplespace_Type;
end Linda_Env;


Es werden zwei Tupelräume eingerichtet:
Task 1Task 2
OUTPUT ('MULT',2,3);OUTPUT('MULT',4,5);
INPUT ('PROD', NATURAL);INPUT('PROD',NATURAL);
OUTPUT ('MULT',6,7);

Tupelraum "Tupelspace"

‘MULT’

2

3

 

‘MULT’

4

5

 
       

 

 

Tupelraum "Waiting"

T1.BLOCKER.wait

‘PROD’

NATURAL

   

T2.BLOCKER.wait

‘PROD’

NATURAL

   
         

(Task 1 und Task 2 sind nun blockiert)




protected body Linda_Env is

       entry Output (T: in Tuple_Type) when true
       is
           I: Tuplespace_Iterator_Type;
           NT, RT: Tuple_Type := T;
           B: Blocker_Access;
           OR: boolean;		-- Nur lese Task (LINDA READ Primitive)
       begin
           -- Zum Vergleich einen formalen Blocker einfuegen
           Insert_First (NT, Blocker_Parameter_Type'(true, null, false));
           -- Solange, bis ein auf Input wartender Task gefunden wurde
           loop
               -- einen wartenden Task finden
               I := Find_Matching (Waiting, NT);
               -- Das Tupel mit dem des wartenden Tasks binden
               RT := Bind (Value (I), NT);
               -- Den wartenden Task aus Warteraum entfernen
               Delete (I);
               OR := Blocker_Parameter_Type(Value(First(RT))).Only_Reading;
               -- Den Blocker entnehmen
               B := Blocker_Parameter_Type (Value (First (RT))).B;
               Delete_First (RT);
               -- Den wartenden Task erloesen
               B.all.Release (RT);
           exit when not OR;
           end loop;
       exception
           -- Wenn kein wartender Task gefunden wurde
           when No_Tuple_Matches
           -- dann das Tupel in den Tupelraum eintragen
           =>   Insert_First (TupleSpace, T);
       end Output;




entry Input (T: in out Tuple_Type) when true
       is
           I: Tuplespace_Iterator_Type;
           B: Blocker_Access;
           NT: Tuple_Type := T;
       begin
           -- Ein passendes Tupel im Tupelraum suchen
           I := Find_Matching (TupleSpace, T);
           -- Die Tupel binden
           T := Bind (Value (I), T);
           -- Das gefundene Tupel aus dem Tupelraum entfernen
           Delete (I);

       exception
           -- wenn kein passendes Tupel im Tupelraum gefunden wurde
           when No_Tuple_Matches
                -- einen Blocker erzeugen um den aufrufenden Task warten 
                -- zu lassen
                B := new Blocker_Type;
                -- Den Blocker in das Tupel eintragen
                Insert_First (NT, Blocker_Parameter_Type'(false, B,false));
                -- Das Tupel in den Warteraum stellen
                Insert_First (Waiting, NT);
                -- Und den Task endlich warten lassen
                requeue B.all.Wait;
       end Input;

Bermerkung: Der Programmcode für Read ist fast analog

Blockers

Werden verwendet um den INPUT Task solange aufzuhalten, bis ein für die Input Funktion passendes Tupel mit OUTPUT in den Tupelraum gestellt wird.

Solche Blocker werden einem Tupel im Waiting-Tupelraum mitgegeben, um es einem Prozeß, der mit OUTPUT ein passendes Tupel ausgibt, zu ermöglichen den wartenden Prozeß aufzuwecken (siehe Prozedur output)

with Tuple; use Tuple;

package Blocker is
    protected type Blocker_Type is
        entry Wait (T: in out Tuple_Type);
        procedure Release (T: in Tuple_Type);
    private
        Tup: Tuple_Type;
        Blocked: boolean := true;
    end Blocker_Type;
end Blocker;

package body Blocker is
    protected body Blocker_Type is
        entry Wait (T: in out Tuple_Type)
        when not Blocked is
        begin
            T := Tup;
        end Wait;

        procedure Release (T: in Tuple_Type) is 
        begin
            Tup := T;
            Blocked := false;
        end;
    end Blocker_Type;
end Blocker;