11. Anlage
 
zurück
11.3.7 Prozeßbezeichnung und Attribute


Manchmal ist es günstig, auf einen Prozeß zu verweisen, ohne daß er von einem bestimmten Prozeßtyp sein muß. Ein Server-Prozeß kann z. B. die letzten Aufrufer aufzeichnen wollen, so daß sie bei nachfolgenden Aufrufen als "bekannt" identifiziert werden.
Die Objekte des privaten Typs Task_Id im Paket Task_Identification können Prozesse von jedem Prozeßtyp bezeichnen. Dies macht es möglich nur ein Unterprogramm zu schreiben, das Prozesse mit unterschiedlichem Prozeßtyp behandelt.
Das Paket Task_Identifikation beschreibt Operationen und Attribute, die für die Beschaffung der Bezeichnung eines Prozesses verwendet werden können. Es gibt das folgende sprachdefinierte Bibliothekspaket:

package Ada.Task_Identification is
   type Task_ID is private;
   -- Ein Wert des Typs Task_ID bezeichnet einen existierenden Prozeß.

   Null_Task_ID : constant Task_ID;
   -- Die Konstante Null_Task_ID definiert keinen Prozeß. Jedes Objekt
   -- des Typs Task_ID wird standardmäßig mit dem Wert von
   -- Null_Task_ID initialisiert.

   function "=" ( Left, Right : Task_ID ) return Boolean;
   -- Die Funktion "=" gibt nur "wahr" zurück, wenn die rechte und linke
   -- Seite den gleichen Prozeß bezeichnen, oder beide Null_Task_ID
   -- als Wert haben.

   function Image ( T : Task_ID ) return String;
   -- Die Funktion Image definiert eine implementierungsabhängige
   -- Zeichenkette, die den Prozeß T bezeichnet. Wenn T gleich
   -- Null_Task_ID ist, gibt ‘Image eine leere Zeichenkette zurück.

   function Current_Task return Task_ID;
   -- Die Funktion Current_Task gibt einen Wert zurück, der den
   -- aufrufenden Prozeß bezeichnet.
   -- Current_Task kann in Verbindung mit anderen Operationen, die
   -- einen Prozeß als Argument benötigen, wie Set_Priority (siehe
   -- Anlage D), verwendet werden.
   -- Die Funktionen Current_Task kann Task_ID Wert zurückgeben, der
   -- einen Umgebungsprozeß bezeichnet.

   procedure Abort_Task ( T: in out Task_ID );
   -- Die Wirkung von Abort_Task ist gleich wie bei der
   -- Abbruchanweisung für den durch T bezeichneten Prozeß. Zusätzlich
   -- wird die ganze Partition abgebrochen (siehe Anlage E), wenn
   -- T den Umgebungsprozeß bezeichnet. Abort_Task ist eine
   -- möglicherweise blockierende Operation.
   -- Program_Error wird gesetzt, wenn der Wert Null_Task_ID als
   -- Parameter übergeben wird.

   function Is_Terminated ( T : Task_ID ) return Boolean;
   -- Die Funktionen Is_Terminated geben den Wert des entsprechenden
   -- Prozeßattributes des durch T bezeichneten Prozesses zurück.
   -- Program_Error wird gesetzt, wenn der Wert Null_Task_ID als
   -- Parameter übergeben wird.

   function Is_Callable ( T: Task_ID ) return Boolean;
   -- Die Funktionen Is_Terminated und Is_Callable geben den Wert des
   -- entsprechenden Prozeßattributes des durch T bezeichneten
   -- Prozesses zurück.
   -- Program_Error wird gesetzt, wenn der Wert Null_Task_ID als
   -- Parameter übergeben wird.
private
   --... nicht definiert
end Ada.Task_Identification;



Für das Präfix T, das von einem Prozeßtyp ist (nach einer etwaigen impliziten Dereferenzierung), wird folgendes Attribut definiert:

T’Identity ergibt einen Wert vom Typ Task_ID, der einen Prozeß bezeichnet.

Für ein Präfix E, das eine Eingangsvereinbarung bezeichnet, wird folgendes Attribut definiert:

E’Caller ergibt einen Wert vom Typ "Task_ID", der den Prozeß bezeichnet, dessen Aufruf gerade bedient wird. Die Verwendung dieses Attributes ist nur innerhalb eines Eingangsrumpfes oder einer Annahmeanweisung, die der mit E bezeichneten Eingangsanweisung entspricht, erlaubt.

Das Attribut "Caller" kann einen Wert des Typs "Task_ID" zurückgeben, der den Umgebungsprozeß bezeichnet.

Fehlersituationen

Die Funktion Current_Task darf nicht aus einem Eingangsrumpf oder einem Unterbrechungsbearbeiter aufgerufen werden. Die Ausnahme "Program_Error" wird ausgelöst oder es wird ein implementierungsabhängiger Wert vom Typ "Task_ID" zurückgegeben.
Wenn ein Wert von "Task_ID" als Parameter an irgendeine der in diesem Paket (oder einem Kindpaket) vereinbarten Operationen übergeben wird, und das entsprechende Prozeßobjekt nicht mehr existiert, ist die Ausführung des Programms fehlerhaft.

Beispiel einer einfachen Betriebsmittelzuteilung

Es wird ein einfaches Betriebsmittel betrachtet, das zugeteilt und freigegeben werden kann. Ohne die Verwendung von Prozeßbezeichnern (task identifier) ist es schwierig sicherzustellen, daß ein Betriebsmittel von dem Prozeß freigegeben wird, der es belegt hat. Der folgende Programmcode stellt sicher, daß der Prozeß, der das Betriebsmittel belegt hat, derjenige ist, der es zurückgibt. Dazu merkt sich das geschützte Objekt "Controller" den Prozeßbezeichner, sobald die Belegung mittels "Allocate" erfolgt. Bei der Freigabe mittels "Free" wird der aktuelle Prozeßbezeichner mit dem gemerkten Prozeßbezeichner verglichen.

with Ada.Task_Identification; use Ada.Task_Identification;
   package Secure_Resource_Allocation is
      protected Controller is
         entry Allocate;
         procedure Free;
      private
         Allocated : Boolean := False;
         Current_Owner : Task_ID := Null_Task_ID;
      end Controller;

   Not_Allocated : exception;
   -- wird von Free angestoßen
end Secure_Resource_Allocation;

package body Secure_Resource_Allocation is
   protected body Controller is
      entry Allocate when not Allocated is
      begin
         Allocated := True;
         Current_Owner := Allocate’Caller;
     end Allocate;
     procedure Free is
     begin
        if Current_Task /= Current_Owner then
           raise Not_Allocated;
        else
           Allocated := False;
           Current_Owner := Null_Task_ID;
        end if;
     end Free;
   end Controller;
end Secure_Resource_Allocation;



Das Paket Task_Attributes ordnet einem Prozeß nutzerdefinierte Informationen zu.

Das generische Bibliothekspaket ermöglicht einem Prozeß, nutzerdefinierte Attribute zuzuordnen.

with Ada.Task_Identification; use Ada.Task_Identification
generic
   type Attribute is private;
   Initial_Value : in Attribute;
package Ada.Task_Attribute is
   type Attribute_Handle is access all Attribute;
   function Value ( T : Task_ID := Current_Task ) return Attribute;
   -- Die Operation Value gibt den Wert des zugehörigen Attributes von T zurück.

   function Reference ( T : Task_ID := Current_Task ) return Attribute_Handle;
   -- Die Operation Reference gibt den Zugrifffswert zurück, der das
   -- entsprechende Attribut von T bezeichnet.

   procedure Set_Value ( Val : in Attribute; T : in Task_ID := Current_Task );
   -- Die Operation Set_Value führt alle Abschlußarbeiten auf dem
   -- alten Wert des Attributes von T aus und weist Val dem Attribut zu.

   procedure Reinitialize ( T: in Task_ID := Current_Task );
   -- Die Wirkung der Operation Reinitialize ist die gleiche wie von
   -- Set_Value, wobei der Parameter Val durch Initial_Value ersetzt wird.
end Ada.Task_Attributes;



Fehlersituationen
  • Bei allen in diesem Paket vereinbarten Operationen tritt ein Prozeßfehler auf, wenn der mit T bezeichnete Prozeß beendet ist. Die Ausnahme "Program_Error" wird ausgelöst, wenn der Wert von T gleich Null_Task_ID ist.
  • Der von einem Aufruf von "Reference" zurückgegebenen Zeigerwert darf nach einem weiteren Aufruf von "Reinitialize" oder nachdem der zugehörige Prozeß beendet ist, nicht für das gleiche Prozeßattribut verwendet werden.
  • Wenn ein Wert von "Task_ID" an irgendeine der in diesem Paket vereinbarten Operationen übergeben wurde, und das entsprechende Prozeßobjekt nicht mehr existiert, ist die Ausführung des Programms fehlerhaft.

Bemerkungen
  • Ein Attribut existiert (nach der Initialisierung) immer und hat einen Anfangswert. Es braucht bis zur ersten Operation, die das Objekt wahrscheinlich verändert, keinen Speicher zu belegen. Das gleiche gilt für "Reinitialize".
  • Das Ergebnis der Funktion "Reference" sollte mit Vorsicht verwendet werden; es ist immer sicher
    das Ergebnis in dem Prozeßrumpf zu verwenden, auf dessen Attribut zugegriffen wird. Wenn das Ergebnis jedoch von einem anderen Prozeß verwendet wird, muß der Programmierer sicher sein, daß der Prozeß auf dessen Attribut zugegriffen wird, nicht beendet ist. Andernfalls könnte die Programmausführung fehlerhaft werden.
  • Wenn der Parameter T (in einem Aufruf eines Unterprogramms für eine Instanz dieses Paketes) einen nicht existierenden Prozeß bezeichnet, dann ist die Ausführung des Programms fehlerhaft.

Beispiel für Prozeßattribute - Periodische Planung

Um die Verwendung der Prozeßattribute zu veranschaulichen, wird die periodische Planung von Prozessen betrachtet. Normalerweise wird ein periodischer Prozeß in Ada folgendermaßen strukturiert:

task Cyclic;
task body Cyclic is
   Next_Period : Ada.Real_Time.Time := Ada.Real_Time.Clock;
   Period : Time_Span := To_Time.Span ( 10.0 ); -- angenommen
begin
   loop
   -- Anweisungen, die in jeder Periode ausgeführt werden
      Next_Period := Next_Period + Period;
      delay until Next_Period;
   end loop;
end Cyclic;

In einigen Anwendungen (z.B. hoch integrierten Systemen) ist es notwendig, die Einzelheiten der Einplanung vom Prozeß selbst zu trennen. In diesen Systemen möchte der Prozeß nur einen Aufruf Wait_Until_Next_Schedule ausführen. Das folgende Paket verwendet Prozeßattribute und Prozeßbezeichner, um eine Menge von periodischen Prozessen einzuplanen:

with Ada.Task_Identification; use Ada.Task_Identification;
with Ada.Real_Time; use Ada.Real_Time;
package Periodic_Scheduler is
   procedure Set_Characteristic (T : Task_ID;
                                 Period : Time_Span;
                                 First_Schedule : Time );
   procedure Wait_Until_Next_Schedule;
end Periodic_Scheduler;

with Ada.Task.Attributes;
package body Periodic_Scheduler is
   Start_Up_Time : Time := Clock;
   type Task_Information is record
      Period : Time_Span := Time_Span_Zero;
      Next_Schedule_Time : Time := Time_Of ( 100000, Time_Span_Zero );
   end record;
   Default: Task_Information;
   -- ein Standardobjekt muß für die folgende Paketinstantiierung zur Verfügung stehen

   package Periodic_Attributes is new Ada.Task_Attributes ( Task_Information, Default );
   use Periodic_Attributes;

   procedure Set_Characteristic ( T : Task_ID; Period : Time_Span ; First_Schedule : Time ) is
   begin
      Set_Value ( ( Period, First_Schedule ), T );
   end;

   procedure Wait_Until_Next_Schedule is
      Task_Info : Task_Information := Value;
      Next_Time : Time;
   begin
      Next_Time := Task_Info.Period + Task_Info.Next_Schedule_Time;
      Set_Value (( Task_Info.Period, Next_Time ));
      delay until Next_Time;
   end Wait_Until_Next_Schedule;
end Periodic_Scheduler;

Periodische Prozesse können jetzt folgendermaßen kodiert werden und andere Komponenten der Anwendung können die Periode belegen:

task Cyclic;
task body Cyclic is
begin
   loop
      -- Anweisungen, die in jeder Periode ausgeführt werden
      Periodic_Scheduler.Wait_Until_Next_Schedule;
   end loop;
end Cyclic;


 
zurück
 Index   Ada Tour - Dokumentation  
© 2003 Förderverein Ada Deutschland e.V.