dwww Home | Show directory contents | Find package


                    Section 9: Tasks and Synchronization


1   The execution of an Ada program consists of the execution of one or more
tasks. Each task represents a separate thread of control that proceeds
independently and concurrently between the points where it interacts with
other tasks. The various forms of task interaction are described in this
section, and include:

2     * the activation and termination of a task;

3     * a call on a protected subprogram of a protected object, providing
        exclusive read-write access, or concurrent read-only access to shared
        data;

4     * a call on an entry, either of another task, allowing for synchronous
        communication with that task, or of a protected object, allowing for
        asynchronous communication with one or more other tasks using that
        same protected object;

5     * a timed operation, including a simple delay statement, a timed entry
        call or accept, or a timed asynchronous select statement (see next
        item);

6     * an asynchronous transfer of control as part of an asynchronous select
        statement, where a task stops what it is doing and begins execution at
        a different point in response to the completion of an entry call or
        the expiration of a delay;

7     * an abort statement, allowing one task to cause the termination of
        another task.

8   In addition, tasks can communicate indirectly by reading and updating
(unprotected) shared variables, presuming the access is properly synchronized
through some other kind of task interaction.


                              Static Semantics

9   The properties of a task are defined by a corresponding task declaration
and task_body, which together define a program unit called a task unit.


                              Dynamic Semantics

10  Over time, tasks proceed through various states. A task is initially
inactive; upon activation, and prior to its termination it is either blocked
(as part of some task interaction) or ready to run. While ready, a task
competes for the available execution resources that it requires to run.

        NOTES

11      1  Concurrent task execution may be implemented on multicomputers,
        multiprocessors, or with interleaved execution on a single physical
        processor. On the other hand, whenever an implementation can determine
        that the required semantic effects can be achieved when parts of the
        execution of a given task are performed by different physical
        processors acting in parallel, it may choose to perform them in this
        way.


9.1 Task Units and Task Objects


1   A task unit is declared by a task declaration, which has a corresponding
task_body. A task declaration may be a task_type_declaration, in which case it
declares a named task type; alternatively, it may be a
single_task_declaration, in which case it defines an anonymous task type, as
well as declaring a named task object of that type.


                                   Syntax

2/2     task_type_declaration ::= 
           task type defining_identifier [known_discriminant_part] [is
             [new interface_list with]
             task_definition];

3/2     single_task_declaration ::= 
           task defining_identifier [is
             [new interface_list with]
             task_definition];

4       task_definition ::= 
             {task_item}
          [ private
             {task_item}]
          end [task_identifier]

5/1     task_item ::= entry_declaration | aspect_clause

6       task_body ::= 
           task body defining_identifier is
             declarative_part
           begin
             handled_sequence_of_statements
           end [task_identifier];

7       If a task_identifier appears at the end of a task_definition or
        task_body, it shall repeat the defining_identifier.


                               Legality Rules

8/2 This paragraph was deleted.


                              Static Semantics

9   A task_definition defines a task type and its first subtype. The first
list of task_items of a task_definition, together with the known_discriminant_-
part, if any, is called the visible part of the task unit. The optional list
of task_items after the reserved word private is called the private part of
the task unit.

9.1/1 For a task declaration without a task_definition, a task_definition
without task_items is assumed.

9.2/2 For a task declaration with an interface_list, the task type inherits
user-defined primitive subprograms from each progenitor type (see 3.9.4), in
the same way that a derived type inherits user-defined primitive subprograms
from its progenitor types (see 3.4). If the first parameter of a primitive
inherited subprogram is of the task type or an access parameter designating
the task type, and there is an entry_declaration for a single entry with the
same identifier within the task declaration, whose profile is type conformant
with the prefixed view profile of the inherited subprogram, the inherited
subprogram is said to be implemented by the conforming task entry.


                               Legality Rules

9.3/2 A task declaration requires a completion, which shall be a task_body,
and every task_body shall be the completion of some task declaration.

9.4/2 Each interface_subtype_mark of an interface_list appearing within a task
declaration shall denote a limited interface type that is not a protected
interface.

9.5/2 The prefixed view profile of an explicitly declared primitive subprogram
of a tagged task type shall not be type conformant with any entry of the task
type, if the first parameter of the subprogram is of the task type or is an
access parameter designating the task type.

9.6/2 For each primitive subprogram inherited by the type declared by a task
declaration, at most one of the following shall apply:

9.7/2   * the inherited subprogram is overridden with a primitive subprogram
        of the task type, in which case the overriding subprogram shall be
        subtype conformant with the inherited subprogram and not abstract; or

9.8/2   * the inherited subprogram is implemented by a single entry of the
        task type; in which case its prefixed view profile shall be subtype
        conformant with that of the task entry.

9.9/2 If neither applies, the inherited subprogram shall be a null procedure.
In addition to the places where Legality Rules normally apply (see 12.3),
these rules also apply in the private part of an instance of a generic unit.


                              Dynamic Semantics

10  The elaboration of a task declaration elaborates the task_definition. The
elaboration of a single_task_declaration also creates an object of an
(anonymous) task type.

11  The elaboration of a task_definition creates the task type and its first
subtype; it also includes the elaboration of the entry_declarations in the
given order.

12/1 As part of the initialization of a task object, any aspect_clauses and
any per-object constraints associated with entry_declarations of the
corresponding task_definition are elaborated in the given order.

13  The elaboration of a task_body has no effect other than to establish that
tasks of the type can from then on be activated without failing the
Elaboration_Check.

14  The execution of a task_body is invoked by the activation of a task of the
corresponding type (see 9.2).

15  The content of a task object of a given task type includes:

16    * The values of the discriminants of the task object, if any;

17    * An entry queue for each entry of the task object;

18    * A representation of the state of the associated task.

        NOTES

19/2    2  Other than in an access_definition, the name of a task unit within
        the declaration or body of the task unit denotes the current instance
        of the unit (see 8.6), rather than the first subtype of the
        corresponding task type (and thus the name cannot be used as a
        subtype_mark).

20      3  The notation of a selected_component can be used to denote a
        discriminant of a task (see 4.1.3). Within a task unit, the name of a
        discriminant of the task type denotes the corresponding discriminant
        of the current instance of the unit.

21/2    4  A task type is a limited type (see 7.5), and hence precludes use of
        assignment_statements and predefined equality operators. If an
        application needs to store and exchange task identities, it can do so
        by defining an access type designating the corresponding task objects
        and by using access values for identification purposes. Assignment is
        available for such an access type as for any access type.
        Alternatively, if the implementation supports the Systems Programming
        Annex, the Identity attribute can be used for task identification (see
        C.7.1).


                                  Examples

22  Examples of declarations of task types:

23      task type Server is
           entry Next_Work_Item(WI : in Work_Item);
           entry Shut_Down;
        end Server;

24/2    task type Keyboard_Driver(ID : Keyboard_ID := New_ID) is
              new Serial_Device with  -- see 3.9.4
           entry Read (C : out Character);
           entry Write(C : in  Character);
        end Keyboard_Driver;

25  Examples of declarations of single tasks:

26      task Controller is
           entry Request(Level)(D : Item);  --  a family of entries
        end Controller;

27      task Parser is
           entry Next_Lexeme(L : in  Lexical_Element);
           entry Next_Action(A : out Parser_Action);
        end;

28      task User;  --  has no entries

29  Examples of task objects:

30      Agent    : Server;
        Teletype : Keyboard_Driver(TTY_ID);
        Pool     : array(1 .. 10) of Keyboard_Driver;

31  Example of access type designating task objects:

32      type Keyboard is access Keyboard_Driver;
        Terminal : Keyboard := new Keyboard_Driver(Term_ID);


9.2 Task Execution - Task Activation



                              Dynamic Semantics

1   The execution of a task of a given task type consists of the execution of
the corresponding task_body. The initial part of this execution is called the
activation of the task; it consists of the elaboration of the
declarative_part of the task_body. Should an exception be propagated by the
elaboration of its declarative_part, the activation of the task is defined to
have failed, and it becomes a completed task.

2/2 A task object (which represents one task) can be a part of a stand-alone
object, of an object created by an allocator, or of an anonymous object of a
limited type, or a coextension of one of these. All tasks that are part or
coextensions of any of the stand-alone objects created by the elaboration of
object_declarations (or generic_associations of formal objects of mode in) of
a single declarative region are activated together. All tasks that are part or
coextensions of a single object that is not a stand-alone object are activated
together.

3/2 For the tasks of a given declarative region, the activations are initiated
within the context of the handled_sequence_of_statements (and its associated
exception_handlers if any - see 11.2), just prior to executing the statements
of the handled_sequence_of_statements. For a package without an explicit body
or an explicit handled_sequence_of_statements, an implicit body or an implicit
null_statement is assumed, as defined in 7.2.

4/2 For tasks that are part or coextensions of a single object that is not a
stand-alone object, activations are initiated after completing any
initialization of the outermost object enclosing these tasks, prior to
performing any other operation on the outermost object. In particular, for
tasks that are part or coextensions of the object created by the evaluation of
an allocator, the activations are initiated as the last step of evaluating the
allocator, prior to returning the new access value. For tasks that are part or
coextensions of an object that is the result of a function call, the
activations are not initiated until after the function returns.

5   The task that created the new tasks and initiated their activations (the
activator) is blocked until all of these activations complete (successfully or
not). Once all of these activations are complete, if the activation of any of
the tasks has failed (due to the propagation of an exception), Tasking_Error
is raised in the activator, at the place at which it initiated the
activations. Otherwise, the activator proceeds with its execution normally.
Any tasks that are aborted prior to completing their activation are ignored
when determining whether to raise Tasking_Error.

6   Should the task that created the new tasks never reach the point where it
would initiate the activations (due to an abort or the raising of an
exception), the newly created tasks become terminated and are never activated.

        NOTES

7       5  An entry of a task can be called before the task has been activated.

8       6  If several tasks are activated together, the execution of any of
        these tasks need not await the end of the activation of the other
        tasks.

9       7  A task can become completed during its activation either because of
        an exception or because it is aborted (see 9.8).


                                  Examples

10  Example of task activation:

11      procedure P is
           A, B : Server;    --  elaborate the task objects A, B
           C    : Server;    --  elaborate the task object C
        begin
           --  the tasks A, B, C are activated together before the first statement
           ...
        end;


9.3 Task Dependence - Termination of Tasks



                              Dynamic Semantics

1   Each task (other than an environment task - see 10.2) depends on one or
more masters (see 7.6.1), as follows:

2     * If the task is created by the evaluation of an allocator for a given
        access type, it depends on each master that includes the elaboration
        of the declaration of the ultimate ancestor of the given access type.

3     * If the task is created by the elaboration of an object_declaration, it
        depends on each master that includes this elaboration.

3.1/2   * Otherwise, the task depends on the master of the outermost object of
        which it is a part (as determined by the accessibility level of that
        object - see 3.10.2 and 7.6.1), as well as on any master whose
        execution includes that of the master of the outermost object.

4   Furthermore, if a task depends on a given master, it is defined to depend
on the task that executes the master, and (recursively) on any master of that
task.

5   A task is said to be completed when the execution of its corresponding
task_body is completed. A task is said to be terminated when any finalization
of the task_body has been performed (see 7.6.1). The first step of finalizing
a master (including a task_body) is to wait for the termination of any tasks
dependent on the master. The task executing the master is blocked until all
the dependents have terminated. Any remaining finalization is then performed
and the master is left.

6/1 Completion of a task (and the corresponding task_body) can occur when the
task is blocked at a select_statement with an open terminate_alternative (see
9.7.1); the open terminate_alternative is selected if and only if the
following conditions are satisfied:

7/2   * The task depends on some completed master; and

8     * Each task that depends on the master considered is either already
        terminated or similarly blocked at a select_statement with an open
        terminate_alternative.

9   When both conditions are satisfied, the task considered becomes completed,
together with all tasks that depend on the master considered that are not yet
completed.

        NOTES

10      8  The full view of a limited private type can be a task type, or can
        have subcomponents of a task type. Creation of an object of such a
        type creates dependences according to the full type.

11      9  An object_renaming_declaration defines a new view of an existing
        entity and hence creates no further dependence.

12      10  The rules given for the collective completion of a group of tasks
        all blocked on select_statements with open terminate_alternatives
        ensure that the collective completion can occur only when there are no
        remaining active tasks that could call one of the tasks being
        collectively completed.

13      11  If two or more tasks are blocked on select_statements with open
        terminate_alternatives, and become completed collectively, their
        finalization actions proceed concurrently.

14      12  The completion of a task can occur due to any of the following:

15        * the raising of an exception during the elaboration of the
            declarative_part of the corresponding task_body;

16        * the completion of the handled_sequence_of_statements of the
            corresponding task_body;

17        * the selection of an open terminate_alternative of a
            select_statement in the corresponding task_body;

18        * the abort of the task.


                                  Examples

19  Example of task dependence:

20      declare
           type Global is access Server;        --  see 9.1
           A, B : Server;
           G    : Global;
        begin
           --  activation of A and B
           declare
              type Local is access Server;
              X : Global := new Server;  --  activation of X.all
              L : Local  := new Server;  --  activation of L.all
              C : Server;
           begin
              --  activation of C
              G := X;  --  both G and X designate the same task object
              ...
           end;  --  await termination of C and L.all (but not X.all)
           ...
        end;  --  await termination of A, B, and G.all




9.4 Protected Units and Protected Objects


1   A protected object provides coordinated access to shared data, through
calls on its visible protected operations, which can be protected subprograms
or protected entries. A protected unit is declared by a protected declaration,
which has a corresponding protected_body. A protected declaration may be a
protected_type_declaration, in which case it declares a named protected type;
alternatively, it may be a single_protected_declaration, in which case it
defines an anonymous protected type, as well as declaring a named protected
object of that type.


                                   Syntax

2/2     protected_type_declaration ::= 
          protected type defining_identifier [known_discriminant_part] is
             [new interface_list with]
             protected_definition;

3/2     single_protected_declaration ::= 
          protected defining_identifier is
             [new interface_list with]
             protected_definition;

4       protected_definition ::= 
            { protected_operation_declaration }
        [ private
            { protected_element_declaration } ]
          end [protected_identifier]

5/1     protected_operation_declaration ::= subprogram_declaration
             | entry_declaration
             | aspect_clause

6       protected_element_declaration ::= protected_operation_declaration
             | component_declaration

7       protected_body ::= 
          protected body defining_identifier is
           { protected_operation_item }
          end [protected_identifier];

8/1     protected_operation_item ::= subprogram_declaration
             | subprogram_body
             | entry_body
             | aspect_clause

9       If a protected_identifier appears at the end of a
        protected_definition or protected_body, it shall repeat the
        defining_identifier.


                               Legality Rules

10/2 This paragraph was deleted.


                              Static Semantics

11/2 A protected_definition defines a protected type and its first subtype.
The list of protected_operation_declarations of a protected_definition,
together with the known_discriminant_part, if any, is called the visible part
of the protected unit. The optional list of protected_element_declarations
after the reserved word private is called the private part of the protected
unit.

11.1/2 For a protected declaration with an interface_list, the protected type
inherits user-defined primitive subprograms from each progenitor type (see
3.9.4), in the same way that a derived type inherits user-defined primitive
subprograms from its progenitor types (see 3.4). If the first parameter of a
primitive inherited subprogram is of the protected type or an access parameter
designating the protected type, and there is a
protected_operation_declaration for a protected subprogram or single entry
with the same identifier within the protected declaration, whose profile is
type conformant with the prefixed view profile of the inherited subprogram,
the inherited subprogram is said to be implemented by the conforming protected
subprogram or entry.


                               Legality Rules

11.2/2 A protected declaration requires a completion, which shall be a
protected_body, and every protected_body shall be the completion of some
protected declaration.

11.3/2 Each interface_subtype_mark of an interface_list appearing within a
protected declaration shall denote a limited interface type that is not a task
interface.

11.4/2 The prefixed view profile of an explicitly declared primitive
subprogram of a tagged protected type shall not be type conformant with any
protected operation of the protected type, if the first parameter of the
subprogram is of the protected type or is an access parameter designating the
protected type.

11.5/2 For each primitive subprogram inherited by the type declared by a
protected declaration, at most one of the following shall apply:

11.6/2   * the inherited subprogram is overridden with a primitive subprogram
        of the protected type, in which case the overriding subprogram shall
        be subtype conformant with the inherited subprogram and not abstract;
        or

11.7/2   * the inherited subprogram is implemented by a protected subprogram
        or single entry of the protected type, in which case its prefixed view
        profile shall be subtype conformant with that of the protected
        subprogram or entry.

11.8/2 If neither applies, the inherited subprogram shall be a null procedure.
In addition to the places where Legality Rules normally apply (see 12.3),
these rules also apply in the private part of an instance of a generic unit.

11.9/2 If an inherited subprogram is implemented by a protected procedure or
an entry, then the first parameter of the inherited subprogram shall be of
mode out or in out, or an access-to-variable parameter.

11.10/2 If a protected subprogram declaration has an overriding_indicator,
then at the point of the declaration:

11.11/2   * if the overriding_indicator is overriding, then the subprogram
        shall implement an inherited subprogram;

11.12/2   * if the overriding_indicator is not overriding, then the subprogram
        shall not implement any inherited subprogram.

11.13/2 In addition to the places where Legality Rules normally apply (see
12.3), these rules also apply in the private part of an instance of a generic
unit.


                              Dynamic Semantics

12  The elaboration of a protected declaration elaborates the
protected_definition. The elaboration of a single_protected_declaration also
creates an object of an (anonymous) protected type.

13  The elaboration of a protected_definition creates the protected type and
its first subtype; it also includes the elaboration of the
component_declarations and protected_operation_declarations in the given order.

14  As part of the initialization of a protected object, any per-object
constraints (see 3.8) are elaborated.

15  The elaboration of a protected_body has no other effect than to establish
that protected operations of the type can from then on be called without
failing the Elaboration_Check.

16  The content of an object of a given protected type includes:

17    * The values of the components of the protected object, including
        (implicitly) an entry queue for each entry declared for the protected
        object;

18    * A representation of the state of the execution resource associated
        with the protected object (one such resource is associated with each
        protected object).

19  The execution resource associated with a protected object has to be
acquired to read or update any components of the protected object; it can be
acquired (as part of a protected action - see 9.5.1) either for concurrent
read-only access, or for exclusive read-write access.

20  As the first step of the finalization of a protected object, each call
remaining on any entry queue of the object is removed from its queue and
Program_Error is raised at the place of the corresponding
entry_call_statement.


                          Bounded (Run-Time) Errors

20.1/2 It is a bounded error to call an entry or subprogram of a protected
object after that object is finalized. If the error is detected, Program_Error
is raised. Otherwise, the call proceeds normally, which may leave a task
queued forever.

        NOTES

21/2    13  Within the declaration or body of a protected unit other than in
        an access_definition, the name of the protected unit denotes the
        current instance of the unit (see 8.6), rather than the first subtype
        of the corresponding protected type (and thus the name cannot be used
        as a subtype_mark).

22      14  A selected_component can be used to denote a discriminant of a
        protected object (see 4.1.3). Within a protected unit, the name of a
        discriminant of the protected type denotes the corresponding
        discriminant of the current instance of the unit.

23/2    15  A protected type is a limited type (see 7.5), and hence precludes
        use of assignment_statements and predefined equality operators.

24      16  The bodies of the protected operations given in the
        protected_body define the actions that take place upon calls to the
        protected operations.

25      17  The declarations in the private part are only visible within the
        private part and the body of the protected unit.


                                  Examples

26  Example of declaration of protected type and corresponding body:

27      protected type Resource is
           entry Seize;
           procedure Release;
        private
           Busy : Boolean := False;
        end Resource;

28      protected body Resource is
           entry Seize when not Busy is
           begin
              Busy := True;
           end Seize;

29         procedure Release is
           begin
              Busy := False;
           end Release;
        end Resource;

30  Example of a single protected declaration and corresponding body:

31      protected Shared_Array is
           --  Index, Item, and Item_Array are global types
           function  Component    (N : in Index) return Item;
           procedure Set_Component(N : in Index; E : in  Item);
        private
           Table : Item_Array(Index) := (others => Null_Item);
        end Shared_Array;

32      protected body Shared_Array is
           function Component(N : in Index) return Item is
           begin
              return Table(N);
           end Component;

33         procedure Set_Component(N : in Index; E : in Item) is
           begin
              Table(N) := E;
           end Set_Component;
        end Shared_Array;

34  Examples of protected objects:

35      Control  : Resource;
        Flags    : array(1 .. 100) of Resource;


9.5 Intertask Communication


1   The primary means for intertask communication is provided by calls on
entries and protected subprograms. Calls on protected subprograms allow
coordinated access to shared data objects. Entry calls allow for blocking the
caller until a given condition is satisfied (namely, that the corresponding
entry is open - see 9.5.3), and then communicating data or control information
directly with another task or indirectly via a shared protected object.


                              Static Semantics

2   Any call on an entry or on a protected subprogram identifies a target
object for the operation, which is either a task (for an entry call) or a
protected object (for an entry call or a protected subprogram call). The
target object is considered an implicit parameter to the operation, and is
determined by the operation name (or prefix) used in the call on the
operation, as follows:

3     * If it is a direct_name or expanded name that denotes the declaration
        (or body) of the operation, then the target object is implicitly
        specified to be the current instance of the task or protected unit
        immediately enclosing the operation; such a call is defined to be an
        internal call;

4     * If it is a selected_component that is not an expanded name, then the
        target object is explicitly specified to be the task or protected
        object denoted by the prefix of the name; such a call is defined to be
        an external call;

5     * If the name or prefix is a dereference (implicit or explicit) of an
        access-to-protected-subprogram value, then the target object is
        determined by the prefix of the Access attribute_reference that
        produced the access value originally, and the call is defined to be an
        external call;

6     * If the name or prefix denotes a subprogram_renaming_declaration, then
        the target object is as determined by the name of the renamed entity.

7   A corresponding definition of target object applies to a
requeue_statement (see 9.5.4), with a corresponding distinction between an
internal requeue and an external requeue.


                               Legality Rules

7.1/2 The view of the target protected object associated with a call of a
protected procedure or entry shall be a variable.


                              Dynamic Semantics

8   Within the body of a protected operation, the current instance (see 8.6)
of the immediately enclosing protected unit is determined by the target object
specified (implicitly or explicitly) in the call (or requeue) on the protected
operation.

9   Any call on a protected procedure or entry of a target protected object is
defined to be an update to the object, as is a requeue on such an entry.


9.5.1 Protected Subprograms and Protected Actions


1   A protected subprogram is a subprogram declared immediately within a
protected_definition. Protected procedures provide exclusive read-write access
to the data of a protected object; protected functions provide concurrent
read-only access to the data.


                              Static Semantics

2   Within the body of a protected function (or a function declared
immediately within a protected_body), the current instance of the enclosing
protected unit is defined to be a constant (that is, its subcomponents may be
read but not updated). Within the body of a protected procedure (or a
procedure declared immediately within a protected_body), and within an
entry_body, the current instance is defined to be a variable (updating is
permitted).


                              Dynamic Semantics

3   For the execution of a call on a protected subprogram, the evaluation of
the name or prefix and of the parameter associations, and any assigning back
of in out or out parameters, proceeds as for a normal subprogram call (see
6.4). If the call is an internal call (see 9.5), the body of the subprogram is
executed as for a normal subprogram call. If the call is an external call,
then the body of the subprogram is executed as part of a new protected action
on the target protected object; the protected action completes after the body
of the subprogram is executed. A protected action can also be started by an
entry call (see 9.5.3).

4   A new protected action is not started on a protected object while another
protected action on the same protected object is underway, unless both actions
are the result of a call on a protected function. This rule is expressible in
terms of the execution resource associated with the protected object:

5     * Starting a protected action on a protected object corresponds to
        acquiring the execution resource associated with the protected object,
        either for concurrent read-only access if the protected action is for
        a call on a protected function, or for exclusive read-write access
        otherwise;

6     * Completing the protected action corresponds to releasing the
        associated execution resource.

7   After performing an operation on a protected object other than a call on a
protected function, but prior to completing the associated protected action,
the entry queues (if any) of the protected object are serviced (see 9.5.3).


                          Bounded (Run-Time) Errors

8   During a protected action, it is a bounded error to invoke an operation
that is potentially blocking. The following are defined to be potentially
blocking operations:

9     * a select_statement;

10    * an accept_statement;

11    * an entry_call_statement;

12    * a delay_statement;

13    * an abort_statement;

14    * task creation or activation;

15    * an external call on a protected subprogram (or an external requeue)
        with the same target object as that of the protected action;

16    * a call on a subprogram whose body contains a potentially blocking
        operation.

17  If the bounded error is detected, Program_Error is raised. If not
detected, the bounded error might result in deadlock or a (nested) protected
action on the same target object.

18  Certain language-defined subprograms are potentially blocking. In
particular, the subprograms of the language-defined input-output packages that
manipulate files (implicitly or explicitly) are potentially blocking. Other
potentially blocking subprograms are identified where they are defined. When
not specified as potentially blocking, a language-defined subprogram is
nonblocking.

        NOTES

19      18  If two tasks both try to start a protected action on a protected
        object, and at most one is calling a protected function, then only one
        of the tasks can proceed. Although the other task cannot proceed, it
        is not considered blocked, and it might be consuming processing
        resources while it awaits its turn. There is no language-defined
        ordering or queuing presumed for tasks competing to start a protected
        action - on a multiprocessor such tasks might use busy-waiting; for
        monoprocessor considerations, see D.3, "Priority Ceiling Locking".

20      19  The body of a protected unit may contain declarations and bodies
        for local subprograms. These are not visible outside the protected
        unit.

21      20  The body of a protected function can contain internal calls on
        other protected functions, but not protected procedures, because the
        current instance is a constant. On the other hand, the body of a
        protected procedure can contain internal calls on both protected
        functions and procedures.

22      21  From within a protected action, an internal call on a protected
        subprogram, or an external call on a protected subprogram with a
        different target object is not considered a potentially blocking
        operation.

22.1/2  22  The pragma Detect_Blocking may be used to ensure that all
        executions of potentially blocking operations during a protected
        action raise Program_Error. See H.5.


                                  Examples

23  Examples of protected subprogram calls (see 9.4):

24      Shared_Array.Set_Component(N, E);
        E := Shared_Array.Component(M);
        Control.Release;


9.5.2 Entries and Accept Statements


1   Entry_declarations, with the corresponding entry_bodies or
accept_statements, are used to define potentially queued operations on tasks
and protected objects.


                                   Syntax

2/2     entry_declaration ::= 
           [overriding_indicator]
           entry defining_identifier [(discrete_subtype_definition
        )] parameter_profile;

3       accept_statement ::= 
           accept entry_direct_name [(entry_index)] parameter_profile [do
             handled_sequence_of_statements
           end [entry_identifier]];

4       entry_index ::= expression

5       entry_body ::= 
          entry defining_identifier  entry_body_formal_part  entry_barrier is
            declarative_part
          begin
            handled_sequence_of_statements
          end [entry_identifier];

6       entry_body_formal_part ::= [(entry_index_specification
        )] parameter_profile

7       entry_barrier ::= when condition

8       entry_index_specification ::= for defining_identifier
         in discrete_subtype_definition

9       If an entry_identifier appears at the end of an accept_statement, it
        shall repeat the entry_direct_name. If an entry_identifier appears at
        the end of an entry_body, it shall repeat the defining_identifier.

10      An entry_declaration is allowed only in a protected or task
        declaration.

10.1/2  An overriding_indicator is not allowed in an entry_declaration that
        includes a discrete_subtype_definition.


                            Name Resolution Rules

11  In an accept_statement, the expected profile for the entry_direct_name is
that of the entry_declaration; the expected type for an entry_index is that of
the subtype defined by the discrete_subtype_definition of the corresponding
entry_declaration.

12  Within the handled_sequence_of_statements of an accept_statement, if a
selected_component has a prefix that denotes the corresponding
entry_declaration, then the entity denoted by the prefix is the accept_-
statement, and the selected_component is interpreted as an expanded name (see
4.1.3); the selector_name of the selected_component has to be the identifier
for some formal parameter of the accept_statement.


                               Legality Rules

13  An entry_declaration in a task declaration shall not contain a
specification for an access parameter (see 3.10).

13.1/2 If an entry_declaration has an overriding_indicator, then at the point
of the declaration:

13.2/2   * if the overriding_indicator is overriding, then the entry shall
        implement an inherited subprogram;

13.3/2   * if the overriding_indicator is not overriding, then the entry shall
        not implement any inherited subprogram.

13.4/2 In addition to the places where Legality Rules normally apply (see
12.3), these rules also apply in the private part of an instance of a generic
unit.

14  For an accept_statement, the innermost enclosing body shall be a
task_body, and the entry_direct_name shall denote an entry_declaration in the
corresponding task declaration; the profile of the accept_statement shall
conform fully to that of the corresponding entry_declaration. An accept_-
statement shall have a parenthesized entry_index if and only if the
corresponding entry_declaration has a discrete_subtype_definition.

15  An accept_statement shall not be within another accept_statement that
corresponds to the same entry_declaration, nor within an asynchronous_select
inner to the enclosing task_body.

16  An entry_declaration of a protected unit requires a completion, which
shall be an entry_body, and every entry_body shall be the completion of an
entry_declaration of a protected unit. The profile of the entry_body shall
conform fully to that of the corresponding declaration.

17  An entry_body_formal_part shall have an entry_index_specification if and
only if the corresponding entry_declaration has a discrete_subtype_definition.
In this case, the discrete_subtype_definitions of the entry_declaration and
the entry_index_specification shall fully conform to one another (see 6.3.1).

18  A name that denotes a formal parameter of an entry_body is not allowed
within the entry_barrier of the entry_body.


                              Static Semantics

19  The parameter modes defined for parameters in the parameter_profile of an
entry_declaration are the same as for a subprogram_declaration and have the
same meaning (see 6.2).

20  An entry_declaration with a discrete_subtype_definition (see 3.6) declares
a family of distinct entries having the same profile, with one such entry for
each value of the entry index subtype defined by the
discrete_subtype_definition. A name for an entry of a family takes the form of
an indexed_component, where the prefix denotes the entry_declaration for the
family, and the index value identifies the entry within the family. The term
single entry is used to refer to any entry other than an entry of an entry
family.

21  In the entry_body for an entry family, the entry_index_specification
declares a named constant whose subtype is the entry index subtype defined by
the corresponding entry_declaration; the value of the named entry index
identifies which entry of the family was called.


                              Dynamic Semantics

22/1 The elaboration of an entry_declaration for an entry family consists of
the elaboration of the discrete_subtype_definition, as described in 3.8. The
elaboration of an entry_declaration for a single entry has no effect.

23  The actions to be performed when an entry is called are specified by the
corresponding accept_statements (if any) for an entry of a task unit, and by
the corresponding entry_body for an entry of a protected unit.

24  For the execution of an accept_statement, the entry_index, if any, is
first evaluated and converted to the entry index subtype; this index value
identifies which entry of the family is to be accepted. Further execution of
the accept_statement is then blocked until a caller of the corresponding entry
is selected (see 9.5.3), whereupon the handled_sequence_of_statements, if any,
of the accept_statement is executed, with the formal parameters associated
with the corresponding actual parameters of the selected entry call. Upon
completion of the handled_sequence_of_statements, the accept_statement
completes and is left. When an exception is propagated from the
handled_sequence_of_statements of an accept_statement, the same exception is
also raised by the execution of the corresponding entry_call_statement.

25  The above interaction between a calling task and an accepting task is
called a rendezvous. After a rendezvous, the two tasks continue their
execution independently.

26  An entry_body is executed when the condition of the entry_barrier
evaluates to True and a caller of the corresponding single entry, or entry of
the corresponding entry family, has been selected (see 9.5.3). For the
execution of the entry_body, the declarative_part of the entry_body is
elaborated, and the handled_sequence_of_statements of the body is executed, as
for the execution of a subprogram_body. The value of the named entry index, if
any, is determined by the value of the entry index specified in the
entry_name of the selected entry call (or intermediate requeue_statement - see
9.5.4).

        NOTES

27      23  A task entry has corresponding accept_statements (zero or more),
        whereas a protected entry has a corresponding entry_body (exactly one).

28      24  A consequence of the rule regarding the allowed placements of
        accept_statements is that a task can execute accept_statements only
        for its own entries.

29/2    25  A return statement (see 6.5) or a requeue_statement (see 9.5.4)
        may be used to complete the execution of an accept_statement or an
        entry_body.

30      26  The condition in the entry_barrier may reference anything visible
        except the formal parameters of the entry. This includes the entry
        index (if any), the components (including discriminants) of the
        protected object, the Count attribute of an entry of that protected
        object, and data global to the protected unit.

31      The restriction against referencing the formal parameters within an
        entry_barrier ensures that all calls of the same entry see the same
        barrier value. If it is necessary to look at the parameters of an
        entry call before deciding whether to handle it, the entry_barrier can
        be "when True" and the caller can be requeued (on some private entry)
        when its parameters indicate that it cannot be handled immediately.


                                  Examples

32  Examples of entry declarations:

33      entry Read(V : out Item);
        entry Seize;
        entry Request(Level)(D : Item);  --  a family of entries

34  Examples of accept statements:

35      accept Shut_Down;

36      accept Read(V : out Item) do
           V := Local_Item;
        end Read;

37      accept Request(Low)(D : Item) do
           ...
        end Request;


9.5.3 Entry Calls


1   An entry_call_statement (an entry call) can appear in various contexts. A
simple entry call is a stand-alone statement that represents an unconditional
call on an entry of a target task or a protected object. Entry calls can also
appear as part of select_statements (see 9.7).


                                   Syntax

2       entry_call_statement ::= entry_name [actual_parameter_part];


                            Name Resolution Rules

3   The entry_name given in an entry_call_statement shall resolve to denote an
entry. The rules for parameter associations are the same as for subprogram
calls (see 6.4 and 6.4.1).


                              Static Semantics

4   The entry_name of an entry_call_statement specifies (explicitly or
implicitly) the target object of the call, the entry or entry family, and the
entry index, if any (see 9.5).


                              Dynamic Semantics

5   Under certain circumstances (detailed below), an entry of a task or
protected object is checked to see whether it is open or closed:

6     * An entry of a task is open if the task is blocked on an
        accept_statement that corresponds to the entry (see 9.5.2), or on a
        selective_accept (see 9.7.1) with an open accept_alternative that
        corresponds to the entry; otherwise it is closed.

7     * An entry of a protected object is open if the condition of the
        entry_barrier of the corresponding entry_body evaluates to True;
        otherwise it is closed. If the evaluation of the condition propagates
        an exception, the exception Program_Error is propagated to all current
        callers of all entries of the protected object.

8   For the execution of an entry_call_statement, evaluation of the name and
of the parameter associations is as for a subprogram call (see 6.4). The entry
call is then issued: For a call on an entry of a protected object, a new
protected action is started on the object (see 9.5.1). The named entry is
checked to see if it is open; if open, the entry call is said to be selected
immediately, and the execution of the call proceeds as follows:

9     * For a call on an open entry of a task, the accepting task becomes
        ready and continues the execution of the corresponding
        accept_statement (see 9.5.2).

10    * For a call on an open entry of a protected object, the corresponding
        entry_body is executed (see 9.5.2) as part of the protected action.

11  If the accept_statement or entry_body completes other than by a requeue
(see 9.5.4), return is made to the caller (after servicing the entry queues -
see below); any necessary assigning back of formal to actual parameters
occurs, as for a subprogram call (see 6.4.1); such assignments take place
outside of any protected action.

12  If the named entry is closed, the entry call is added to an entry queue
(as part of the protected action, for a call on a protected entry), and the
call remains queued until it is selected or cancelled; there is a separate
(logical) entry queue for each entry of a given task or protected object
(including each entry of an entry family).

13  When a queued call is selected, it is removed from its entry queue.
Selecting a queued call from a particular entry queue is called servicing the
entry queue. An entry with queued calls can be serviced under the following
circumstances:

14    * When the associated task reaches a corresponding accept_statement, or
        a selective_accept with a corresponding open accept_alternative;

15    * If after performing, as part of a protected action on the associated
        protected object, an operation on the object other than a call on a
        protected function, the entry is checked and found to be open.

16  If there is at least one call on a queue corresponding to an open entry,
then one such call is selected according to the entry queuing policy in effect
(see below), and the corresponding accept_statement or entry_body is executed
as above for an entry call that is selected immediately.

17  The entry queuing policy controls selection among queued calls both for
task and protected entry queues. The default entry queuing policy is to select
calls on a given entry queue in order of arrival. If calls from two or more
queues are simultaneously eligible for selection, the default entry queuing
policy does not specify which queue is serviced first. Other entry queuing
policies can be specified by pragmas (see D.4).

18  For a protected object, the above servicing of entry queues continues
until there are no open entries with queued calls, at which point the
protected action completes.

19  For an entry call that is added to a queue, and that is not the
triggering_statement of an asynchronous_select (see 9.7.4), the calling task
is blocked until the call is cancelled, or the call is selected and a
corresponding accept_statement or entry_body completes without requeuing. In
addition, the calling task is blocked during a rendezvous.

20  An attempt can be made to cancel an entry call upon an abort (see 9.8) and
as part of certain forms of select_statement (see 9.7.2, 9.7.3, and 9.7.4).
The cancellation does not take place until a point (if any) when the call is
on some entry queue, and not protected from cancellation as part of a requeue
(see 9.5.4); at such a point, the call is removed from the entry queue and the
call completes due to the cancellation. The cancellation of a call on an entry
of a protected object is a protected action, and as such cannot take place
while any other protected action is occurring on the protected object. Like
any protected action, it includes servicing of the entry queues (in case some
entry barrier depends on a Count attribute).

21  A call on an entry of a task that has already completed its execution
raises the exception Tasking_Error at the point of the call; similarly, this
exception is raised at the point of the call if the called task completes its
execution or becomes abnormal before accepting the call or completing the
rendezvous (see 9.8). This applies equally to a simple entry call and to an
entry call as part of a select_statement.


                         Implementation Permissions

22  An implementation may perform the sequence of steps of a protected action
using any thread of control; it need not be that of the task that started the
protected action. If an entry_body completes without requeuing, then the
corresponding calling task may be made ready without waiting for the entire
protected action to complete.

23  When the entry of a protected object is checked to see whether it is open,
the implementation need not reevaluate the condition of the corresponding
entry_barrier if no variable or attribute referenced by the condition
(directly or indirectly) has been altered by the execution (or cancellation)
of a protected procedure or entry call on the object since the condition was
last evaluated.

24  An implementation may evaluate the conditions of all entry_barriers of a
given protected object any time any entry of the object is checked to see if
it is open.

25  When an attempt is made to cancel an entry call, the implementation need
not make the attempt using the thread of control of the task (or interrupt)
that initiated the cancellation; in particular, it may use the thread of
control of the caller itself to attempt the cancellation, even if this might
allow the entry call to be selected in the interim.

        NOTES

26      27  If an exception is raised during the execution of an entry_body,
        it is propagated to the corresponding caller (see 11.4).

27      28  For a call on a protected entry, the entry is checked to see if it
        is open prior to queuing the call, and again thereafter if its Count
        attribute (see 9.9) is referenced in some entry barrier.

28      29  In addition to simple entry calls, the language permits timed,
        conditional, and asynchronous entry calls (see 9.7.2, 9.7.3, and see
        9.7.4).

29      30  The condition of an entry_barrier is allowed to be evaluated by an
        implementation more often than strictly necessary, even if the
        evaluation might have side effects. On the other hand, an
        implementation need not reevaluate the condition if nothing it
        references was updated by an intervening protected action on the
        protected object, even if the condition references some global
        variable that might have been updated by an action performed from
        outside of a protected action.


                                  Examples

30  Examples of entry calls:

31      Agent.Shut_Down;                      --  see 9.1
        Parser.Next_Lexeme(E);                --  see 9.1
        Pool(5).Read(Next_Char);              --  see 9.1
        Controller.Request(Low)(Some_Item);   --  see 9.1
        Flags(3).Seize;                       --  see 9.4


9.5.4 Requeue Statements


1   A requeue_statement can be used to complete an accept_statement or
entry_body, while redirecting the corresponding entry call to a new (or the
same) entry queue. Such a requeue can be performed with or without allowing an
intermediate cancellation of the call, due to an abort or the expiration of a
delay.


                                   Syntax

2       requeue_statement ::= requeue entry_name [with abort];


                            Name Resolution Rules

3   The entry_name of a requeue_statement shall resolve to denote an entry
(the target entry) that either has no parameters, or that has a profile that
is type conformant (see 6.3.1) with the profile of the innermost enclosing
entry_body or accept_statement.


                               Legality Rules

4   A requeue_statement shall be within a callable construct that is either an
entry_body or an accept_statement, and this construct shall be the innermost
enclosing body or callable construct.

5   If the target entry has parameters, then its profile shall be subtype
conformant with the profile of the innermost enclosing callable construct.

6   In a requeue_statement of an accept_statement of some task unit, either
the target object shall be a part of a formal parameter of the
accept_statement, or the accessibility level of the target object shall not be
equal to or statically deeper than any enclosing accept_statement of the task
unit. In a requeue_statement of an entry_body of some protected unit, either
the target object shall be a part of a formal parameter of the entry_body, or
the accessibility level of the target object shall not be statically deeper
than that of the entry_declaration.


                              Dynamic Semantics

7   The execution of a requeue_statement proceeds by first evaluating the
entry_name, including the prefix identifying the target task or protected
object and the expression identifying the entry within an entry family, if
any. The entry_body or accept_statement enclosing the requeue_statement is
then completed, finalized, and left (see 7.6.1).

8   For the execution of a requeue on an entry of a target task, after leaving
the enclosing callable construct, the named entry is checked to see if it is
open and the requeued call is either selected immediately or queued, as for a
normal entry call (see 9.5.3).

9   For the execution of a requeue on an entry of a target protected object,
after leaving the enclosing callable construct:

10    * if the requeue is an internal requeue (that is, the requeue is back on
        an entry of the same protected object - see 9.5), the call is added to
        the queue of the named entry and the ongoing protected action
        continues (see 9.5.1);

11    * if the requeue is an external requeue (that is, the target protected
        object is not implicitly the same as the current object - see 9.5), a
        protected action is started on the target object and proceeds as for a
        normal entry call (see 9.5.3).

12  If the new entry named in the requeue_statement has formal parameters,
then during the execution of the accept_statement or entry_body corresponding
to the new entry, the formal parameters denote the same objects as did the
corresponding formal parameters of the callable construct completed by the
requeue. In any case, no parameters are specified in a requeue_statement; any
parameter passing is implicit.

13  If the requeue_statement includes the reserved words with abort (it is a
requeue-with-abort), then:

14    * if the original entry call has been aborted (see 9.8), then the
        requeue acts as an abort completion point for the call, and the call
        is cancelled and no requeue is performed;

15    * if the original entry call was timed (or conditional), then the
        original expiration time is the expiration time for the requeued call.

16  If the reserved words with abort do not appear, then the call remains
protected against cancellation while queued as the result of the
requeue_statement.

        NOTES

17      31  A requeue is permitted from a single entry to an entry of an entry
        family, or vice-versa. The entry index, if any, plays no part in the
        subtype conformance check between the profiles of the two entries; an
        entry index is part of the entry_name for an entry of a family.


                                  Examples

18  Examples of requeue statements:

19      requeue Request(Medium) with abort;
                            -- requeue on a member of an entry family of the current task, see 9.1

20      requeue Flags(I).Seize;
                            -- requeue on an entry of an array component, see 9.4


9.6 Delay Statements, Duration, and Time


1   A delay_statement is used to block further execution until a specified
expiration time is reached. The expiration time can be specified either as a
particular point in time (in a delay_until_statement), or in seconds from the
current time (in a delay_relative_statement). The language-defined package
Calendar provides definitions for a type Time and associated operations,
including a function Clock that returns the current time.


                                   Syntax

2       delay_statement ::= delay_until_statement
         | delay_relative_statement

3       delay_until_statement ::= delay until delay_expression;

4       delay_relative_statement ::= delay delay_expression;


                            Name Resolution Rules

5   The expected type for the delay_expression in a delay_relative_statement
is the predefined type Duration. The delay_expression in a
delay_until_statement is expected to be of any nonlimited type.


                               Legality Rules

6   There can be multiple time bases, each with a corresponding clock, and a
corresponding time type. The type of the delay_expression in a
delay_until_statement shall be a time type - either the type Time defined in
the language-defined package Calendar (see below), or some other
implementation-defined time type (see D.8).


                              Static Semantics

7   There is a predefined fixed point type named Duration, declared in the
visible part of package Standard; a value of type Duration is used to
represent the length of an interval of time, expressed in seconds. The type
Duration is not specific to a particular time base, but can be used with any
time base.

8   A value of the type Time in package Calendar, or of some other
implementation-defined time type, represents a time as reported by a
corresponding clock.

9   The following language-defined library package exists:

10      
        package Ada.Calendar is
          type Time is private;

11/2      subtype Year_Number  is Integer range 1901 .. 2399;
          subtype Month_Number is Integer range 1 .. 12;
          subtype Day_Number   is Integer range 1 .. 31;
          subtype Day_Duration is Duration range 0.0 .. 86_400.0;

12        function Clock return Time;

13        function Year   (Date : Time) return Year_Number;
          function Month  (Date : Time) return Month_Number;
          function Day    (Date : Time) return Day_Number;
          function Seconds(Date : Time) return Day_Duration;

14        procedure Split (Date  : in Time;
                           Year    : out Year_Number;
                           Month   : out Month_Number;
                           Day     : out Day_Number;
                           Seconds : out Day_Duration);

15        function Time_Of(Year  : Year_Number;
                           Month   : Month_Number;
                           Day     : Day_Number;
                           Seconds : Day_Duration := 0.0)
           return Time;

16        function "+" (Left : Time;   Right : Duration) return Time;
          function "+" (Left : Duration; Right : Time) return Time;
          function "-" (Left : Time;   Right : Duration) return Time;
          function "-" (Left : Time;   Right : Time) return Duration;

17        function "<" (Left, Right : Time) return Boolean;
          function "<="(Left, Right : Time) return Boolean;
          function ">" (Left, Right : Time) return Boolean;
          function ">="(Left, Right : Time) return Boolean;

18        Time_Error : exception;

19      private
           ... -- not specified by the language
        end Ada.Calendar;


                              Dynamic Semantics

20  For the execution of a delay_statement, the delay_expression is first
evaluated. For a delay_until_statement, the expiration time for the delay is
the value of the delay_expression, in the time base associated with the type
of the expression. For a delay_relative_statement, the expiration time is
defined as the current time, in the time base associated with relative delays,
plus the value of the delay_expression converted to the type Duration, and
then rounded up to the next clock tick. The time base associated with relative
delays is as defined in D.9, "Delay Accuracy" or is implementation defined.

21  The task executing a delay_statement is blocked until the expiration time
is reached, at which point it becomes ready again. If the expiration time has
already passed, the task is not blocked.

22  If an attempt is made to cancel the delay_statement (as part of an
asynchronous_select or abort - see 9.7.4 and 9.8), the _statement is cancelled
if the expiration time has not yet passed, thereby completing the
delay_statement.

23  The time base associated with the type Time of package Calendar is
implementation defined. The function Clock of package Calendar returns a value
representing the current time for this time base. The implementation-defined
value of the named number System.Tick (see 13.7) is an approximation of the
length of the real-time interval during which the value of Calendar.Clock
remains constant.

24/2 The functions Year, Month, Day, and Seconds return the corresponding
values for a given value of the type Time, as appropriate to an
implementation-defined time zone; the procedure Split returns all four
corresponding values. Conversely, the function Time_Of combines a year number,
a month number, a day number, and a duration, into a value of type Time. The
operators "+" and "-" for addition and subtraction of times and durations, and
the relational operators for times, have the conventional meaning.

25  If Time_Of is called with a seconds value of 86_400.0, the value returned
is equal to the value of Time_Of for the next day with a seconds value of 0.0.
The value returned by the function Seconds or through the Seconds parameter of
the procedure Split is always less than 86_400.0.

26/1 The exception Time_Error is raised by the function Time_Of if the actual
parameters do not form a proper date. This exception is also raised by the
operators "+" and "-" if the result is not representable in the type Time or
Duration, as appropriate. This exception is also raised by the functions Year,
Month, Day, and Seconds and the procedure Split if the year number of the
given date is outside of the range of the subtype Year_Number.


                         Implementation Requirements

27  The implementation of the type Duration shall allow representation of time
intervals (both positive and negative) up to at least 86400 seconds (one day);
Duration'Small shall not be greater than twenty milliseconds. The
implementation of the type Time shall allow representation of all dates with
year numbers in the range of Year_Number; it may allow representation of other
dates as well (both earlier and later).


                         Implementation Permissions

28  An implementation may define additional time types (see D.8).

29  An implementation may raise Time_Error if the value of a
delay_expression in a delay_until_statement of a select_statement represents a time
more than 90 days past the current time. The actual limit, if any, is
implementation-defined.


                            Implementation Advice

30  Whenever possible in an implementation, the value of Duration'Small should
be no greater than 100 microseconds.

31  The time base for delay_relative_statements should be monotonic; it need
not be the same time base as used for Calendar.Clock.

        NOTES

32      32  A delay_relative_statement with a negative value of the
        delay_expression is equivalent to one with a zero value.

33      33  A delay_statement may be executed by the environment task;
        consequently delay_statements may be executed as part of the
        elaboration of a library_item or the execution of the main subprogram.
        Such statements delay the environment task (see 10.2).

34      34  A delay_statement is an abort completion point and a potentially
        blocking operation, even if the task is not actually blocked.

35      35  There is no necessary relationship between System.Tick (the
        resolution of the clock of package Calendar) and Duration'Small (the
        small of type Duration).

36      36  Additional requirements associated with delay_statements are given
        in D.9, "Delay Accuracy".


                                  Examples

37  Example of a relative delay statement:

38      delay 3.0;  -- delay 3.0 seconds

39  Example of a periodic task:

40      declare
           use Ada.Calendar;
           Next_Time : Time := Clock + Period;
                              -- Period is a global constant of type Duration
        begin
           loop               -- repeated every Period seconds
              delay until Next_Time;
              ... -- perform some actions
              Next_Time := Next_Time + Period;
           end loop;
        end;


9.6.1 Formatting, Time Zones, and other operations for Time



                              Static Semantics

1/2 The following language-defined library packages exist:

2/2     package Ada.Calendar.Time_Zones is

3/2        -- Time zone manipulation:

4/2        type Time_Offset is range -28*60 .. 28*60;

5/2        Unknown_Zone_Error : exception;

6/2        function UTC_Time_Offset (Date : Time := Clock) return Time_Offset;

7/2     end Ada.Calendar.Time_Zones;

8/2     
        package Ada.Calendar.Arithmetic is

9/2        -- Arithmetic on days:

10/2       type Day_Count is range
             -366*(1+Year_Number'Last - Year_Number'First)
             ..
             366*(1+Year_Number'Last - Year_Number'First);

11/2       subtype Leap_Seconds_Count is Integer range -2047 .. 2047;

12/2       procedure Difference (Left, Right : in Time;
                                 Days : out Day_Count;
                                 Seconds : out Duration;
                                 Leap_Seconds : out Leap_Seconds_Count);

13/2       function "+" (Left : Time; Right : Day_Count) return Time;
           function "+" (Left : Day_Count; Right : Time) return Time;
           function "-" (Left : Time; Right : Day_Count) return Time;
           function "-" (Left, Right : Time) return Day_Count;

14/2    end Ada.Calendar.Arithmetic;

15/2    
        with Ada.Calendar.Time_Zones;
        package Ada.Calendar.Formatting is

16/2       -- Day of the week:

17/2       type Day_Name is (Monday, Tuesday, Wednesday, Thursday,
               Friday, Saturday, Sunday);

18/2       function Day_of_Week (Date : Time) return Day_Name;

19/2       -- Hours:Minutes:Seconds access:

20/2       subtype Hour_Number         is Natural range 0 .. 23;
           subtype Minute_Number       is Natural range 0 .. 59;
           subtype Second_Number       is Natural range 0 .. 59;
           subtype Second_Duration     is Day_Duration range 0.0 .. 1.0;

21/2       function Year       (Date : Time;
                                Time_Zone  : Time_Zones.Time_Offset := 0)
                                   return Year_Number;

22/2       function Month      (Date : Time;
                                Time_Zone  : Time_Zones.Time_Offset := 0)
                                   return Month_Number;

23/2       function Day        (Date : Time;
                                Time_Zone  : Time_Zones.Time_Offset := 0)
                                   return Day_Number;

24/2       function Hour       (Date : Time;
                                Time_Zone  : Time_Zones.Time_Offset := 0)
                                   return Hour_Number;

25/2       function Minute     (Date : Time;
                                Time_Zone  : Time_Zones.Time_Offset := 0)
                                   return Minute_Number;

26/2       function Second     (Date : Time)
                                   return Second_Number;

27/2       function Sub_Second (Date : Time)
                                   return Second_Duration;

28/2       function Seconds_Of (Hour   :  Hour_Number;
                                Minute : Minute_Number;
                                Second : Second_Number := 0;
                                Sub_Second : Second_Duration := 0.0)
               return Day_Duration;

29/2       procedure Split (Seconds    : in Day_Duration;
                            Hour       : out Hour_Number;
                            Minute     : out Minute_Number;
                            Second     : out Second_Number;
                            Sub_Second : out Second_Duration);

30/2       function Time_Of (Year       : Year_Number;
                             Month      : Month_Number;
                             Day        : Day_Number;
                             Hour       : Hour_Number;
                             Minute     : Minute_Number;
                             Second     : Second_Number;
                             Sub_Second : Second_Duration := 0.0;
                             Leap_Second: Boolean := False;
                             Time_Zone  : Time_Zones.Time_Offset := 0)
                                     return Time;

31/2       function Time_Of (Year       : Year_Number;
                             Month      : Month_Number;
                             Day        : Day_Number;
                             Seconds    : Day_Duration := 0.0;
                             Leap_Second: Boolean := False;
                             Time_Zone  : Time_Zones.Time_Offset := 0)
                                     return Time;

32/2       procedure Split (Date       : in Time;
                            Year       : out Year_Number;
                            Month      : out Month_Number;
                            Day        : out Day_Number;
                            Hour       : out Hour_Number;
                            Minute     : out Minute_Number;
                            Second     : out Second_Number;
                            Sub_Second : out Second_Duration;
                            Time_Zone  : in Time_Zones.Time_Offset := 0);

33/2       procedure Split (Date       : in Time;
                            Year       : out Year_Number;
                            Month      : out Month_Number;
                            Day        : out Day_Number;
                            Hour       : out Hour_Number;
                            Minute     : out Minute_Number;
                            Second     : out Second_Number;
                            Sub_Second : out Second_Duration;
                            Leap_Second: out Boolean;
                            Time_Zone  : in Time_Zones.Time_Offset := 0);

34/2       procedure Split (Date       : in Time;
                            Year       : out Year_Number;
                            Month      : out Month_Number;
                            Day        : out Day_Number;
                            Seconds    : out Day_Duration;
                            Leap_Second: out Boolean;
                            Time_Zone  : in Time_Zones.Time_Offset := 0);

35/2       -- Simple image and value:
           function Image (Date : Time;
                           Include_Time_Fraction : Boolean := False;
                           Time_Zone  : Time_Zones.Time_Offset := 0) return String;

36/2       function Value (Date : String;
                           Time_Zone  : Time_Zones.Time_Offset := 0) return Time;

37/2       function Image (Elapsed_Time : Duration;
                           Include_Time_Fraction : Boolean := False) return String;

38/2       function Value (Elapsed_Time : String) return Duration;

39/2    end Ada.Calendar.Formatting;

40/2 Type Time_Offset represents the number of minutes difference between the
implementation-defined time zone used by Calendar and another time zone.

41/2    function UTC_Time_Offset (Date : Time := Clock) return Time_Offset;

42/2        Returns, as a number of minutes, the difference between the
            implementation-defined time zone of Calendar, and UTC time, at the
            time Date. If the time zone of the Calendar implementation is
            unknown, then Unknown_Zone_Error is raised.

43/2    procedure Difference (Left, Right : in Time;
                              Days : out Day_Count;
                              Seconds : out Duration;
                              Leap_Seconds : out Leap_Seconds_Count);

44/2        Returns the difference between Left and Right. Days is the number
            of days of difference, Seconds is the remainder seconds of
            difference excluding leap seconds, and Leap_Seconds is the number
            of leap seconds. If Left < Right, then Seconds <= 0.0, Days <= 0,
            and Leap_Seconds <= 0. Otherwise, all values are nonnegative. The
            absolute value of Seconds is always less than 86_400.0. For the
            returned values, if Days = 0, then Seconds +
            Duration(Leap_Seconds) = Calendar."-" (Left, Right).

45/2    function "+" (Left : Time; Right : Day_Count) return Time;
        function "+" (Left : Day_Count; Right : Time) return Time;

46/2        Adds a number of days to a time value. Time_Error is raised if the
            result is not representable as a value of type Time.

47/2    function "-" (Left : Time; Right : Day_Count) return Time;

48/2        Subtracts a number of days from a time value. Time_Error is raised
            if the result is not representable as a value of type Time.

49/2    function "-" (Left, Right : Time) return Day_Count;

50/2        Subtracts two time values, and returns the number of days between
            them. This is the same value that Difference would return in Days.

51/2    function Day_of_Week (Date : Time) return Day_Name;

52/2        Returns the day of the week for Time. This is based on the Year,
            Month, and Day values of Time.

53/2    function Year       (Date : Time;
                             Time_Zone  : Time_Zones.Time_Offset := 0)
                                return Year_Number;

54/2        Returns the year for Date, as appropriate for the specified time
            zone offset.

55/2    function Month      (Date : Time;
                             Time_Zone  : Time_Zones.Time_Offset := 0)
                                return Month_Number;

56/2        Returns the month for Date, as appropriate for the specified time
            zone offset.

57/2    function Day        (Date : Time;
                             Time_Zone  : Time_Zones.Time_Offset := 0)
                                return Day_Number;

58/2        Returns the day number for Date, as appropriate for the specified
            time zone offset.

59/2    function Hour       (Date : Time;
                             Time_Zone  : Time_Zones.Time_Offset := 0)
                                return Hour_Number;

60/2        Returns the hour for Date, as appropriate for the specified time
            zone offset.

61/2    function Minute     (Date : Time;
                             Time_Zone  : Time_Zones.Time_Offset := 0)
                                return Minute_Number;

62/2        Returns the minute within the hour for Date, as appropriate for
            the specified time zone offset.

63/2    function Second     (Date : Time)
                                return Second_Number;

64/2        Returns the second within the hour and minute for Date.

65/2    function Sub_Second (Date : Time)
                                return Second_Duration;

66/2        Returns the fraction of second for Date (this has the same
            accuracy as Day_Duration). The value returned is always less than
            1.0.

67/2    function Seconds_Of (Hour   : Hour_Number;
                             Minute : Minute_Number;
                             Second : Second_Number := 0;
                             Sub_Second : Second_Duration := 0.0)
            return Day_Duration;

68/2        Returns a Day_Duration value for the combination of the given
            Hour, Minute, Second, and Sub_Second. This value can be used in
            Calendar.Time_Of as well as the argument to Calendar."+" and
            Calendar."-". If Seconds_Of is called with a Sub_Second value of
            1.0, the value returned is equal to the value of Seconds_Of for
            the next second with a Sub_Second value of 0.0.

69/2    procedure Split (Seconds    : in Day_Duration;
                         Hour       : out Hour_Number;
                         Minute     : out Minute_Number;
                         Second     : out Second_Number;
                         Sub_Second : out Second_Duration);

70/2        Splits Seconds into Hour, Minute, Second and Sub_Second in such a
            way that the resulting values all belong to their respective
            subtypes. The value returned in the Sub_Second parameter is always
            less than 1.0.

71/2    function Time_Of (Year       : Year_Number;
                          Month      : Month_Number;
                          Day        : Day_Number;
                          Hour       : Hour_Number;
                          Minute     : Minute_Number;
                          Second     : Second_Number;
                          Sub_Second : Second_Duration := 0.0;
                          Leap_Second: Boolean := False;
                          Time_Zone  : Time_Zones.Time_Offset := 0)
                                  return Time;

72/2        If Leap_Second is False, returns a Time built from the date and
            time values, relative to the specified time zone offset. If
            Leap_Second is True, returns the Time that represents the time
            within the leap second that is one second later than the time
            specified by the other parameters. Time_Error is raised if the
            parameters do not form a proper date or time. If Time_Of is called
            with a Sub_Second value of 1.0, the value returned is equal to the
            value of Time_Of for the next second with a Sub_Second value of
            0.0.

73/2    function Time_Of (Year       : Year_Number;
                          Month      : Month_Number;
                          Day        : Day_Number;
                          Seconds    : Day_Duration := 0.0;
                          Leap_Second: Boolean := False;
                          Time_Zone  : Time_Zones.Time_Offset := 0)
                                  return Time;

74/2        If Leap_Second is False, returns a Time built from the date and
            time values, relative to the specified time zone offset. If
            Leap_Second is True, returns the Time that represents the time
            within the leap second that is one second later than the time
            specified by the other parameters. Time_Error is raised if the
            parameters do not form a proper date or time. If Time_Of is called
            with a Seconds value of 86_400.0, the value returned is equal to
            the value of Time_Of for the next day with a Seconds value of 0.0.

75/2    procedure Split (Date       : in Time;
                         Year       : out Year_Number;
                         Month      : out Month_Number;
                         Day        : out Day_Number;
                         Hour       : out Hour_Number;
                         Minute     : out Minute_Number;
                         Second     : out Second_Number;
                         Sub_Second : out Second_Duration;
                         Leap_Second: out Boolean;
                         Time_Zone  : in Time_Zones.Time_Offset := 0);

76/2        If Date does not represent a time within a leap second, splits
            Date into its constituent parts (Year, Month, Day, Hour, Minute,
            Second, Sub_Second), relative to the specified time zone offset,
            and sets Leap_Second to False. If Date represents a time within a
            leap second, set the constituent parts to values corresponding to
            a time one second earlier than that given by Date, relative to the
            specified time zone offset, and sets Leap_Seconds to True. The
            value returned in the Sub_Second parameter is always less than 1.0.

77/2    procedure Split (Date       : in Time;
                         Year       : out Year_Number;
                         Month      : out Month_Number;
                         Day        : out Day_Number;
                         Hour       : out Hour_Number;
                         Minute     : out Minute_Number;
                         Second     : out Second_Number;
                         Sub_Second : out Second_Duration;
                         Time_Zone  : in Time_Zones.Time_Offset := 0);

78/2        Splits Date into its constituent parts (Year, Month, Day, Hour,
            Minute, Second, Sub_Second), relative to the specified time zone
            offset. The value returned in the Sub_Second parameter is always
            less than 1.0.

79/2    procedure Split (Date       : in Time;
                         Year       : out Year_Number;
                         Month      : out Month_Number;
                         Day        : out Day_Number;
                         Seconds    : out Day_Duration;
                         Leap_Second: out Boolean;
                         Time_Zone  : in Time_Zones.Time_Offset := 0);

80/2        If Date does not represent a time within a leap second, splits
            Date into its constituent parts (Year, Month, Day, Seconds),
            relative to the specified time zone offset, and sets Leap_Second
            to False. If Date represents a time within a leap second, set the
            constituent parts to values corresponding to a time one second
            earlier than that given by Date, relative to the specified time
            zone offset, and sets Leap_Seconds to True. The value returned in
            the Seconds parameter is always less than 86_400.0.

81/2    function Image (Date : Time;
                        Include_Time_Fraction : Boolean := False;
                        Time_Zone  : Time_Zones.Time_Offset := 0) return String;

82/2        Returns a string form of the Date relative to the given Time_Zone.
            The format is "Year-Month-Day Hour:Minute:Second", where the Year
            is a 4-digit value, and all others are 2-digit values, of the
            functions defined in Calendar and Calendar.Formatting, including a
            leading zero, if needed. The separators between the values are a
            minus, another minus, a colon, and a single space between the Day
            and Hour. If Include_Time_Fraction is True, the integer part of
            Sub_Seconds*100 is suffixed to the string as a point followed by a
            2-digit value.

83/2    function Value (Date : String;
                        Time_Zone  : Time_Zones.Time_Offset := 0) return Time;

84/2        Returns a Time value for the image given as Date, relative to the
            given time zone. Constraint_Error is raised if the string is not
            formatted as described for Image, or the function cannot interpret
            the given string as a Time value.

85/2    function Image (Elapsed_Time : Duration;
                        Include_Time_Fraction : Boolean := False) return String;

86/2        Returns a string form of the Elapsed_Time. The format is
            "Hour:Minute:Second", where all values are 2-digit values,
            including a leading zero, if needed. The separators between the
            values are colons. If Include_Time_Fraction is True, the integer
            part of Sub_Seconds*100 is suffixed to the string as a point
            followed by a 2-digit value. If Elapsed_Time < 0.0, the result is
            Image (abs Elapsed_Time, Include_Time_Fraction) prefixed with a
            minus sign. If abs Elapsed_Time represents 100 hours or more, the
            result is implementation-defined.

87/2    function Value (Elapsed_Time : String) return Duration;

88/2        Returns a Duration value for the image given as Elapsed_Time.
            Constraint_Error is raised if the string is not formatted as
            described for Image, or the function cannot interpret the given
            string as a Duration value.


                            Implementation Advice

89/2 An implementation should support leap seconds if the target system
supports them. If leap seconds are not supported, Difference should return
zero for Leap_Seconds, Split should return False for Leap_Second, and Time_Of
should raise Time_Error if Leap_Second is True.

        NOTES

90/2    37  The implementation-defined time zone of package Calendar may, but
        need not, be the local time zone. UTC_Time_Offset always returns the
        difference relative to the implementation-defined time zone of package
        Calendar. If UTC_Time_Offset does not raise Unknown_Zone_Error, UTC
        time can be safely calculated (within the accuracy of the underlying
        time-base).

91/2    38  Calling Split on the results of subtracting
        Duration(UTC_Time_Offset*60) from Clock provides the components
        (hours, minutes, and so on) of the UTC time. In the United States, for
        example, UTC_Time_Offset will generally be negative.


9.7 Select Statements


1   There are four forms of the select_statement. One form provides a
selective wait for one or more select_alternatives. Two provide timed and
conditional entry calls. The fourth provides asynchronous transfer of control.


                                   Syntax

2       select_statement ::= 
           selective_accept
          | timed_entry_call
          | conditional_entry_call
          | asynchronous_select


                                  Examples

3   Example of a select statement:

4       select
           accept Driver_Awake_Signal;
        or
           delay 30.0*Seconds;
           Stop_The_Train;
        end select;


9.7.1 Selective Accept


1   This form of the select_statement allows a combination of waiting for, and
selecting from, one or more alternatives. The selection may depend on
conditions associated with each alternative of the selective_accept.


                                   Syntax

2       selective_accept ::= 
          select
           [guard]
             select_alternative
        { or
           [guard]
             select_alternative }
        [ else
           sequence_of_statements ]
          end select;

3       guard ::= when condition =>

4       select_alternative ::= 
           accept_alternative
          | delay_alternative
          | terminate_alternative

5       accept_alternative ::= 
          accept_statement [sequence_of_statements]

6       delay_alternative ::= 
          delay_statement [sequence_of_statements]

7       terminate_alternative ::= terminate;

8       A selective_accept shall contain at least one accept_alternative. In
        addition, it can contain:

9         * a terminate_alternative (only one); or

10        * one or more delay_alternatives; or

11        * an else part (the reserved word else followed by a
            sequence_of_statements).

12      These three possibilities are mutually exclusive.


                               Legality Rules

13  If a selective_accept contains more than one delay_alternative, then all
shall be delay_relative_statements, or all shall be delay_until_statements for
the same time type.


                              Dynamic Semantics

14  A select_alternative is said to be open if it is not immediately preceded
by a guard, or if the condition of its guard evaluates to True. It is said to
be closed otherwise.

15  For the execution of a selective_accept, any guard conditions are
evaluated; open alternatives are thus determined. For an open
delay_alternative, the delay_expression is also evaluated. Similarly, for an
open accept_alternative for an entry of a family, the entry_index is also
evaluated. These evaluations are performed in an arbitrary order, except that
a delay_expression or entry_index is not evaluated until after evaluating the
corresponding condition, if any. Selection and execution of one open
alternative, or of the else part, then completes the execution of the
selective_accept; the rules for this selection are described below.

16  Open accept_alternatives are first considered. Selection of one such
alternative takes place immediately if the corresponding entry already has
queued calls. If several alternatives can thus be selected, one of them is
selected according to the entry queuing policy in effect (see 9.5.3 and D.4).
When such an alternative is selected, the selected call is removed from its
entry queue and the handled_sequence_of_statements (if any) of the
corresponding accept_statement is executed; after the rendezvous completes any
subsequent sequence_of_statements of the alternative is executed. If no
selection is immediately possible (in the above sense) and there is no else
part, the task blocks until an open alternative can be selected.

17  Selection of the other forms of alternative or of an else part is
performed as follows:

18    * An open delay_alternative is selected when its expiration time is
        reached if no accept_alternative or other delay_alternative can be
        selected prior to the expiration time. If several delay_alternatives
        have this same expiration time, one of them is selected according to
        the queuing policy in effect (see D.4); the default queuing policy
        chooses arbitrarily among the delay_alternatives whose expiration time
        has passed.

19    * The else part is selected and its sequence_of_statements is executed
        if no accept_alternative can immediately be selected; in particular,
        if all alternatives are closed.

20    * An open terminate_alternative is selected if the conditions stated at
        the end of clause 9.3 are satisfied.

21  The exception Program_Error is raised if all alternatives are closed and
there is no else part.

        NOTES

22      39  A selective_accept is allowed to have several open
        delay_alternatives. A selective_accept is allowed to have several open
        accept_alternatives for the same entry.


                                  Examples

23  Example of a task body with a selective accept:

24      task body Server is
           Current_Work_Item : Work_Item;
        begin
           loop
              select
                 accept Next_Work_Item(WI : in Work_Item) do
                    Current_Work_Item := WI;
                  end;
                  Process_Work_Item(Current_Work_Item);
              or
                 accept Shut_Down;
                 exit;       -- Premature shut down requested
              or
                 terminate;  -- Normal shutdown at end of scope
              end select;
           end loop;
        end Server;


9.7.2 Timed Entry Calls


1/2 A timed_entry_call issues an entry call that is cancelled if the call (or
a requeue-with-abort of the call) is not selected before the expiration time
is reached. A procedure call may appear rather than an entry call for cases
where the procedure might be implemented by an entry.


                                   Syntax

2       timed_entry_call ::= 
          select
           entry_call_alternative
          or
           delay_alternative
          end select;

3/2     entry_call_alternative ::= 
          procedure_or_entry_call [sequence_of_statements]

3.1/2   procedure_or_entry_call ::= 
          procedure_call_statement | entry_call_statement


                               Legality Rules

3.2/2 If a procedure_call_statement is used for a procedure_or_entry_call, the
procedure_name or procedure_prefix of the procedure_call_statement shall
statically denote an entry renamed as a procedure or (a view of) a primitive
subprogram of a limited interface whose first parameter is a controlling
parameter (see 3.9.2).


                              Static Semantics

3.3/2 If a procedure_call_statement is used for a procedure_or_entry_call, and
the procedure is implemented by an entry, then the procedure_name, or
procedure_prefix and possibly the first parameter of the
procedure_call_statement, determine the target object of the call and the
entry to be called.


                              Dynamic Semantics

4/2 For the execution of a timed_entry_call, the entry_name, procedure_name,
or procedure_prefix, and any actual parameters are evaluated, as for a simple
entry call (see 9.5.3) or procedure call (see 6.4). The expiration time (see
9.6) for the call is determined by evaluating the delay_expression of the
delay_alternative. If the call is an entry call or a call on a procedure
implemented by an entry, the entry call is then issued. Otherwise, the call
proceeds as described in 6.4 for a procedure call, followed by the sequence_of_-
statements of the entry_call_alternative; the sequence_of_statements of the
delay_alternative is ignored.

5   If the call is queued (including due to a requeue-with-abort), and not
selected before the expiration time is reached, an attempt to cancel the call
is made. If the call completes due to the cancellation, the optional
sequence_of_statements of the delay_alternative is executed; if the entry call
completes normally, the optional sequence_of_statements of the entry_call_-
alternative is executed.


                                  Examples

6   Example of a timed entry call:

7       select
           Controller.Request(Medium)(Some_Item);
        or
           delay 45.0;
           --  controller too busy, try something else
        end select;


9.7.3 Conditional Entry Calls


1/2 A conditional_entry_call issues an entry call that is then cancelled if it
is not selected immediately (or if a requeue-with-abort of the call is not
selected immediately). A procedure call may appear rather than an entry call
for cases where the procedure might be implemented by an entry.


                                   Syntax

2       conditional_entry_call ::= 
          select
           entry_call_alternative
          else
           sequence_of_statements
          end select;


                              Dynamic Semantics

3   The execution of a conditional_entry_call is defined to be equivalent to
the execution of a timed_entry_call with a delay_alternative specifying an
immediate expiration time and the same sequence_of_statements as given after
the reserved word else.

        NOTES

4       40  A conditional_entry_call may briefly increase the Count attribute
        of the entry, even if the conditional call is not selected.


                                  Examples

5   Example of a conditional entry call:

6       procedure Spin(R : in Resource) is
        begin
           loop
              select
                 R.Seize;
                 return;
              else
                 null;  --  busy waiting
              end select;
           end loop;
        end;




9.7.4 Asynchronous Transfer of Control


1   An asynchronous select_statement provides asynchronous transfer of control
upon completion of an entry call or the expiration of a delay.


                                   Syntax

2       asynchronous_select ::= 
          select
           triggering_alternative
          then abort
           abortable_part
          end select;

3       triggering_alternative ::= triggering_statement
         [sequence_of_statements]

4/2     triggering_statement ::= procedure_or_entry_call | delay_statement

5       abortable_part ::= sequence_of_statements


                              Dynamic Semantics

6/2 For the execution of an asynchronous_select whose triggering_statement is
a procedure_or_entry_call, the entry_name, procedure_name, or
procedure_prefix, and actual parameters are evaluated as for a simple entry call (see
9.5.3) or procedure call (see 6.4). If the call is an entry call or a call on
a procedure implemented by an entry, the entry call is issued. If the entry
call is queued (or requeued-with-abort), then the abortable_part is executed.
If the entry call is selected immediately, and never requeued-with-abort, then
the abortable_part is never started. If the call is on a procedure that is not
implemented by an entry, the call proceeds as described in 6.4, followed by
the sequence_of_statements of the triggering_alternative; the abortable_part
is never started.

7   For the execution of an asynchronous_select whose triggering_statement is
a delay_statement, the delay_expression is evaluated and the expiration time
is determined, as for a normal delay_statement. If the expiration time has not
already passed, the abortable_part is executed.

8   If the abortable_part completes and is left prior to completion of the
triggering_statement, an attempt to cancel the triggering_statement is made.
If the attempt to cancel succeeds (see 9.5.3 and 9.6), the
asynchronous_select is complete.

9   If the triggering_statement completes other than due to cancellation, the
abortable_part is aborted (if started but not yet completed - see 9.8). If the
triggering_statement completes normally, the optional sequence_of_statements
of the triggering_alternative is executed after the abortable_part is left.


                                  Examples

10  Example of a main command loop for a command interpreter:

11      loop
            select
                Terminal.Wait_For_Interrupt;
                Put_Line("Interrupted");
            then abort
                -- This will be abandoned upon terminal interrupt
                Put_Line("-> ");
                Get_Line(Command, Last);
                Process_Command(Command(1..Last));
            end select;
        end loop;

12  Example of a time-limited calculation:

13      select
           delay 5.0;
           Put_Line("Calculation does not converge");
        then abort
           -- This calculation should finish in 5.0 seconds;
           --  if not, it is assumed to diverge.
           Horribly_Complicated_Recursive_Function(X, Y);
        end select;


9.8 Abort of a Task - Abort of a Sequence of Statements


1   An abort_statement causes one or more tasks to become abnormal, thus
preventing any further interaction with such tasks. The completion of the
triggering_statement of an asynchronous_select causes a sequence_of_statements
to be aborted.


                                   Syntax

2       abort_statement ::= abort task_name {, task_name};


                            Name Resolution Rules

3   Each task_name is expected to be of any task type; they need not all be of
the same task type.


                              Dynamic Semantics

4   For the execution of an abort_statement, the given task_names are
evaluated in an arbitrary order. Each named task is then aborted, which
consists of making the task abnormal and aborting the execution of the
corresponding task_body, unless it is already completed.

5   When the execution of a construct is aborted (including that of a task_-
body or of a sequence_of_statements), the execution of every construct
included within the aborted execution is also aborted, except for executions
included within the execution of an abort-deferred operation; the execution of
an abort-deferred operation continues to completion without being affected by
the abort; the following are the abort-deferred operations:

6     * a protected action;

7     * waiting for an entry call to complete (after having initiated the
        attempt to cancel it - see below);

8     * waiting for the termination of dependent tasks;

9     * the execution of an Initialize procedure as the last step of the
        default initialization of a controlled object;

10    * the execution of a Finalize procedure as part of the finalization of a
        controlled object;

11    * an assignment operation to an object with a controlled part.

12  The last three of these are discussed further in 7.6.

13  When a master is aborted, all tasks that depend on that master are aborted.

14  The order in which tasks become abnormal as the result of an
abort_statement or the abort of a sequence_of_statements is not specified by
the language.

15  If the execution of an entry call is aborted, an immediate attempt is made
to cancel the entry call (see 9.5.3). If the execution of a construct is
aborted at a time when the execution is blocked, other than for an entry call,
at a point that is outside the execution of an abort-deferred operation, then
the execution of the construct completes immediately. For an abort due to an
abort_statement, these immediate effects occur before the execution of the
abort_statement completes. Other than for these immediate cases, the execution
of a construct that is aborted does not necessarily complete before the
abort_statement completes. However, the execution of the aborted construct
completes no later than its next abort completion point (if any) that occurs
outside of an abort-deferred operation; the following are abort completion
points for an execution:

16    * the point where the execution initiates the activation of another task;

17    * the end of the activation of a task;

18    * the start or end of the execution of an entry call, accept_statement,
        delay_statement, or abort_statement;

19    * the start of the execution of a select_statement, or of the
        sequence_of_statements of an exception_handler.


                          Bounded (Run-Time) Errors

20  An attempt to execute an asynchronous_select as part of the execution of
an abort-deferred operation is a bounded error. Similarly, an attempt to
create a task that depends on a master that is included entirely within the
execution of an abort-deferred operation is a bounded error. In both cases,
Program_Error is raised if the error is detected by the implementation;
otherwise the operations proceed as they would outside an abort-deferred
operation, except that an abort of the abortable_part or the created task
might or might not have an effect.


                             Erroneous Execution

21  If an assignment operation completes prematurely due to an abort, the
assignment is said to be disrupted; the target of the assignment or its parts
can become abnormal, and certain subsequent uses of the object can be
erroneous, as explained in 13.9.1.

        NOTES

22      41  An abort_statement should be used only in situations requiring
        unconditional termination.

23      42  A task is allowed to abort any task it can name, including itself.

24      43  Additional requirements associated with abort are given in D.6
        , "Preemptive Abort".


9.9 Task and Entry Attributes



                              Dynamic Semantics

1   For a prefix T that is of a task type (after any implicit dereference),
the following attributes are defined:

2   T'Callable  Yields the value True when the task denoted by T is callable,
                and False otherwise; a task is callable unless it is completed
                or abnormal. The value of this attribute is of the predefined
                type Boolean.

3   T'Terminated
                Yields the value True if the task denoted by T is terminated,
                and False otherwise. The value of this attribute is of the
                predefined type Boolean.

4   For a prefix E that denotes an entry of a task or protected unit, the
following attribute is defined. This attribute is only allowed within the body
of the task or protected unit, but excluding, in the case of an entry of a
task unit, within any program unit that is, itself, inner to the body of the
task unit.

5   E'Count     Yields the number of calls presently queued on the entry E of
                the current instance of the unit. The value of this attribute
                is of the type universal_integer.

        NOTES

6       44  For the Count attribute, the entry can be either a single entry or
        an entry of a family. The name of the entry or entry family can be
        either a direct_name or an expanded name.

7       45  Within task units, algorithms interrogating the attribute E'Count
        should take precautions to allow for the increase of the value of this
        attribute for incoming entry calls, and its decrease, for example with
        timed_entry_calls. Also, a conditional_entry_call may briefly increase
        this value, even if the conditional call is not accepted.

8       46  Within protected units, algorithms interrogating the attribute
        E'Count in the entry_barrier for the entry E should take precautions
        to allow for the evaluation of the condition of the barrier both
        before and after queuing a given caller.


9.10 Shared Variables



                              Static Semantics

1   If two different objects, including nonoverlapping parts of the same
object, are independently addressable, they can be manipulated concurrently by
two different tasks without synchronization. Normally, any two nonoverlapping
objects are independently addressable. However, if packing, record layout, or
Component_Size is specified for a given composite object, then it is
implementation defined whether or not two nonoverlapping parts of that
composite object are independently addressable.


                              Dynamic Semantics

2   Separate tasks normally proceed independently and concurrently with one
another. However, task interactions can be used to synchronize the actions of
two or more tasks to allow, for example, meaningful communication by the
direct updating and reading of variables shared between the tasks. The actions
of two different tasks are synchronized in this sense when an action of one
task signals an action of the other task; an action A1 is defined to signal an
action A2 under the following circumstances:

3     * If A1 and A2 are part of the execution of the same task, and the
        language rules require A1 to be performed before A2;

4     * If A1 is the action of an activator that initiates the activation of a
        task, and A2 is part of the execution of the task that is activated;

5     * If A1 is part of the activation of a task, and A2 is the action of
        waiting for completion of the activation;

6     * If A1 is part of the execution of a task, and A2 is the action of
        waiting for the termination of the task;

6.1/1   * If A1 is the termination of a task T, and A2 is either the
        evaluation of the expression T'Terminated or a call to
        Ada.Task_Identification.Is_Terminated with an actual parameter that
        identifies T (see C.7.1);

7     * If A1 is the action of issuing an entry call, and A2 is part of the
        corresponding execution of the appropriate entry_body or
        accept_statement.

8     * If A1 is part of the execution of an accept_statement or entry_body,
        and A2 is the action of returning from the corresponding entry call;

9     * If A1 is part of the execution of a protected procedure body or
        entry_body for a given protected object, and A2 is part of a later
        execution of an entry_body for the same protected object;

10    * If A1 signals some action that in turn signals A2.


                             Erroneous Execution

11  Given an action of assigning to an object, and an action of reading or
updating a part of the same object (or of a neighboring object if the two are
not independently addressable), then the execution of the actions is erroneous
unless the actions are sequential. Two actions are sequential if one of the
following is true:

12    * One action signals the other;

13    * Both actions occur as part of the execution of the same task;

14    * Both actions occur as part of protected actions on the same protected
        object, and at most one of the actions is part of a call on a
        protected function of the protected object.

15  A pragma Atomic or Atomic_Components may also be used to ensure that
certain reads and updates are sequential - see C.6.


9.11 Example of Tasking and Synchronization



                                  Examples

1   The following example defines a buffer protected object to smooth
variations between the speed of output of a producing task and the speed of
input of some consuming task. For instance, the producing task might have the
following structure:

2       task Producer;

3/2     task body Producer is
           Person : Person_Name; -- see 3.10.1
        begin
           loop
              ... --  simulate arrival of the next customer
              Buffer.Append_Wait(Person);
              exit when Person = null;
           end loop;
        end Producer;

4   and the consuming task might have the following structure:

5       task Consumer;

6/2     task body Consumer is
           Person : Person_Name;
        begin
           loop
              Buffer.Remove_First_Wait(Person);
              exit when Person = null;
              ... --  simulate serving a customer
           end loop;
        end Consumer;

7/2 The buffer object contains an internal array of person names managed in a
round-robin fashion. The array has two indices, an In_Index denoting the index
for the next input person name and an Out_Index denoting the index for the
next output person name.

7.1/2 The Buffer is defined as an extension of the Synchronized_Queue
interface (see 3.9.4), and as such promises to implement the abstraction
defined by that interface. By doing so, the Buffer can be passed to the
Transfer class-wide operation defined for objects of a type covered by
Queue'Class.

8/2     protected Buffer is new Synchronized_Queue with  -- see 3.9.4
           entry Append_Wait(Person : in Person_Name);
           entry Remove_First_Wait(Person : out Person_Name);
           function Cur_Count return Natural;
           function Max_Count return Natural;
           procedure Append(Person : in Person_Name);
           procedure Remove_First(Person : out Person_Name);
        private
           Pool      : Person_Name_Array(1 .. 100);
           Count     : Natural := 0;
           In_Index, Out_Index : Positive := 1;
        end Buffer;

9/2     protected body Buffer is
           entry Append_Wait(Person : in Person_Name)
              when Count < Pool'Length is
           begin
              Append(Person);
           end Append_Wait;

9.1/2      procedure Append(Person : in Person_Name) is
           begin
              if Count = Pool'Length then
                 raise Queue_Error with "Buffer Full";  -- see 11.3
              end if;
              Pool(In_Index) := Person;
              In_Index       := (In_Index mod Pool'Length) + 1;
              Count          := Count + 1;
           end Append;

10/2       entry Remove_First_Wait(Person : out Person_Name)
              when Count > 0 is
           begin
              Remove_First(Person);
           end Remove_First_Wait;

11/2       procedure Remove_First(Person : out Person_Name) is
           begin
              if Count = 0 then
                 raise Queue_Error with "Buffer Empty"; -- see 11.3
              end if;
              Person    := Pool(Out_Index);
              Out_Index := (Out_Index mod Pool'Length) + 1;
              Count     := Count - 1;
           end Remove_First;

12/2       function Cur_Count return Natural is
           begin
               return Buffer.Count;
           end Cur_Count;

13/2       function Max_Count return Natural is
           begin
               return Pool'Length;
           end Max_Count;
        end Buffer;

Generated by dwww version 1.15 on Sat May 18 05:51:13 CEST 2024.