Weiter: Die Implementation des
Hinauf: 18.8 Ein Platten-Treiber
Zurück: Der,Scheibenwischer-Task``
Bisher haben wir ein Problem überhaupt nicht behandelt, nämlich,
wie wir den eine Leseanforderung abgebenden Task blockieren. Interessanterweise
stellt dies ein nicht-triviales Problem dar. Es gibt zwei Eigenschaften von Ada-Tasks:
-
Die Methode der Prozeßkommunikation in Ada, das Rendezvous, ist
asymmetrisch.
Es gibt nämlich eine strikte Unterscheidung zwischen aufrufenden
und gerufenen Task.
-
Eine Möglichkeit diese Eigenschaft zu umgehen, bestünde darin,
dem gerufenen Task einen Pointer auf den aufrufenden Task zu übergeben.
Aber in Ada
ist das aus Sichtbarkeitsgründen nicht möglich. Der gerufene Task
müßte dann alle Task-Typen der ihn potentiell rufenden Tasks ,,sehen``
können, d.h., er müßte eine entsprechende With-Anweisung haben.
Interessanterweise ist unser Problem auch nicht mit den in Abschnitt C
vorgestellten Task_IDs zu lösen.
Wie lösen wir nun unser Problem?
Wir geben zwei Lösungsvarianten. Bei der ersten vermeiden wir eine Abhängigkeit
zu Anhang D, benötigen allerdings für jede Leseanforderung
einen zusätzlichen Task.
Bei der zweiten kommen wir ohne solche Tasks aus, indem wir
ein Suspension_Object benützen.
Die Vorführung der ersten Variante ist also nur didaktisch zu sehen;
sie dient lediglich dazu, den Leser das Tasking-Konzept von Ada (noch)
näher zu bringen.
- Lösung 1:
-
Wir kreieren lokal in
Platten_Treiber.Read mittels
new einen Blockier-Task, dessen Typ dem ,,Scheibenwischer-Task`` bekannt
ist. Daher können wir einem seiner Entries einen entsprechenden
Pointer mitgeben. Die Aufgabe des Blockier-Tasks ist es, an einer
Accept-Anweisung zu warten. Der ,,Scheibenwischer-Task`` ruft den
entsprechenden Task-Entry, wenn er die geforderten Daten von der
Platte gelesen hat und übergibt diese Daten beim Aufruf des Entry's.
Der Blockier-Task speichert die Daten in einer internen Variable
und wartet an einer Accept-Anweisung, bis die Operation
Platten_Treiber.Read den Aufruf des entsprechenden Entry's abgibt. Bei diesem Rendezvous
werden dann die gelesenen Daten vom Blockier-Task an
Platten_Treiber.Read
übergeben. Die tatsächliche Blockierung tritt auf, weil
Platten_Treiber.Read
an dem gerade erwähnten Aufruf des Entry's hängen bleibt, bis
die Daten angeliefert werden können.
- Lösung 2:
-
Anstelle des Blockier-Tasks wird hier ein Suspension_Object
(vgl. D.8) in das Listen_element
eingetragen.
Die Funktion Read ruft dann ein Set_False gefolgt
von einem Suspend_Until_True auf.
Der Platten_Treiber_Task hebt die solcher Art erfolgte Blockierung
durch den Aufruf Set_True wieder auf.
Im folgenden wird nur der Quell-Code für die erste Variante vorgeführt.
Der Leser sollte allerdings mit den oben angeführten Erklärungen
leicht in der Lage sein, die entsprechenden Änderungen durchzuführen.
In einer praktischen Realisierung eines Plattentreibers wird man
auf jeden Fall die zweite Variante bevorzugen.
Weiter: Die Implementation des
Hinauf: 18.8 Ein Platten-Treiber
Zurück: Der,Scheibenwischer-Task``
Johann Blieberger
Wed Feb 11 09:58:52 MET 1998