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/3 This subclause 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/3 The support for interfacing aspects (see Annex B) should include 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/3 The implementation shall document the subprogram calling conventions associated with the convention identifiers available for use with the Convention aspect (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/3 This subclause specifies minimal requirements on the support for representation items and related features. Implementation Requirements 2/3 The implementation shall support at least the functionality defined by the recommended levels of support in Clause 13. C.3 Interrupt Support 1/3 This subclause 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 nonreserved 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 1. 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 2. Any interrupts that cannot be blocked, and the effect of attaching handlers to such interrupts, if this is permitted. 15 3. 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 4. Any implementation- or hardware-specific activity that happens before a user-defined interrupt handler gets control (e.g., reading device registers, acknowledging devices). 17 5. Any timing or other limitations imposed on the execution of interrupt handlers. 18 6. The state (blocked/unblocked) of the nonreserved 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 7. Whether the interrupted task is allowed to resume execution before the interrupt handler returns. 20 8. 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 9. 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 10. On a multi-processor, the rules governing the delivery of an interrupt to a particular processor. Implementation Permissions 23/2 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 for which 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/3 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/2 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 finer-grained 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 Paragraphs 1 through 6 were moved to Annex J, "Obsolescent Features". Static Semantics 6.1/3 For a parameterless protected procedure, the following language-defined representation aspects may be specified: 6.2/3 Interrupt_Handler The type of aspect Interrupt_Handler is Boolean. If directly specified, the aspect_definition shall be a static expression. This aspect is never inherited; if not directly specified, the aspect is False. 6.3/3 Attach_Handler The aspect Attach_Handler is an expression, which shall be of type Interrupts.Interrupt_Id. This aspect is never inherited. Legality Rules 7/3 If either the Attach_Handler or Interrupt_Handler aspect are specified for a protected procedure, the corresponding protected_type_declaration or single_- protected_declaration shall be a library-level declaration and shall not be declared within a generic body. In addition to the places where Legality Rules normally apply (see 12.3), this rule also applies in the private part of an instance of a generic unit. 8/3 This paragraph was deleted. Dynamic Semantics 9/3 If the Interrupt_Handler aspect of a protected procedure is True, then the procedure may be attached dynamically, as a handler, to interrupts (see C.3.2). Such procedures are allowed to be attached to multiple interrupts. 10/3 The expression specified for the Attach_Handler aspect of a protected procedure P is evaluated as part of the creation of the protected object that contains P. The value of the expression identifies an interrupt. As part of the initialization of that object, P (the handler procedure) is attached to the identified 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/3 If the Ceiling_Locking policy (see D.3) is in effect, then upon the initialization of a protected object that contains a protected procedure for which either the Attach_Handler aspect is specified or the Interrupt_Handler aspect is True, a check is made that the initial ceiling priority of the object is in the range of System.Interrupt_Priority. If the check fails, Program_Error is raised. 12/3 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. If the Attach_- Handler aspect was specified and the most recently attached handler for the same interrupt is the same as the one that was attached at the time the protected object was initialized, 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. 14.1/3 If the handlers for a given interrupt attached via aspect Attach_Handler are not attached and detached in a stack-like (LIFO) order, program execution is erroneous. In particular, when a protected object is finalized, the execution is erroneous if any of the procedures of the protected object are attached to interrupts via aspect Attach_Handler and the most recently attached handler for the same interrupt is not the same as the one that was attached at the time the protected object was initialized. Metrics 15 The following metric shall be documented by the implementation: 16/2 * 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/3 When the aspects Attach_Handler or Interrupt_Handler are specified for 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/3 Notwithstanding what this subclause says elsewhere, the Attach_Handler and Interrupt_Handler aspects 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/3 4 The Attach_Handler aspect may provide static attachment of handlers to interrupts if the implementation supports preelaboration of protected objects. (See C.4.) 23/2 5 A protected object that has a (protected) procedure attached to an interrupt should have a ceiling priority 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/3 with System; with System.Multiprocessors; package Ada.Interrupts is type Interrupt_Id is implementation-defined; type Parameterless_Handler is access protected procedure; 3/1 This paragraph was deleted. 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; 10.1/3 function Get_CPU (Interrupt : Interrupt_Id) return System.Multiprocessors.CPU_Range; 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/1 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 null. 17/3 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 for which the aspect Interrupt_Handler is False, Program_Error is raised. In this case, the operation does not modify the existing interrupt treatment. 18/1 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. If the previous treatment is not a user-defined handler, null is returned. 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/3 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 aspect Attach_Handler), the handler is not detached and Program_Error is raised. 22/2 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. 22.1/3 The function Get_CPU returns the processor on which the handler for Interrupt is executed. If the handler can execute on more than one processor the value System.Multiprocessors.Not_A_Specific_CPU is returned. 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/3 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 a protected procedure that specifies either the Attach_Handler or Interrupt_Handler aspects, but does not specify the Interrupt_Priority aspect. 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/3 Device_Priority : constant array (1..5) of System.Interrupt_Priority := ( ... ); protected type Device_Interface (Int_Id : Ada.Interrupts.Interrupt_Id) with Interrupt_Priority => Device_Priority(Int_Id) is procedure Handler with Attach_Handler => Int_Id; ... end Device_Interface; ... Device_1_Driver : Device_Interface(1); ... Device_5_Driver : Device_Interface(5); ... C.4 Preelaboration Requirements 1/3 This subclause 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; 4.1/2 * no subtype_mark denotes a controlled type, a private type, a private extension, a generic formal private type, a generic formal derived type, or a descendant of such a type; 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 Aspect Discard_Names 1/4 Specifying the aspect Discard_Names can be used to request a reduction in storage used for the names of entities with runtime name text. Static Semantics 1.1/4 An entity with runtime name text is a nonderived enumeration first subtype, a tagged first subtype, or an exception. 1.2/4 For an entity with runtime name text, the following language-defined representation aspect may be specified: 1.3/4 Discard_Names The type of aspect Discard_Names is Boolean. If directly specified, the aspect_definition shall be a static expression. If not specified (including by inheritance), the aspect is False. 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/4 The local_name (if present) shall denote an entity with runtime name text. The pragma specifies that the aspect Discard_Names for the type or exception has the value True. Without a local_name, the pragma specifies that all entities with runtime name text declared after the pragma, within the same declarative region have the value True for aspect Discard_Names. Alternatively, the pragma can be used as a configuration pragma. If the configuration pragma Discard_Names applies to a compilation unit, all entities with runtime name text declared in the compilation unit have the value True for the aspect Discard_Names.. Static Semantics 6 If a local_name is given, then a pragma Discard_Names is a representation pragma. 7/4 If the aspect Discard_Names is True for an enumeration type, then the semantics of the Wide_Wide_Image and Wide_Wide_Value attributes are implementation defined for that type; the semantics of Image, Wide_Image, Value, and Wide_Value are still defined in terms of Wide_Wide_Image and Wide_Wide_Value. In addition, the semantics of Text_IO.Enumeration_IO are implementation defined. If the aspect Discard_Names is True for a tagged type, then the semantics of the Tags.Wide_Wide_Expanded_Name function are implementation defined for that type; the semantics of Tags.Expanded_Name and Tags.Wide_Expanded_Name are still defined in terms of Tags.Wide_Wide_Expanded_- Name. If the aspect Discard_Names is True for an exception, then the semantics of the Exceptions.Wide_Wide_Exception_Name function are implementation defined for that exception; the semantics of Exceptions.Exception_Name and Exceptions.- Wide_Exception_Name are still defined in terms of Exceptions.Wide_Wide_- Exception_Name. Implementation Advice 8/4 If the aspect Discard_Names is True for 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/3 This subclause defines representation aspects that control the use of shared variables. Paragraphs 2 through 6 were moved to Annex J, "Obsolescent Features". Static Semantics 6.1/3 For an object_declaration, a component_declaration, or a full_type_declaration, the following representation aspects may be specified: 6.2/3 Atomic The type of aspect Atomic is Boolean. 6.3/3 Independent The type of aspect Independent is Boolean. 6.4/3 Volatile The type of aspect Volatile is Boolean. 6.5/3 For a full_type_declaration of an array type (including the anonymous type of an object_declaration of an anonymous array object), the following representation aspects may be specified: 6.6/3 Atomic_Components The type of aspect Atomic_Components is Boolean. 6.7/3 Volatile_Components The type of aspect Volatile_Components is Boolean. 6.8/3 For a full_type_declaration (including the anonymous type of an object_declaration of an anonymous array object), the following representation aspect may be specified: 6.9/3 Independent_Components The type of aspect Independent_Components is Boolean. 6.10/3 If any of these aspects are directly specified, the aspect_definition shall be a static expression. If not specified (including by inheritance), each of these aspects is False. 7/3 An atomic type is one for which the aspect Atomic is True. An atomic object (including a component) is one for which the aspect Atomic is True, or a component of an array for which the aspect Atomic_Components is True for the associated type, or any object of an atomic type, other than objects obtained by evaluating a slice. 8/3 A volatile type is one for which the aspect Volatile is True. A volatile object (including a component) is one for which the aspect Volatile is True, or a component of an array for which the aspect Volatile_Components is True for the associated type, 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). 8.1/4 When True, the aspects Independent and Independent_Components specify as independently addressable the named object or component(s), or in the case of a type, all objects or components of that type. All atomic objects and aliased objects are considered to be specified as independently addressable. Paragraph 9 was moved to Annex J, "Obsolescent Features". Legality Rules 9.1/3 If aspect Independent_Components is specified for a full_type_declaration, the declaration shall be that of an array or record type. 10/4 It is illegal to specify either of the aspects Atomic or Atomic_Components to have the value True for an object or type if the implementation cannot support the indivisible and independent reads and updates required by the aspect (see below). 11/4 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 and independent reads and updates. 12/3 If an atomic object is passed as a parameter, then the formal parameter shall either have an atomic type or allow pass by copy. 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. Corresponding rules apply to volatile objects and types. 12.1/3 If a volatile type is used as an actual for a generic formal array type, then the element type of the formal type shall be volatile. 13/3 If an aspect Volatile, Volatile_Components, Atomic, or Atomic_Components is directly specified to have the value True for a stand-alone constant object, then the aspect Import shall also be specified as True for it. 13.1/3 It is illegal to specify the aspect Independent or Independent_Components as True for a component, object or type if the implementation cannot provide the independent addressability required by the aspect (see 9.10). 13.2/3 It is illegal to specify a representation aspect for a component, object or type for which the aspect Independent or Independent_Components is True, in a way that prevents the implementation from providing the independent addressability required by the aspect. Paragraph 14 was moved to Annex J, "Obsolescent Features". Dynamic Semantics 15 For an atomic object (including an atomic component) all reads and updates of the object as a whole are indivisible. 16/3 All tasks of the program (on all processors) that read or update volatile variables see the same order of updates to the variables. A use of an atomic variable or other mechanism may be necessary to avoid erroneous execution and to ensure that access to nonatomic volatile variables is sequential (see 9.10). 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/4 This paragraph was deleted. Implementation Advice 22/2 A load or store of a volatile object whose size is a multiple of System.Storage_Unit and whose alignment is nonzero, should be implemented by accessing exactly the bits of the object and no others. 23/2 A load or store of an atomic object should, where possible, be implemented by a single load or store instruction. NOTES 24 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." 25/4 10 Specifying the Pack aspect cannot override the effect of specifying an Atomic or Atomic_Components aspect. C.7 Task Information 1/3 This subclause 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. Finally, a package that associates termination procedures with a task or set of tasks is defined. C.7.1 The Package Task_Identification Static Semantics 1 The following language-defined library package exists: 2/2 package Ada.Task_Identification is pragma Preelaborate(Task_Identification); type Task_Id is private; pragma Preelaborable_Initialization (Task_Id); Null_Task_Id : constant Task_Id; function "=" (Left, Right : Task_Id) return Boolean; 3/3 function Image (T : Task_Id) return String; function Current_Task return Task_Id; function Environment_Task return Task_Id; procedure Abort_Task (T : in Task_Id); 4/3 function Is_Terminated (T : Task_Id) return Boolean; function Is_Callable (T : Task_Id) return Boolean; function Activation_Is_Complete (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. 8.1/3 The function Environment_Task returns a value that identifies the environment 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. 10.1/3 The function Activation_Is_Complete returns True if the task identified by T has completed its activation (whether successfully or not). It returns False otherwise. If T identifies the environment task, Activation_Is_Complete returns True after the elaboration of the library_items of the partition has completed. 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/3 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 accept_statement, or entry_body after the entry_barrier, 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/3 It is a bounded error to call the Current_Task function from an entry_body, interrupt handler, or finalization of a task attribute. 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 11 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 12 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. 13.1/2 After a task has terminated, all of its attributes are finalized, unless they have been finalized earlier. When the master of an instantiation of Ada.Task_Attributes is finalized, the corresponding attribute of each task is finalized, unless it has been finalized earlier. Bounded (Run-Time) Errors 13.2/1 If the package Ada.Task_Attributes is instantiated with a controlled type and the controlled type has user-defined Adjust or Finalize operations that in turn access task attributes by any of the above operations, then a call of Set_Value of the instantiated package constitutes a bounded error. The call may perform as expected or may result in forever blocking the calling task and subsequently some or all tasks of the partition. 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. 15.1/2 An access to a task attribute via a value of type Attribute_Handle is erroneous if executed concurrently with another such access or a call of any of the operations declared in package Task_Attributes. An access to a task attribute is erroneous if executed concurrently with or after the finalization of the task attribute. Implementation Requirements 16/1 For a given attribute of a given task, the implementation shall perform the operations declared in this package atomically with respect to any of these operations of the same attribute of the same task. The granularity of any locking mechanism necessary to achieve such atomicity is implementation defined. 17/2 After task attributes are finalized, the implementation shall reclaim any 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/2 The implementation shall document the following metrics: A task calling the following subprograms shall execute at 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 type whose size is equal to the size of the predefined type Integer. 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, nonterminated, 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/2 * 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/2 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 attributes of a task, 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. 30.1/2 Finalization of task attributes and reclamation of associated storage should be performed as soon as possible after task termination. NOTES 31 13 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 14 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. C.7.3 The Package Task_Termination Static Semantics 1/2 The following language-defined library package exists: 2/2 with Ada.Task_Identification; with Ada.Exceptions; package Ada.Task_Termination is pragma Preelaborate(Task_Termination); 3/2 type Cause_Of_Termination is (Normal, Abnormal, Unhandled_Exception); 4/2 type Termination_Handler is access protected procedure (Cause : in Cause_Of_Termination; T : in Ada.Task_Identification.Task_Id; X : in Ada.Exceptions.Exception_Occurrence); 5/2 procedure Set_Dependents_Fallback_Handler (Handler: in Termination_Handler); function Current_Task_Fallback_Handler return Termination_Handler; 6/2 procedure Set_Specific_Handler (T : in Ada.Task_Identification.Task_Id; Handler : in Termination_Handler); function Specific_Handler (T : Ada.Task_Identification.Task_Id) return Termination_Handler; 7/2 end Ada.Task_Termination; Dynamic Semantics 8/3 The type Termination_Handler identifies a protected procedure to be executed by the implementation when a task terminates. Such a protected procedure is called a handler. In all cases T identifies the task that is terminating. If the task terminates due to completing the last statement of its body, or as a result of waiting on a terminate alternative, and the finalization of the task completes normally, then Cause is set to Normal and X is set to Null_Occurrence. If the task terminates because it is being aborted, then Cause is set to Abnormal; X is set to Null_Occurrence if the finalization of the task completes normally. If the task terminates because of an exception raised by the execution of its task_body, then Cause is set to Unhandled_Exception; X is set to the associated exception occurrence if the finalization of the task completes normally. Independent of how the task completes, if finalization of the task propagates an exception, then Cause is either Unhandled_Exception or Abnormal, and X is an exception occurrence that identifies the Program_Error exception. 9/2 Each task has two termination handlers, a fall-back handler and a specific handler. The specific handler applies only to the task itself, while the fall-back handler applies only to the dependent tasks of the task. A handler is said to be set if it is associated with a nonnull value of type Termination_Handler, and cleared otherwise. When a task is created, its specific handler and fall-back handler are cleared. 10/3 The procedure Set_Dependents_Fallback_Handler changes the fall-back handler for the calling task: if Handler is null, that fall-back handler is cleared; otherwise, it is set to be Handler.all. If a fall-back handler had previously been set it is replaced. 11/3 The function Current_Task_Fallback_Handler returns the fall-back handler that is currently set for the calling task, if one is set; otherwise, it returns null. 12/3 The procedure Set_Specific_Handler changes the specific handler for the task identified by T: if Handler is null, that specific handler is cleared; otherwise, it is set to be Handler.all. If a specific handler had previously been set it is replaced. 13/3 The function Specific_Handler returns the specific handler that is currently set for the task identified by T, if one is set; otherwise, it returns null. 14/2 As part of the finalization of a task_body, after performing the actions specified in 7.6 for finalization of a master, the specific handler for the task, if one is set, is executed. If the specific handler is cleared, a search for a fall-back handler proceeds by recursively following the master relationship for the task. If a task is found whose fall-back handler is set, that handler is executed; otherwise, no handler is executed. 15/2 For Set_Specific_Handler or Specific_Handler, Tasking_Error is raised if the task identified by T has already terminated. Program_Error is raised if the value of T is Ada.Task_Identification.Null_Task_Id. 16/2 An exception propagated from a handler that is invoked as part of the termination of a task has no effect. Erroneous Execution 17/2 For a call of Set_Specific_Handler or Specific_Handler, if the task identified by T no longer exists, the execution of the program is erroneous.