next up previous contents index
Weiter: Die Implementation des Hinauf: 18.8 Ein Platten-Treiber Zurück: Der,Scheibenwischer-Task``

Der Blockier-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:

  1. Die Methode der Prozeßkommunikation in Ada, das Rendezvous, ist asymmetrisch.     Es gibt nämlich eine strikte Unterscheidung zwischen aufrufenden und gerufenen Task.
  2. 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.



next up previous contents index
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