Terminator : in Element := Default_Terminator) return Element_Array; 21 This function returns an Element_Array whose value is the array pointed to by Ref, up to and including the first Terminator; the lower bound of the array is Index'First. Interfaces.C.Strings.- Dereference_Error is propagated if Ref is null. 22 function Value(Ref : in Pointer; Length : in ptrdiff_t) return Element_Array; 23 This function returns an Element_Array comprising the first Length elements pointed to by Ref. The exception Interfaces.C.- Strings.Dereference_Error is propagated if Ref is null. 24 The "+" and "-" functions perform arithmetic on Pointer values, based on the Size of the array elements. In each of these functions, Pointer_Error is propagated if a Pointer parameter is null. 25 procedure Increment (Ref : in out Pointer); 26 Equivalent to Ref := Ref+1. 27 procedure Decrement (Ref : in out Pointer); 28 Equivalent to Ref := Ref-1. 29 function Virtual_Length (Ref : in Pointer; Terminator : in Element := Default_Terminator) return ptrdiff_t; 30 Returns the number of Elements, up to the one just before the first Terminator, in Value(Ref, Terminator). 31 procedure Copy_Terminated_Array (Source : in Pointer; Target : in Pointer; Limit : in ptrdiff_t := ptrdiff_t'Las\ t; Terminator : in Element := Default_Termina\ tor); 32 This procedure copies Value(Source, Terminator) into the array pointed to by Target; it stops either after Terminator has been copied, or the number of elements copied is Limit, whichever occurs first. Dereference_Error is propagated if either Source or Target is null. 33 procedure Copy_Array (Source : in Pointer; Target : in Pointer; Length : in ptrdiff_t); 34 This procedure copies the first Length elements from the array pointed to by Source, into the array pointed to by Target. Dereference_Error is propagated if either Source or Target is null. Erroneous Execution 35 It is erroneous to dereference a Pointer that does not designate an aliased Element. 36 Execution of Value(Ref, Terminator) is erroneous if Ref does not designate an aliased Element in an Element_Array terminated by Terminator. 37 Execution of Value(Ref, Length) is erroneous if Ref does not designate an aliased Element in an Element_Array containing at least Length Elements between the designated Element and the end of the array, inclusive. 38 Execution of Virtual_Length(Ref, Terminator) is erroneous if Ref does not designate an aliased Element in an Element_Array terminated by Terminator. 39 Execution of Copy_Terminated_Array(Source, Target, Limit, Terminator) is erroneous in either of the following situations: 40 Execution of both Value(Source,Terminator) and Value(Source,Limit) are erroneous, or 41 Copying writes past the end of the array containing the Element designated by Target. 42 Execution of Copy_Array(Source, Target, Length) is erroneous if either Value(Source, Length) is erroneous, or copying writes past the end of the array containing the Element designated by Target. NOTES 43 (14) To compose a Pointer from an Element_Array, use 'Access on the first element. For example (assuming appropriate instantiations): 44 Some_Array : Element_Array(0..5) ; Some_Pointer : Pointer := Some_Array(0)'Access; Examples 45 Example of Interfaces.C.Pointers: 46 with Interfaces.C.Pointers; with Interfaces.C.Strings; procedure Test_Pointers is package C renames Interfaces.C; package Char_Ptrs is new C.Pointers (Index => C.size_t, Element => C.char, Element_Array => C.char_array, Default_Terminator => C.nul); 47 use type Char_Ptrs.Pointer; subtype Char_Star is Char_Ptrs.Pointer; 48 procedure Strcpy (Target_Ptr, Source_Ptr : Char_Star) is Target_Temp_Ptr : Char_Star := Target_Ptr; Source_Temp_Ptr : Char_Star := Source_Ptr; Element : C.char; begin if Target_Temp_Ptr = null or Source_Temp_Ptr = null then raise C.Strings.Dereference_Error; end if; 49 loop Element := Source_Temp_Ptr.all; Target_Temp_Ptr.all := Element; exit when Element = C.nul; Char_Ptrs.Increment(Target_Temp_Ptr); Char_Ptrs.Increment(Source_Temp_Ptr); end loop; end Strcpy; begin ... end Test_Pointers; B.4 Interfacing with COBOL 1 The facilities relevant to interfacing with the COBOL language are the package Interfaces.COBOL and support for the Import, Export and Convention pragmas with convention_identifier COBOL. 2 The COBOL interface package supplies several sets of facilities: 3 A set of types corresponding to the native COBOL types of the supported COBOL implementation (so-called ``internal COBOL representations''), allowing Ada data to be passed as parameters to COBOL programs 4 A set of types and constants reflecting external data representations such as might be found in files or databases, allowing COBOL-generated data to be read by an Ada program, and Ada-generated data to be read by COBOL programs 5 A generic package for converting between an Ada decimal type value and either an internal or external COBOL representation Static Semantics 6 The library package Interfaces.COBOL has the following declaration: 7 package Interfaces.COBOL is pragma Preelaborate(COBOL); 8 -- Types and operations for internal data representations 9 type Floating is digits implementation-defined; type Long_Floating is digits implementation-defined; 10 type Binary is range implementation-defined; type Long_Binary is range implementation-defined; 11 Max_Digits_Binary : constant := implementation-defined; Max_Digits_Long_Binary : constant := implementation-defined; 12 type Decimal_Element is mod implementation-defined; type Packed_Decimal is array (Positive range <>) of Decimal_Element; pragma Pack(Packed_Decimal); 13 type COBOL_Character is implementation-defined character type; 14 Ada_To_COBOL : array (Character) of COBOL_Character := implementation-de\ fined; 15 COBOL_To_Ada : array (COBOL_Character) of Character := implementation-de\ fined; 16 type Alphanumeric is array (Positive range <>) of COBOL_Character; pragma Pack(Alphanumeric); 17 function To_COBOL (Item : in String) return Alphanumeric; function To_Ada (Item : in Alphanumeric) return String; 18 procedure To_COBOL (Item : in String; Target : out Alphanumeric; Last : out Natural); 19 procedure To_Ada (Item : in Alphanumeric; Target : out String; Last : out Natural); 20 type Numeric is array (Positive range <>) of COBOL_Character; pragma Pack(Numeric); 21 -- Formats for COBOL data representations 22 type Display_Format is private; 23 Unsigned : constant Display_Format; Leading_Separate : constant Display_Format; Trailing_Separate : constant Display_Format; Leading_Nonseparate : constant Display_Format; Trailing_Nonseparate : constant Display_Format; 24 type Binary_Format is private; 25 High_Order_First : constant Binary_Format; Low_Order_First : constant Binary_Format; Native_Binary : constant Binary_Format; 26 type Packed_Format is private; 27 Packed_Unsigned : constant Packed_Format; Packed_Signed : constant Packed_Format; 28 -- Types for external representation of COBOL binary data 29 type Byte is mod 2**COBOL_Character'Size; type Byte_Array is array (Positive range <>) of Byte; pragma Pack (Byte_Array); 30 Conversion_Error : exception; 31 generic type Num is delta <> digits <>; package Decimal_Conversions is 32 -- Display Formats: data values are represented as Numeric 33 function Valid (Item : in Numeric; Format : in Display_Format) return Boolean; 34 function Length (Format : in Display_Format) return Natural; 35 function To_Decimal (Item : in Numeric; Format : in Display_Format) return Num; 36 function To_Display (Item : in Num; Format : in Display_Format) return Numeric; 37 -- Packed Formats: data values are represented as Packed_Decimal 38 function Valid (Item : in Packed_Decimal; Format : in Packed_Format) return Boolean; 39 function Length (Format : in Packed_Format) return Natural; 40 function To_Decimal (Item : in Packed_Decimal; Format : in Packed_Format) return Num; 41 function To_Packed (Item : in Num; Format : in Packed_Format) return Packed_Decimal; 42 -- Binary Formats: external data values are represented as Byte_Array 43 function Valid (Item : in Byte_Array; Format : in Binary_Format) return Boolean; 44 function Length (Format : in Binary_Format) return Natural; function To_Decimal (Item : in Byte_Array; Format : in Binary_Format) return Num; 45 function To_Binary (Item : in Num; Format : in Binary_Format) return Byte_Array; 46 -- Internal Binary formats: data values are of type Binary or Long_Bi\ nary 47 function To_Decimal (Item : in Binary) return Num; function To_Decimal (Item : in Long_Binary) return Num; 48 function To_Binary (Item : in Num) return Binary; function To_Long_Binary (Item : in Num) return Long_Binary; 49 end Decimal_Conversions; 50 private ... -- not specified by the language end Interfaces.COBOL; 51 Each of the types in Interfaces.COBOL is COBOL-compatible. 52 The types Floating and Long_Floating correspond to the native types in COBOL for data items with computational usage implemented by floating point. The types Binary and Long_Binary correspond to the native types in COBOL for data items with binary usage, or with computational usage implemented by binary. 53 Max_Digits_Binary is the largest number of decimal digits in a numeric value that is represented as Binary. Max_Digits_Long_Binary is the largest number of decimal digits in a numeric value that is represented as Long_ Binary. 54 The type Packed_Decimal corresponds to COBOL's packed-decimal usage. 55 The type COBOL_Character defines the run-time character set used in the COBOL implementation. Ada_To_COBOL and COBOL_To_Ada are the mappings between the Ada and COBOL run-time character sets. 56 Type Alphanumeric corresponds to COBOL's alphanumeric data category. 57 Each of the functions To_COBOL and To_Ada converts its parameter based on the mappings Ada_To_COBOL and COBOL_To_Ada, respectively. The length of the result for each is the length of the parameter, and the lower bound of the result is 1. Each component of the result is obtained by applying the relevant mapping to the corresponding component of the parameter. 58 Each of the procedures To_COBOL and To_Ada copies converted elements from Item to Target, using the appropriate mapping (Ada_To_COBOL or COBOL_To_ Ada, respectively). The index in Target of the last element assigned is returned in Last (0 if Item is a null array). If Item'Length exceeds Target'Length, Constraint_Error is propagated. 59 Type Numeric corresponds to COBOL's numeric data category with display usage. 60 The types Display_Format, Binary_Format, and Packed_Format are used in conversions between Ada decimal type values and COBOL internal or external data representations. The value of the constant Native_Binary is either High_Order_First or Low_Order_First, depending on the implementation. 61 function Valid (Item : in Numeric; Format : in Display_Format) return Boolean; 62 The function Valid checks that the Item parameter has a value consistent with the value of Format. If the value of Format is other than Unsigned, Leading_Separate, and Trailing_Separate, the effect is implementation defined. If Format does have one of these values, the following rules apply: 63 Format=Unsigned: if Item comprises zero or more leading space characters followed by one or more decimal digit characters then Valid returns True, else it returns False. 64 Format=Leading_Separate: if Item comprises zero or more leading space characters, followed by a single occurrence of the plus or minus sign character, and then one or more decimal digit characters, then Valid returns True, else it returns False. 65 Format=Trailing_Separate: if Item comprises zero or more leading space characters, followed by one or more decimal digit characters and finally a plus or minus sign character, then Valid returns True, else it returns False. 66 function Length (Format : in Display_Format) return Natural; 67 The Length function returns the minimal length of a Numeric value sufficient to hold any value of type Num when represented as Format. 68 function To_Decimal (Item : in Numeric; Format : in Display_Format) return Num; 69 Produces a value of type Num corresponding to Item as represented by Format. The number of digits after the assumed radix point in Item is Num'Scale. Conversion_Error is propagated if the value represented by Item is outside the range of Num. 70 function To_Display (Item : in Num; Format : in Display_Format) return Numeric; 71 This function returns the Numeric value for Item, represented in accordance with Format. Conversion_Error is propagated if Num is negative and Format is Unsigned. 72 function Valid (Item : in Packed_Decimal; Format : in Packed_Format) return Boolean; 73 This function returns True if Item has a value consistent with Format, and False otherwise. The rules for the formation of Packed_ Decimal values are implementation defined. 74 function Length (Format : in Packed_Format) return Natural; 75 This function returns the minimal length of a Packed_Decimal value sufficient to hold any value of type Num when represented as Format. 76 function To_Decimal (Item : in Packed_Decimal; Format : in Packed_Format) return Num; 77 Produces a value of type Num corresponding to Item as represented by Format. Num'Scale is the number of digits after the assumed radix point in Item. Conversion_Error is propagated if the value represented by Item is outside the range of Num. 78 function To_Packed (Item : in Num; Format : in Packed_Format) return Packed_Decimal; 79 This function returns the Packed_Decimal value for Item, represented in accordance with Format. Conversion_Error is propagated if Num is negative and Format is Packed_Unsigned. 80 function Valid (Item : in Byte_Array; Format : in Binary_Format) return Boolean; 81 This function returns True if Item has a value consistent with Format, and False otherwise. 82 function Length (Format : in Binary_Format) return Natural; 83 This function returns the minimal length of a Byte_Array value sufficient to hold any value of type Num when represented as Format. 84 function To_Decimal (Item : in Byte_Array; Format : in Binary_Format) return Num; 85 Produces a value of type Num corresponding to Item as represented by Format. Num'Scale is the number of digits after the assumed radix point in Item. Conversion_Error is propagated if the value represented by Item is outside the range of Num. 86 function To_Binary (Item : in Num; Format : in Binary_Format) return Byte_Array; 87 This function returns the Byte_Array value for Item, represented in accordance with Format. 88 function To_Decimal (Item : in Binary) return Num; function To_Decimal (Item : in Long_Binary) return Num; 89 These functions convert from COBOL binary format to a corresponding value of the decimal type Num. Conversion_Error is propagated if Item is too large for Num. 90 function To_Binary (Item : in Num) return Binary; function To_Long_Binary (Item : in Num) return Long_Binary; 91 These functions convert from Ada decimal to COBOL binary format. Conversion_Error is propagated if the value of Item is too large to be represented in the result type. Implementation Requirements 92 An implementation shall support pragma Convention with a COBOL convention_identifier for a COBOL-eligible type (see B.1). Implementation Permissions 93 An implementation may provide additional constants of the private types Display_Format, Binary_Format, or Packed_Format. 94 An implementation may provide further floating point and integer types in Interfaces.COBOL to match additional native COBOL types, and may also supply corresponding conversion functions in the generic package Decimal_ Conversions. Implementation Advice 95 An Ada implementation should support the following interface correspondences between Ada and COBOL. 96 An Ada access T parameter is passed as a ``BY REFERENCE'' data item of the COBOL type corresponding to T. 97 An Ada in scalar parameter is passed as a ``BY CONTENT'' data item of the corresponding COBOL type. 98 Any other Ada parameter is passed as a ``BY REFERENCE'' data item of the COBOL type corresponding to the Ada parameter type; for scalars, a local copy is used if necessary to ensure by-copy semantics. NOTES 99 (15) An implementation is not required to support pragma Convention for access types, nor is it required to support pragma Import, Export or Convention for functions. 100 (16) If an Ada subprogram is exported to COBOL, then a call from COBOL call may specify either ``BY CONTENT'' or ``BY REFERENCE''. Examples 101 Examples of Interfaces.COBOL: 102 with Interfaces.COBOL; procedure Test_Call is 103 -- Calling a foreign COBOL program -- Assume that a COBOL program PROG has the following declaration -- in its LINKAGE section: -- 01 Parameter-Area -- 05 NAME PIC X(20). -- 05 SSN PIC X(9). -- 05 SALARY PIC 99999V99 USAGE COMP. -- The effect of PROG is to update SALARY based on some algorithm 104 package COBOL renames Interfaces.COBOL; 105 type Salary_Type is delta 0.01 digits 7; 106 type COBOL_Record is record Name : COBOL.Numeric(1..20); SSN : COBOL.Numeric(1..9); Salary : COBOL.Binary; -- Assume Binary = 32 bits end record; pragma Convention (COBOL, COBOL_Record); 107 procedure Prog (Item : in out COBOL_Record); pragma Import (COBOL, Prog, "PROG"); 108 package Salary_Conversions is new COBOL.Decimal_Conversions(Salary_Type); 109 Some_Salary : Salary_Type := 12_345.67; Some_Record : COBOL_Record := (Name => "Johnson, John ", SSN => "111223333", Salary => Salary_Conversions.To_Binary(Some_Salary)); 110 begin Prog (Some_Record); ... end Test_Call; 111 with Interfaces.COBOL; with COBOL_Sequential_IO; -- Assumed to be supplied by implementation procedure Test_External_Formats is 112 -- Using data created by a COBOL program -- Assume that a COBOL program has created a sequential file with -- the following record structure, and that we need to -- process the records in an Ada program -- 01 EMPLOYEE-RECORD -- 05 NAME PIC X(20). -- 05 SSN PIC X(9). -- 05 SALARY PIC 99999V99 USAGE COMP. -- 05 ADJUST PIC S999V999 SIGN LEADING SEPARATE. -- The COMP data is binary (32 bits), high-order byte first 113 package COBOL renames Interfaces.COBOL; 114 type Salary_Type is delta 0.01 digits 7; type Adjustments_Type is delta 0.001 digits 6; 115 type COBOL_Employee_Record_Type is -- External representation record Name : COBOL.Alphanumeric(1..20); SSN : COBOL.Alphanumeric(1..9); Salary : COBOL.Byte_Array(1..4); Adjust : COBOL.Numeric(1..7); -- Sign and 6 digits end record; pragma Convention (COBOL, COBOL_Employee_Record_Type); 116 package COBOL_Employee_IO is new COBOL_Sequential_IO(COBOL_Employee_Record_Type); use COBOL_Employee_IO; 117 COBOL_File : File_Type; 118 type Ada_Employee_Record_Type is -- Internal representation record Name : String(1..20); SSN : String(1..9); Salary : Salary_Type; Adjust : Adjustments_Type; end record; 119 COBOL_Record : COBOL_Employee_Record_Type; Ada_Record : Ada_Employee_Record_Type; 120 package Salary_Conversions is new COBOL.Decimal_Conversions(Salary_Type); use Salary_Conversions; 121 package Adjustments_Conversions is new COBOL.Decimal_Conversions(Adjustments_Type); use Adjustments_Conversions; 122 begin Open (COBOL_File, Name => "Some_File"); 123 loop Read (COBOL_File, COBOL_Record); 124 Ada_Record.Name := To_Ada(COBOL_Record.Name); Ada_Record.SSN := To_Ada(COBOL_Record.SSN); Ada_Record.Salary := To_Decimal(COBOL_Record.Salary, COBOL.High_Order_First); Ada_Record.Adjust := To_Decimal(COBOL_Record.Adjust, COBOL.Leading_Separate); ... -- Process Ada_Record end loop; exception when End_Error => ... end Test_External_Formats; B.5 Interfacing with Fortran 1 The facilities relevant to interfacing with the Fortran language are the package Interfaces.Fortran and support for the Import, Export and Convention pragmas with convention_identifier Fortran. 2 The package Interfaces.Fortran defines Ada types whose representations are identical to the default representations of the Fortran intrinsic types Integer, Real, Double Precision, Complex, Logical, and Character in a supported Fortran implementation. These Ada types can therefore be used to pass objects between Ada and Fortran programs. Static Semantics 3 The library package Interfaces.Fortran has the following declaration: 4 with Ada.Numerics.Generic_Complex_Types; -- see G.1.1 pragma Elaborate_All(Ada.Numerics.Generic_Complex_Types); package Interfaces.Fortran is pragma Pure(Fortran); 5 type Fortran_Integer is range implementation-defined; 6 type Real is digits implementation-defined; type Double_Precision is digits implementation-defined; 7 type Logical is new Boolean; 8 package Single_Precision_Complex_Types is new Ada.Numerics.Generic_Complex_Types (Real); 9 type Complex is new Single_Precision_Complex_Types.Complex; 10 subtype Imaginary is Single_Precision_Complex_Types.Imaginary; i : Imaginary renames Single_Precision_Complex_Types.i; j : Imaginary renames Single_Precision_Complex_Types.j; 11 type Character_Set is implementation-defined character type; 12 type Fortran_Character is array (Positive range <>) of Character_Set; pragma Pack (Fortran_Character); 13 function To_Fortran (Item : in Character) return Character_Set; function To_Ada (Item : in Character_Set) return Character; 14 function To_Fortran (Item : in String) return Fortran_Character; function To_Ada (Item : in Fortran_Character) return String; 15 procedure To_Fortran (Item : in String; Target : out Fortran_Character; Last : out Natural); 16 procedure To_Ada (Item : in Fortran_Character; Target : out String; Last : out Natural); 17 end Interfaces.Fortran; 18 The types Fortran_Integer, Real, Double_Precision, Logical, Complex, and Fortran_Character are Fortran-compatible. 19 The To_Fortran and To_Ada functions map between the Ada type Character and the Fortran type Character_Set, and also between the Ada type String and the Fortran type Fortran_Character. The To_Fortran and To_Ada procedures have analogous effects to the string conversion subprograms found in Interfaces.COBOL. Implementation Requirements 20 An implementation shall support pragma Convention with a Fortran convention_identifier for a Fortran-eligible type (see B.1). Implementation Permissions 21 An implementation may add additional declarations to the Fortran interface packages. For example, the Fortran interface package for an implementation of Fortran 77 (ANSI X3.9-1978) that defines types like Integer*n, Real*n, Logical*n, and Complex*n may contain the declarations of types named Integer_Star_n, Real_Star_n, Logical_Star_n, and Complex_Star_n. (This convention should not apply to Character*n, for which the Ada analog is the constrained array subtype Fortran_Character (1..n).) Similarly, the Fortran interface package for an implementation of Fortran 90 that provides multiple kinds of intrinsic types, e.g. Integer (Kind=n), Real (Kind=n), Logical (Kind=n), Complex (Kind=n), and Character (Kind=n), may contain the declarations of types with the recommended names Integer_Kind_n, Real_Kind_n, Logical_Kind_n, Complex_Kind_n, and Character_Kind_n. Implementation Advice 22 An Ada implementation should support the following interface correspondences between Ada and Fortran: 23 An Ada procedure corresponds to a Fortran subroutine. 24 An Ada function corresponds to a Fortran function. 25 An Ada parameter of an elementary, array, or record type T is passed as a T argument to a Fortran procedure, where T is the F F Fortran type corresponding to the Ada type T, and where the INTENT attribute of the corresponding dummy argument matches the Ada formal parameter mode; the Fortran implementation's parameter passing conventions are used. For elementary types, a local copy is used if necessary to ensure by-copy semantics. 26 An Ada parameter of an access-to-subprogram type is passed as a reference to a Fortran procedure whose interface corresponds to the designated subprogram's specification. NOTES 27 (17) An object of a Fortran-compatible record type, declared in a library package or subprogram, can correspond to a Fortran common block; the type also corresponds to a Fortran ``derived type''. Examples 28 Example of Interfaces.Fortran: 29 with Interfaces.Fortran; use Interfaces.Fortran; procedure Ada_Application is 30 type Fortran_Matrix is array (Integer range <>, Integer range <>) of Double_Precision; pragma Convention (Fortran, Fortran_Matrix); -- stored in Fortran's -- column-major order procedure Invert (Rank : in Fortran_Integer; X : in out Fortran_Matrix); pragma Import (Fortran, Invert); -- a Fortran subroutine 31 Rank : constant Fortran_Integer := 100; My_Matrix : Fortran_Matrix (1 .. Rank, 1 .. Rank); 32 begin 33 ... My_Matrix := ...; ... Invert (Rank, My_Matrix); ... 34 end Ada_Application; Annex C (normative) Systems Programming 1 The Systems Programming Annex specifies additional capabilities provided for low-level programming. These capabilities are also required in many real-time, embedded, distributed, and information systems. C.1 Access to Machine Operations 1 This clause specifies rules regarding access to machine instructions from within an Ada program. Implementation Requirements 2 The implementation shall support machine code insertions (see 13.8) or intrinsic subprograms (see 6.3.1) (or both). Implementation-defined attributes shall be provided to allow the use of Ada entities as operands. Implementation Advice 3 The machine code or intrinsics support should allow access to all operations normally available to assembly language programmers for the target environment, including privileged instructions, if any. 4 The interfacing pragmas (see Annex B) should support interface to assembler; the default assembler should be associated with the convention identifier Assembler. 5 If an entity is exported to assembly language, then the implementation should allocate it at an addressable location, and should ensure that it is retained by the linking process, even if not otherwise referenced from the Ada code. The implementation should assume that any call to a machine code or assembler subprogram is allowed to read or update every object that is specified as exported. Documentation Requirements 6 The implementation shall document the overhead associated with calling machine-code or intrinsic subprograms, as compared to a fully-inlined call, and to a regular out-of-line call. 7 The implementation shall document the types of the package System.Machine_Code usable for machine code insertions, and the attributes to be used in machine code insertions for references to Ada entities. 8 The implementation shall document the subprogram calling conventions associated with the convention identifiers available for use with the interfacing pragmas (Ada and Assembler, at a minimum), including register saving, exception propagation, parameter passing, and function value returning. 9 For exported and imported subprograms, the implementation shall document the mapping between the Link_Name string, if specified, or the Ada designator, if not, and the external link name used for such a subprogram. Implementation Advice 10 The implementation should ensure that little or no overhead is associated with calling intrinsic and machine-code subprograms. 11 It is recommended that intrinsic subprograms be provided for convenient access to any machine operations that provide special capabilities or efficiency and that are not otherwise available through the language constructs. Examples of such instructions include: 12 Atomic read-modify-write operations -- e.g., test and set, compare and swap, decrement and test, enqueue/dequeue. 13 Standard numeric functions -- e.g., sin, log. 14 String manipulation operations -- e.g., translate and test. 15 Vector operations -- e.g., compare vector against thresholds. 16 Direct operations on I/O ports. C.2 Required Representation Support 1 This clause specifies minimal requirements on the implementation's support for representation items and related features. Implementation Requirements 2 The implementation shall support at least the functionality defined by the recommended levels of support in Section 13. C.3 Interrupt Support 1 This clause specifies the language-defined model for hardware interrupts in addition to mechanisms for handling interrupts. Dynamic Semantics 2 An interrupt represents a class of events that are detected by the hardware or the system software. Interrupts are said to occur. An occurrence of an interrupt is separable into generation and delivery. Generation of an interrupt is the event in the underlying hardware or system that makes the interrupt available to the program. Delivery is the action that invokes part of the program as response to the interrupt occurrence. Between generation and delivery, the interrupt occurrence (or interrupt) is pending. Some or all interrupts may be blocked. When an interrupt is blocked, all occurrences of that interrupt are prevented from being delivered. Certain interrupts are reserved. The set of reserved interrupts is implementation defined. A reserved interrupt is either an interrupt for which user-defined handlers are not supported, or one which already has an attached handler by some other implementation-defined means. Program units can be connected to non-reserved interrupts. While connected, the program unit is said to be attached to that interrupt. The execution of that program unit, the interrupt handler, is invoked upon delivery of the interrupt occurrence. 3 While a handler is attached to an interrupt, it is called once for each delivered occurrence of that interrupt. While the handler executes, the corresponding interrupt is blocked. 4 While an interrupt is blocked, all occurrences of that interrupt are prevented from being delivered. Whether such occurrences remain pending or are lost is implementation defined. 5 Each interrupt has a default treatment which determines the system's response to an occurrence of that interrupt when no user-defined handler is attached. The set of possible default treatments is implementation defined, as is the method (if one exists) for configuring the default treatments for interrupts. 6 An interrupt is delivered to the handler (or default treatment) that is in effect for that interrupt at the time of delivery. 7 An exception propagated from a handler that is invoked by an interrupt has no effect. 8 If the Ceiling_Locking policy (see D.3) is in effect, the interrupt handler executes with the active priority that is the ceiling priority of the corresponding protected object. Implementation Requirements 9 The implementation shall provide a mechanism to determine the minimum stack space that is needed for each interrupt handler and to reserve that space for the execution of the handler. This space should accommodate nested invocations of the handler where the system permits this. 10 If the hardware or the underlying system holds pending interrupt occurrences, the implementation shall provide for later delivery of these occurrences to the program. 11 If the Ceiling_Locking policy is not in effect, the implementation shall provide means for the application to specify whether interrupts are to be blocked during protected actions. Documentation Requirements 12 The implementation shall document the following items: 13 For each interrupt, which interrupts are blocked from delivery when a handler attached to that interrupt executes (either as a result of an interrupt delivery or of an ordinary call on a procedure of the corresponding protected object). 14 Any interrupts that cannot be blocked, and the effect of attaching handlers to such interrupts, if this is permitted. 15 Which run-time stack an interrupt handler uses when it executes as a result of an interrupt delivery; if this is configurable, what is the mechanism to do so; how to specify how much space to reserve on that stack. 16 Any implementation- or hardware-specific activity that happens before a user-defined interrupt handler gets control (e.g., reading device registers, acknowledging devices). 17 Any timing or other limitations imposed on the execution of interrupt handlers. 18 The state (blocked/unblocked) of the non-reserved interrupts when the program starts; if some interrupts are unblocked, what is the mechanism a program can use to protect itself before it can attach the corresponding handlers. 19 Whether the interrupted task is allowed to resume execution before the interrupt handler returns. 20 The treatment of interrupt occurrences that are generated while the interrupt is blocked; i.e., whether one or more occurrences are held for later delivery, or all are lost. 21 Whether predefined or implementation-defined exceptions are raised as a result of the occurrence of any interrupt, and the mapping between the machine interrupts (or traps) and the predefined exceptions. 22 On a multi-processor, the rules governing the delivery of an interrupt to a particular processor. Implementation Permissions 23 If the underlying system or hardware does not allow interrupts to be blocked, then no blocking is required as part of the execution of subprograms of a protected object whose one of its subprograms is an interrupt handler. 24 In a multi-processor with more than one interrupt subsystem, it is implementation defined whether (and how) interrupt sources from separate subsystems share the same Interrupt_ID type (see C.3.2). In particular, the meaning of a blocked or pending interrupt may then be applicable to one processor only. 25 Implementations are allowed to impose timing or other limitations on the execution of interrupt handlers. 26 Other forms of handlers are allowed to be supported, in which case, the rules of this subclause should be adhered to. 27 The active priority of the execution of an interrupt handler is allowed to vary from one occurrence of the same interrupt to another. Implementation Advice 28 If the Ceiling_Locking policy is not in effect, the implementation should provide means for the application to specify which interrupts are to be blocked during protected actions, if the underlying system allows for a finer-grain control of interrupt blocking. NOTES 29 (1) The default treatment for an interrupt can be to keep the interrupt pending or to deliver it to an implementation-defined handler. Examples of actions that an implementation-defined handler is allowed to perform include aborting the partition, ignoring (i.e., discarding occurrences of) the interrupt, or queuing one or more occurrences of the interrupt for possible later delivery when a user-defined handler is attached to that interrupt. 30 (2) It is a bounded error to call Task_Identification.Current_Task (see C.7.1) from an interrupt handler. 31 (3) The rule that an exception propagated from an interrupt handler has no effect is modeled after the rule about exceptions propagated out of task bodies. C.3.1 Protected Procedure Handlers Syntax 1 The form of a pragma Interrupt_Handler is as follows: 2 pragma Interrupt_Handler(handler_name); 3 The form of a pragma Attach_Handler is as follows: 4 pragma Attach_Handler(handler_name, expression); Name Resolution Rules 5 For the Interrupt_Handler and Attach_Handler pragmas, the handler_name shall resolve to denote a protected procedure with a parameterless profile. 6 For the Attach_Handler pragma, the expected type for the expression is Interrupts.Interrupt_ID (see C.3.2). Legality Rules 7 The Attach_Handler pragma is only allowed immediately within the protected_definition where the corresponding subprogram is declared. The corresponding protected_type_declaration or single_protected_declaration shall be a library level declaration. 8 The Interrupt_Handler pragma is only allowed immediately within a protected_definition. The corresponding protected_type_declaration shall be a library level declaration. In addition, any object_declaration of such a type shall be a library level declaration. Dynamic Semantics 9 If the pragma Interrupt_Handler appears in a protected_definition, then the corresponding procedure can be attached dynamically, as a handler, to interrupts (see C.3.2). Such procedures are allowed to be attached to multiple interrupts. 10 The expression in the Attach_Handler pragma as evaluated at object creation time specifies an interrupt. As part of the initialization of that object, if the Attach_Handler pragma is specified, the handler procedure is attached to the specified interrupt. A check is made that the corresponding interrupt is not reserved. Program_Error is raised if the check fails, and the existing treatment for the interrupt is not affected. 11 If the Ceiling_Locking policy (see D.3) is in effect then upon the initialization of a protected object that either an Attach_Handler or Interrupt_Handler pragma applies to one of its procedures, a check is made that the ceiling priority defined in the protected_definition is in the range of System.Interrupt_Priority. If the check fails, Program_Error is raised. 12 When a protected object is finalized, for any of its procedures that are attached to interrupts, the handler is detached. If the handler was attached by a procedure in the Interrupts package or if no user handler was previously attached to the interrupt, the default treatment is restored. Otherwise, that is, if an Attach_Handler pragma was used, the previous handler is restored. 13 When a handler is attached to an interrupt, the interrupt is blocked (subject to the Implementation Permission in C.3) during the execution of every protected action on the protected object containing the handler. Erroneous Execution 14 If the Ceiling_Locking policy (see D.3) is in effect and an interrupt is delivered to a handler, and the interrupt hardware priority is higher than the ceiling priority of the corresponding protected object, the execution of the program is erroneous. Metrics 15 The following metric shall be documented by the implementation: 16 The worst case overhead for an interrupt handler that is a parameterless protected procedure, in clock cycles. This is the execution time not directly attributable to the handler procedure or the interrupted execution. It is estimated as C - (A+B), where A is how long it takes to complete a given sequence of instructions without any interrupt, B is how long it takes to complete a normal call to a given protected procedure, and C is how long it takes to complete the same sequence of instructions when it is interrupted by one execution of the same procedure called via an interrupt. Implementation Permissions 17 When the pragmas Attach_Handler or Interrupt_Handler apply to a protected procedure, the implementation is allowed to impose implementation- defined restrictions on the corresponding protected_type_declaration and protected_body. 18 An implementation may use a different mechanism for invoking a protected procedure in response to a hardware interrupt than is used for a call to that protected procedure from a task. 19 Notwithstanding what this subclause says elsewhere, the Attach_Handler and Interrupt_Handler pragmas are allowed to be used for other, implementation defined, forms of interrupt handlers. Implementation Advice 20 Whenever possible, the implementation should allow interrupt handlers to be called directly by the hardware. 21 Whenever practical, the implementation should detect violations of any implementation-defined restrictions before run time. NOTES 22 (4) The Attach_Handler pragma can provide static attachment of handlers to interrupts if the implementation supports preelaboration of protected objects. (See C.4.) 23 (5) The ceiling priority of a protected object that one of its procedures is attached to an interrupt should be at least as high as the highest processor priority at which that interrupt will ever be delivered. 24 (6) Protected procedures can also be attached dynamically to interrupts via operations declared in the predefined package Interrupts. 25 (7) An example of a possible implementation-defined restriction is disallowing the use of the standard storage pools within the body of a protected procedure that is an interrupt handler. C.3.2 The Package Interrupts Static Semantics 1 The following language-defined packages exist: 2 with System; package Ada.Interrupts is type Interrupt_ID is implementation-defined; type Parameterless_Handler is access protected procedure; 3 4 function Is_Reserved (Interrupt : Interrupt_ID) return Boolean; 5 function Is_Attached (Interrupt : Interrupt_ID) return Boolean; 6 function Current_Handler (Interrupt : Interrupt_ID) return Parameterless_Handler; 7 procedure Attach_Handler (New_Handler : in Parameterless_Handler; Interrupt : in Interrupt_ID); 8 procedure Exchange_Handler (Old_Handler : out Parameterless_Handler; New_Handler : in Parameterless_Handler; Interrupt : in Interrupt_ID); 9 procedure Detach_Handler (Interrupt : in Interrupt_ID); 10 function Reference(Interrupt : Interrupt_ID) return System.Address; 11 private ... -- not specified by the language end Ada.Interrupts; 12 package Ada.Interrupts.Names is implementation-defined : constant Interrupt_ID := implementation-defined; . . . implementation-defined : constant Interrupt_ID := implementation-defined; end Ada.Interrupts.Names; Dynamic Semantics 13 The Interrupt_ID type is an implementation-defined discrete type used to identify interrupts. 14 The Is_Reserved function returns True if and only if the specified interrupt is reserved. 15 The Is_Attached function returns True if and only if a user-specified interrupt handler is attached to the interrupt. 16 The Current_Handler function returns a value that represents the attached handler of the interrupt. If no user-defined handler is attached to the interrupt, Current_Handler returns a value that designates the default treatment; calling Attach_Handler or Exchange_Handler with this value restores the default treatment. 17 The Attach_Handler procedure attaches the specified handler to the interrupt, overriding any existing treatment (including a user handler) in effect for that interrupt. If New_Handler is null, the default treatment is restored. If New_Handler designates a protected procedure to which the pragma Interrupt_Handler does not apply, Program_Error is raised. In this case, the operation does not modify the existing interrupt treatment. 18 The Exchange_Handler procedure operates in the same manner as Attach_ Handler with the addition that the value returned in Old_Handler designates the previous treatment for the specified interrupt. 19 The Detach_Handler procedure restores the default treatment for the specified interrupt. 20 For all operations defined in this package that take a parameter of type Interrupt_ID, with the exception of Is_Reserved and Reference, a check is made that the specified interrupt is not reserved. Program_Error is raised if this check fails. 21 If, by using the Attach_Handler, Detach_Handler, or Exchange_Handler procedures, an attempt is made to detach a handler that was attached statically (using the pragma Attach_Handler), the handler is not detached and Program_Error is raised. 22 The Reference function returns a value of type System.Address that can be used to attach a task entry, via an address clause (see J.7.1) to the interrupt specified by Interrupt. This function raises Program_Error if attaching task entries to interrupts (or to this particular interrupt) is not supported. Implementation Requirements 23 At no time during attachment or exchange of handlers shall the current handler of the corresponding interrupt be undefined. Documentation Requirements 24 If the Ceiling_Locking policy (see D.3) is in effect the implementation shall document the default ceiling priority assigned to a protected object that contains either the Attach_Handler or Interrupt_Handler pragmas, but not the Interrupt_Priority pragma. This default need not be the same for all interrupts. Implementation Advice 25 If implementation-defined forms of interrupt handler procedures are supported, such as protected procedures with parameters, then for each such form of a handler, a type analogous to Parameterless_Handler should be specified in a child package of Interrupts, with the same operations as in the predefined package Interrupts. NOTES 26 (8) The package Interrupts.Names contains implementation-defined names (and constant values) for the interrupts that are supported by the implementation. Examples 27 Example of interrupt handlers: 28 Device_Priority : constant array (1..5) of System.Interrupt_Priority := ( ... ); protected type Device_Interface (Int_ID : Ada.Interrupts.Interrupt_ID) is procedure Handler; pragma Attach_Handler(Handler, Int_ID); ... pragma Interrupt_Priority(Device_Priority(Int_ID)); end Device_Interface; ... Device_1_Driver : Device_Interface(1); ... Device_5_Driver : Device_Interface(5); ... C.4 Preelaboration Requirements 1 This clause specifies additional implementation and documentation requirements for the Preelaborate pragma (see 10.2.1). Implementation Requirements 2 The implementation shall not incur any run-time overhead for the elaboration checks of subprograms and protected_bodies declared in preelaborated library units. 3 The implementation shall not execute any memory write operations after load time for the elaboration of constant objects declared immediately within the declarative region of a preelaborated library package, so long as the subtype and initial expression (or default initial expressions if initialized by default) of the object_declaration satisfy the following restrictions. The meaning of load time is implementation defined. 4 Any subtype_mark denotes a statically constrained subtype, with statically constrained subcomponents, if any; 5 any constraint is a static constraint; 6 any allocator is for an access-to-constant type; 7 any uses of predefined operators appear only within static expressions; 8 any primaries that are names, other than attribute_references for the Access or Address attributes, appear only within static expressions; 9 any name that is not part of a static expression is an expanded name or direct_name that statically denotes some entity; 10 any discrete_choice of an array_aggregate is static; 11 no language-defined check associated with the elaboration of the object_declaration can fail. Documentation Requirements 12 The implementation shall document any circumstances under which the elaboration of a preelaborated package causes code to be executed at run time. 13 The implementation shall document whether the method used for initialization of preelaborated variables allows a partition to be restarted without reloading. Implementation Advice 14 It is recommended that preelaborated packages be implemented in such a way that there should be little or no code executed at run time for the elaboration of entities not already covered by the Implementation Requirements. C.5 Pragma Discard_Names 1 A pragma Discard_Names may be used to request a reduction in storage used for the names of certain entities. Syntax 2 The form of a pragma Discard_Names is as follows: 3 pragma Discard_Names[([On => ] local_name)]; 4 A pragma Discard_Names is allowed only immediately within a declarative_part, immediately within a package_specification, or as a configuration pragma. Legality Rules 5 The local_name (if present) shall denote a non-derived enumeration first subtype, a tagged first subtype, or an exception. The pragma applies to the type or exception. Without a local_name, the pragma applies to all such entities declared after the pragma, within the same declarative region. Alternatively, the pragma can be used as a configuration pragma. If the pragma applies to a type, then it applies also to all descendants of the type. Static Semantics 6 If a local_name is given, then a pragma Discard_Names is a representation pragma. 7 If the pragma applies to an enumeration type, then the semantics of the Wide_Image and Wide_Value attributes are implementation defined for that type; the semantics of Image and Value are still defined in terms of Wide_ Image and Wide_Value. In addition, the semantics of Text_IO.Enumeration_IO are implementation defined. If the pragma applies to a tagged type, then the semantics of the Tags.Expanded_Name function are implementation defined for that type. If the pragma applies to an exception, then the semantics of the Exceptions.Exception_Name function are implementation defined for that exception. Implementation Advice 8 If the pragma applies to an entity, then the implementation should reduce the amount of storage used for storing names associated with that entity. C.6 Shared Variable Control 1 This clause specifies representation pragmas that control the use of shared variables. Syntax 2 The form for pragmas Atomic, Volatile, Atomic_Components, and Volatile_Components is as follows: 3 pragma Atomic(local_name); 4 pragma Volatile(local_name); 5 pragma Atomic_Components(array_local_name); 6 pragma Volatile_Components(array_local_name); 7 An atomic type is one to which a pragma Atomic applies. An atomic object (including a component) is one to which a pragma Atomic applies, or a component of an array to which a pragma Atomic_Components applies, or any object of an atomic type. 8 A volatile type is one to which a pragma Volatile applies. A volatile object (including a component) is one to which a pragma Volatile applies, or a component of an array to which a pragma Volatile_Components applies, or any object of a volatile type. In addition, every atomic type or object is also defined to be volatile. Finally, if an object is volatile, then so are all of its subcomponents (the same does not apply to atomic). Name Resolution Rules 9 The local_name in an Atomic or Volatile pragma shall resolve to denote either an object_declaration, a non-inherited component_declaration, or a full_type_declaration. The array_local_name in an Atomic_Components or Volatile_Components pragma shall resolve to denote the declaration of an array type or an array object of an anonymous type. Legality Rules 10 It is illegal to apply either an Atomic or Atomic_Components pragma to an object or type if the implementation cannot support the indivisible reads and updates required by the pragma (see below). 11 It is illegal to specify the Size attribute of an atomic object, the Component_Size attribute for an array type with atomic components, or the layout attributes of an atomic component, in a way that prevents the implementation from performing the required indivisible reads and updates. 12 If an atomic object is passed as a parameter, then the type of the formal parameter shall either be atomic or allow pass by copy (that is, not be a nonatomic by-reference type). If an atomic object is used as an actual for a generic formal object of mode in out, then the type of the generic formal object shall be atomic. If the prefix of an attribute_reference for an Access attribute denotes an atomic object (including a component), then the designated type of the resulting access type shall be atomic. If an atomic type is used as an actual for a generic formal derived type, then the ancestor of the formal type shall be atomic or allow pass by copy. Corresponding rules apply to volatile objects and types. 13 If a pragma Volatile, Volatile_Components, Atomic, or Atomic_Components applies to a stand-alone constant object, then a pragma Import shall also apply to it. Static Semantics 14 These pragmas are representation pragmas (see 13.1). Dynamic Semantics 15 For an atomic object (including an atomic component) all reads and updates of the object as a whole are indivisible. 16 For a volatile object all reads and updates of the object as a whole are performed directly to memory. 17 Two actions are sequential (see 9.10) if each is the read or update of the same atomic object. 18 If a type is atomic or volatile and it is not a by-copy type, then the type is defined to be a by-reference type. If any subcomponent of a type is atomic or volatile, then the type is defined to be a by-reference type. 19 If an actual parameter is atomic or volatile, and the corresponding formal parameter is not, then the parameter is passed by copy. Implementation Requirements 20 The external effect of a program (see 1.1.3) is defined to include each read and update of a volatile or atomic object. The implementation shall not generate any memory reads or updates of atomic or volatile objects other than those specified by the program. 21 If a pragma Pack applies to a type any of whose subcomponents are atomic, the implementation shall not pack the atomic subcomponents more tightly than that for which it can support indivisible reads and updates. NOTES 22 (9) An imported volatile or atomic constant behaves as a constant (i.e. read-only) with respect to other parts of the Ada program, but can still be modified by an ``external source.'' C.7 Task Identification and Attributes 1 This clause describes operations and attributes that can be used to obtain the identity of a task. In addition, a package that associates user-defined information with a task is defined. C.7.1 The Package Task_Identification Static Semantics 1 The following language-defined library package exists: 2 package Ada.Task_Identification is type Task_ID is private; Null_Task_ID : constant Task_ID; function "=" (Left, Right : Task_ID) return Boolean; 3 function Image (T : Task_ID) return String; function Current_Task return Task_ID; procedure Abort_Task (T : in out Task_ID); 4 function Is_Terminated(T : Task_ID) return Boolean; function Is_Callable (T : Task_ID) return Boolean; private ... -- not specified by the language end Ada.Task_Identification; Dynamic Semantics 5 A value of the type Task_ID identifies an existent task. The constant Null_Task_ID does not identify any task. Each object of the type Task_ID is default initialized to the value of Null_Task_ID. 6 The function "=" returns True if and only if Left and Right identify the same task or both have the value Null_Task_ID. 7 The function Image returns an implementation-defined string that identifies T. If T equals Null_Task_ID, Image returns an empty string. 8 The function Current_Task returns a value that identifies the calling task. 9 The effect of Abort_Task is the same as the abort_statement for the task identified by T. In addition, if T identifies the environment task, the entire partition is aborted, See E.1. 10 The functions Is_Terminated and Is_Callable return the value of the corresponding attribute of the task identified by T. 11 For a prefix T that is of a task type (after any implicit dereference), the following attribute is defined: 12 T'Identity Yields a value of the type Task_ID that identifies the task denoted by T. 13 For a prefix E that denotes an entry_declaration, the following attribute is defined: 14 E'Caller Yields a value of the type Task_ID that identifies the task whose call is now being serviced. Use of this attribute is allowed only inside an entry_body or accept_statement corresponding to the entry_declaration denoted by E. 15 Program_Error is raised if a value of Null_Task_ID is passed as a parameter to Abort_Task, Is_Terminated, and Is_Callable. 16 Abort_Task is a potentially blocking operation (see 9.5.1). Bounded (Run-Time) Errors 17 It is a bounded error to call the Current_Task function from an entry body or an interrupt handler. Program_Error is raised, or an implementation-defined value of the type Task_ID is returned. Erroneous Execution 18 If a value of Task_ID is passed as a parameter to any of the operations declared in this package (or any language-defined child of this package), and the corresponding task object no longer exists, the execution of the program is erroneous. Documentation Requirements 19 The implementation shall document the effect of calling Current_Task from an entry body or interrupt handler. NOTES 20 (10) This package is intended for use in writing user-defined task scheduling packages and constructing server tasks. Current_Task can be used in conjunction with other operations requiring a task as an argument such as Set_Priority (see D.5). 21 (11) The function Current_Task and the attribute Caller can return a Task_ID value that identifies the environment task. C.7.2 The Package Task_Attributes Static Semantics 1 The following language-defined generic library package exists: 2 with Ada.Task_Identification; use Ada.Task_Identification; generic type Attribute is private; Initial_Value : in Attribute; package Ada.Task_Attributes is 3 type Attribute_Handle is access all Attribute; 4 function Value(T : Task_ID := Current_Task) return Attribute; 5 function Reference(T : Task_ID := Current_Task) return Attribute_Handle; 6 procedure Set_Value(Val : in Attribute; T : in Task_ID := Current_Task); procedure Reinitialize(T : in Task_ID := Current_Task); 7 end Ada.Task_Attributes; Dynamic Semantics 8 When an instance of Task_Attributes is elaborated in a given active partition, an object of the actual type corresponding to the formal type Attribute is implicitly created for each task (of that partition) that exists and is not yet terminated. This object acts as a user-defined attribute of the task. A task created previously in the partition and not yet terminated has this attribute from that point on. Each task subsequently created in the partition will have this attribute when created. In all these cases, the initial value of the given attribute is Initial_Value. 9 The Value operation returns the value of the corresponding attribute of T. 10 The Reference operation returns an access value that designates the corresponding attribute of T. 11 The Set_Value operation performs any finalization on the old value of the attribute of T and assigns Val to that attribute (see 5.2 and 7.6). 12 The effect of the Reinitialize operation is the same as Set_Value where the Val parameter is replaced with Initial_Value. 13 For all the operations declared in this package, Tasking_Error is raised if the task identified by T is terminated. Program_Error is raised if the value of T is Null_Task_ID. Erroneous Execution 14 It is erroneous to dereference the access value returned by a given call on Reference after a subsequent call on Reinitialize for the same task attribute, or after the associated task terminates. 15 If a value of Task_ID is passed as a parameter to any of the operations declared in this package and the corresponding task object no longer exists, the execution of the program is erroneous. Implementation Requirements 16 The implementation shall perform each of the above operations for a given attribute of a given task atomically with respect to any other of the above operations for the same attribute of the same task. 17 When a task terminates, the implementation shall finalize all attributes of the task, and reclaim any other storage associated with the attributes. Documentation Requirements 18 The implementation shall document the limit on the number of attributes per task, if any, and the limit on the total storage for attribute values per task, if such a limit exists. 19 In addition, if these limits can be configured, the implementation shall document how to configure them. Metrics 20 The implementation shall document the following metrics: A task calling the following subprograms shall execute in a sufficiently high priority as to not be preempted during the measurement period. This period shall start just before issuing the call and end just after the call completes. If the attributes of task T are accessed by the measurement tests, no other task shall access attributes of that task during the measurement period. For all measurements described here, the Attribute type shall be a scalar whose size is equal to the size of the predefined integer size. For each measurement, two cases shall be documented: one where the accessed attributes are of the calling task (that is, the default value for the T parameter is used), and the other, where T identifies another, non-terminated, task. 21 The following calls (to subprograms in the Task_Attributes package) shall be measured: 22 a call to Value, where the return value is Initial_Value; 23 a call to Value, where the return value is not equal to Initial_ Value; 24 a call to Reference, where the return value designates a value equal to Initial_Value; 25 a call to Reference, where the return value designates a value not equal to Initial_Value; 26 a call to Set_Value where the Val parameter is not equal to Initial_Value and the old attribute value is equal to Initial_ Value. 27 a call to Set_Value where the Val parameter is not equal to Initial_Value and the old attribute value is not equal to Initial_Value. Implementation Permissions 28 An implementation need not actually create the object corresponding to a task attribute until its value is set to something other than that of Initial_Value, or until Reference is called for the task attribute. Similarly, when the value of the attribute is to be reinitialized to that of Initial_Value, the object may instead be finalized and its storage reclaimed, to be recreated when needed later. While the object does not exist, the function Value may simply return Initial_Value, rather than implicitly creating the object. 29 An implementation is allowed to place restrictions on the maximum number of attributes a task may have, the maximum size of each attribute, and the total storage size allocated for all the attributes of a task. Implementation Advice 30 Some implementations are targeted to domains in which memory use at run time must be completely deterministic. For such implementations, it is recommended that the storage for task attributes will be pre-allocated statically and not from the heap. This can be accomplished by either placing restrictions on the number and the size of the task's attributes, or by using the pre-allocated storage for the first N attribute objects, and the heap for the others. In the latter case, N should be documented. NOTES 31 (12) An attribute always exists (after instantiation), and has the initial value. It need not occupy memory until the first operation that potentially changes the attribute value. The same holds true after Reinitialize. 32 (13) The result of the Reference function should be used with care; it is always safe to use that result in the task body whose attribute is being accessed. However, when the result is being used by another task, the programmer must make sure that the task whose attribute is being accessed is not yet terminated. Failing to do so could make the program execution erroneous. 33 (14) As specified in C.7.1, if the parameter T (in a call on a subprogram of an instance of this package) identifies a nonexistent task, the execution of the program is erroneous. Annex D (normative) Real-Time Systems 1 This Annex specifies additional characteristics of Ada implementations intended for real-time systems software. To conform to this Annex, an implementation shall also conform to the Systems Programming Annex. Metrics 2 The metrics are documentation requirements; an implementation shall document the values of the language-defined metrics for at least one configuration of hardware or an underlying system supported by the implementation, and shall document the details of that configuration. 3 The metrics do not necessarily yield a simple number. For some, a range is more suitable, for others a formula dependent on some parameter is appropriate, and for others, it may be more suitable to break the metric into several cases. Unless specified otherwise, the metrics in this annex are expressed in processor clock cycles. For metrics that require documentation of an upper bound, if there is no upper bound, the implementation shall report that the metric is unbounded. NOTES 4 (1) The specification of the metrics makes a distinction between upper bounds and simple execution times. Where something is just specified as ``the execution time of'' a piece of code, this leaves one the freedom to choose a nonpathological case. This kind of metric is of the form ``there exists a program such that the value of the metric is V''. Conversely, the meaning of upper bounds is ``there is no program such that the value of the metric is greater than V''. This kind of metric can only be partially tested, by finding the value of V for one or more test programs. 5 (2) The metrics do not cover the whole language; they are limited to features that are specified in Annex C, ``Systems Programming'' and in this Annex. The metrics are intended to provide guidance to potential users as to whether a particular implementation of such a feature is going to be adequate for a particular real-time application. As such, the metrics are aimed at known implementation choices that can result in significant performance differences. 6 (3) The purpose of the metrics is not necessarily to provide fine-grained quantitative results or to serve as a comparison between different implementations on the same or different platforms. Instead, their goal is rather qualitative; to define a standard set of approximate values that can be measured and used to estimate the general suitability of an implementation, or to evaluate the comparative utility of certain features of an implementation for a particular real-time application. D.1 Task Priorities 1 This clause specifies the priority model for real-time systems. In addition, the methods for specifying priorities are defined. Syntax 2 The form of a pragma Priority is as follows: 3 pragma Priority(expression); 4 The form of a pragma Interrupt_Priority is as follows: 5 pragma Interrupt_Priority[(expression)]; Name Resolution Rules 6 The expected type for the expression in a Priority or Interrupt_Priority pragma is Integer. Legality Rules 7 A Priority pragma is allowed only immediately within a task_definition, a protected_definition, or the declarative_part of a subprogram_body. An Interrupt_Priority pragma is allowed only immediately within a task_ definition or a protected_definition. At most one such pragma shall appear within a given construct. 8 For a Priority pragma that appears in the declarative_part of a subprogram_body, the expression shall be static, and its value shall be in the range of System.Priority. Static Semantics 9 The following declarations exist in package System: 10 subtype Any_Priority is Integer range implementation-defined; subtype Priority is Any_Priority range Any_Priority'First .. implementation\ -defined; subtype Interrupt_Priority is Any_Priority range Priority'Last+1 .. Any_Pri\ ority'Last; 11 Default_Priority : constant Priority := (Priority'First + Priority'Last)/2; 12 The full range of priority values supported by an implementation is specified by the subtype Any_Priority. The subrange of priority values that are high enough to require the blocking of one or more interrupts is specified by the subtype Interrupt_Priority. The subrange of priority values below System.Interrupt_Priority'First is specified by the subtype System.Priority. 13 The priority specified by a Priority or Interrupt_Priority pragma is the value of the expression in the pragma, if any. If there is no expression in an Interrupt_Priority pragma, the priority value is Interrupt_Priority'Last. Dynamic Semantics 14 A Priority pragma has no effect if it occurs in the declarative_part of the subprogram_body of a subprogram other than the main subprogram. 15 A task priority is an integer value that indicates a degree of urgency and is the basis for resolving competing demands of tasks for resources. Unless otherwise specified, whenever tasks compete for processors or other implementation-defined resources, the resources are allocated to the task with the highest priority value. The base priority of a task is the priority with which it was created, or to which it was later set by Dynamic_ Priorities.Set_Priority (see D.5). At all times, a task also has an active priority, which generally reflects its base priority as well as any priority it inherits from other sources. Priority inheritance is the process by which the priority of a task or other entity (e.g. a protected object; see D.3) is used in the evaluation of another task's active priority. 16 The effect of specifying such a pragma in a protected_definition is discussed in D.3. 17 The expression in a Priority or Interrupt_Priority pragma that appears in a task_definition is evaluated for each task object (see 9.1). For a Priority pragma, the value of the expression is converted to the subtype Priority; for an Interrupt_Priority pragma, this value is converted to the subtype Any_Priority. The priority value is then associated with the task object whose task_definition contains the pragma. 18 Likewise, the priority value is associated with the environment task if the pragma appears in the declarative_part of the main subprogram. 19 The initial value of a task's base priority is specified by default or by means of a Priority or Interrupt_Priority pragma. After a task is created, its base priority can be changed only by a call to Dynamic_ Priorities.Set_Priority (see D.5). The initial base priority of a task in the absence of a pragma is the base priority of the task that creates it at the time of creation (see 9.1). If a pragma Priority does not apply to the main subprogram, the initial base priority of the environment task is System.Default_Priority. The task's active priority is used when the task competes for processors. Similarly, the task's active priority is used to determine the task's position in any queue when Priority_Queuing is specified (see D.4). 20 At any time, the active priority of a task is the maximum of all the priorities the task is inheriting at that instant. For a task that is not held (see D.11), its base priority is always a source of priority inheritance. Other sources of priority inheritance are specified under the following conditions: 21 During activation, a task being activated inherits the active priority of the its activator (see 9.2). 22 During rendezvous, the task accepting the entry call inherits the active priority of the caller (see 9.5.3). 23 During a protected action on a protected object, a task inherits the ceiling priority of the protected object (see 9.5 and D.3). 24 In all of these cases, the priority ceases to be inherited as soon as the condition calling for the inheritance no longer exists. Implementation Requirements 25 The range of System.Interrupt_Priority shall include at least one value. 26 The range of System.Priority shall include at least 30 values. NOTES 27 (4) The priority expression can include references to discriminants of the enclosing type. 28 (5) It is a consequence of the active priority rules that at the point when a task stops inheriting a priority from another source, its active priority is re-evaluated. This is in addition to other instances described in this Annex for such re-evaluation. 29 (6) An implementation may provide a non-standard mode in which tasks inherit priorities under conditions other than those specified above. D.2 Priority Scheduling 1 This clause describes the rules that determine which task is selected for execution when more than one task is ready (see 9.2). The rules have two parts: the task dispatching model (see D.2.1), and a specific task dispatching policy (see D.2.2). D.2.1 The Task Dispatching Model 1 The task dispatching model specifies preemptive scheduling, based on conceptual priority-ordered ready queues. Dynamic Semantics 2 A task runs (that is, it becomes a running task) only when it is ready (see 9.2) and the execution resources required by that task are available. Processors are allocated to tasks based on each task's active priority. 3 It is implementation defined whether, on a multiprocessor, a task that is waiting for access to a protected object keeps its processor busy. 4 Task dispatching is the process by which one ready task is selected for execution on a processor. This selection is done at certain points during the execution of a task called task dispatching points. A task reaches a task dispatching point whenever it becomes blocked, and whenever it becomes ready. In addition, the completion of an accept_statement (see 9.5.2), and task termination are task dispatching points for the executing task. Other task dispatching points are defined throughout this Annex. 5 Task dispatching policies are specified in terms of conceptual ready queues, task states, and task preemption. A ready queue is an ordered list of ready tasks. The first position in a queue is called the head of the queue, and the last position is called the tail of the queue. A task is ready if it is in a ready queue, or if it is running. Each processor has one ready queue for each priority value. At any instant, each ready queue of a processor contains exactly the set of tasks of that priority that are ready for execution on that processor, but are not running on any processor; that is, those tasks that are ready, are not running on any processor, and can be executed using that processor and other available resources. A task can be on the ready queues of more than one processor. 6 Each processor also has one running task, which is the task currently being executed by that processor. Whenever a task running on a processor reaches a task dispatching point, one task is selected to run on that processor. The task selected is the one at the head of the highest priority nonempty ready queue; this task is then removed from all ready queues to which it belongs. 7 A preemptible resource is a resource that while allocated to one task can be allocated (temporarily) to another instead. Processors are preemptible resources. Access to a protected object (see 9.5.1) is a nonpreemptible resource. When a higher-priority task is dispatched to the processor, and the previously running task is placed on the appropriate ready queue, the latter task is said to be preempted. 8 A new running task is also selected whenever there is a nonempty ready queue with a higher priority than the priority of the running task, or when the task dispatching policy requires a running task to go back to a ready queue. These are also task dispatching points. Implementation Permissions 9 An implementation is allowed to define additional resources as execution resources, and to define the corresponding allocation policies for them. Such resources may have an implementation defined effect on task dispatching (see D.2.2). 10 An implementation may place implementation-defined restrictions on tasks whose active priority is in the Interrupt_Priority range. NOTES 11 (7) Section 9 specifies under which circumstances a task becomes ready. The ready state is affected by the rules for task activation and termination, delay statements, and entry calls. When a task is not ready, it is said to be blocked. 12 (8) An example of a possible implementation-defined execution resource is a page of physical memory, which needs to be loaded with a particular page of virtual memory before a task can continue execution. 13 (9) The ready queues are purely conceptual; there is no requirement that such lists physically exist in an implementation. 14 (10) While a task is running, it is not on any ready queue. Any time the task that is running on a processor is added to a ready queue, a new running task is selected for that processor. 15 (11) In a multiprocessor system, a task can be on the ready queues of more than one processor. At the extreme, if several processors share the same set of ready tasks, the contents of their ready queues is identical, and so they can be viewed as sharing one ready queue, and can be implemented that way. Thus, the dispatching model covers multiprocessors where dispatching is implemented using a single ready queue, as well as those with separate dispatching domains. 16 (12) The priority of a task is determined by rules specified in this subclause, and under D.1, ``Task Priorities'', D.3, ``Priority Ceiling Locking'', and D.5, ``Dynamic Priorities''. D.2.2 The Standard Task Dispatching Policy Syntax 1 The form of a pragma Task_Dispatching_Policy is as follows: 2 pragma Task_Dispatching_Policy(policy_identifier ); Legality Rules 3 The policy_identifier shall either be FIFO_Within_Priorities or an implementation-defined identifier. Post-Compilation Rules 4 A Task_Dispatching_Policy pragma is a configuration pragma. 5 If the FIFO_Within_Priorities policy is specified for a partition, then the Ceiling_Locking policy (see D.3) shall also be specified for the partition. Dynamic Semantics 6 A task dispatching policy specifies the details of task dispatching that are not covered by the basic task dispatching model. These rules govern when tasks are inserted into and deleted from the ready queues, and whether a task is inserted at the head or the tail of the queue for its active priority. The task dispatching policy is specified by a Task_Dispatching_Policy configuration pragma. If no such pragma appears in any of the program units comprising a partition, the task dispatching policy for that partition is unspecified. 7 The language defines only one task dispatching policy, FIFO_Within_ Priorities; when this policy is in effect, modifications to the ready queues occur only as follows: 8 When a blocked task becomes ready, it is added at the tail of the ready queue for its active priority. 9 When the active priority of a ready task that is not running changes, or the setting of its base priority takes effect, the task is removed from the ready queue for its old active priority and is added at the tail of the ready queue for its new active priority, except in the case where the active priority is lowered due to the loss of inherited priority, in which case the task is added at the head of the ready queue for its new active priority. 10 When the setting of the base priority of a running task takes effect, the task is added to the tail of the ready queue for its active priority. 11 When a task executes a delay_statement that does not result in blocking, it is added to the tail of the ready queue for its active priority. 12 Each of the events specified above is a task dispatching point (see D.2.1). 13 In addition, when a task is preempted, it is added at the head of the ready queue for its active priority.