9.5 Intertask Communication
This Reference Manual output has not been verified, and may contain omissions or errors. Report any problems on the tracking issue
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/3When a name
or prefix
denotes an entry, protected subprogram, or a prefixed view of a primitive subprogram of a limited interface whose first parameter is a controlling parameter, the name
or prefix
determines a target object, as follows:
- 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; a call using such a name is defined to be an internal call; 4/3 - If it is a
selected_component
that is not an expanded name, then the target object is explicitly specified to be the object denoted by theprefix
of thename
; a call using such a name is defined to be an external call;
protected type Pt is
procedure Op1;
procedure Op2;
end Pt;
4.cPO : Pt;
Other_Object : Some_Other_Protected_Type;
4.dprotected body Pt is
procedure Op1 is begin ... end Op1;
4.eprocedure Op2 is
begin
Op1; -- An internal call.
Pt.Op1; -- Another internal call.
PO.Op1; -- An external call. It the current instance is PO, then
-- this is a bounded error (see 9.5.1).
Other_Object.Some_Op; -- An external call.
end Op2;
end Pt;
- If the
name
orprefix
is a dereference (implicit or explicit) of an access-to-protected-subprogram value, then the target object is determined by theprefix
of the Accessattribute_reference
that produced the access value originally; a call using such a name is defined to be an external call; 6 - If the
name
orprefix
denotes asubprogram_renaming_declaration
, then the target object is as determined by thename
of the renamed entity.
A call on an entry or a protected subprogram either uses a name
or prefix
that determines a target object implicitly, as above, or is a call on (a non-prefixed view of) a primitive subprogram of a limited interface whose first parameter is a controlling parameter, in which case the target object is identified explicitly by the first parameter. This latter case is an external call.
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/3If a name
or prefix
determines a target object, and the name denotes a protected entry or procedure, then the target object shall be a variable, unless the prefix
is for an attribute_reference
to the Count attribute (see 9.9).
name
whose target object is a constant view of a protected object, directly, or via an access value, renames, or generic formal subprogram. It is, however, legal to say P'Count in a protected function body, even though the protected object is a constant view there. An internal call on a protected function shall not occur within a precondition expression (see 6.1.1) of a protected operation nor within a default_expression
of a parameter_specification
of a protected operation.
Dynamic Semantics
8Within 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.
protected_body
. 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.
Syntax
10/3synchronization_kind
::=
By_Entry | By_Protected_Procedure | Optional
Static Semantics
11/3For the declaration of a primitive procedure of a synchronized tagged type the following language-defined representation aspect may be specified with an aspect_specification
(see 13.1.1):
Synchronization
- If specified, the aspect definition shall be a
synchronization_kind
.
Inherited subprograms inherit the Synchronization aspect, if any, from the corresponding subprogram of the parent or progenitor type. If an overriding operation does not have a directly specified Synchronization aspect then the Synchronization aspect of the inherited operation is inherited by the overriding operation.
Legality Rules
14/3The synchronization_kind
By_Protected_Procedure shall not be applied to a primitive procedure of a task interface.
A procedure for which the specified synchronization_kind
is By_Entry shall be implemented by an entry. A procedure for which the specified synchronization_kind
is By_Protected_Procedure shall be implemented by a protected procedure. A procedure for which the specified synchronization_kind
is Optional may be implemented by an entry or by a procedure (including a protected procedure).
If a primitive procedure overrides an inherited operation for which the Synchronization aspect has been specified to be By_Entry or By_Protected_Procedure, then any specification of the aspect Synchronization applied to the overriding operation shall have the same synchronization_kind
.
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.
Static Semantics
18/5For a program unit, task entry, formal package, formal subprogram, formal object of an anonymous access-to-subprogram type, enumeration literal, and for a subtype (including a formal subtype), the following language-defined operational aspect is defined:
Nonblocking
- This aspect specifies the blocking restriction for the entity; it shall be specified by a static Boolean expression. [The
aspect_definition
can be omitted from the specification of this aspect; in that case, the aspect for the entity is True.]
expression
for any aspect with type Boolean; we take advantage of that here. - The Nonblocking aspect may be specified for all entities for which it is defined, except for protected operations and task entries. In particular, Nonblocking may be specified for generic formal parameters.
- } When aspect Nonblocking is False for an entity, the entity can contain a potentially blocking operation; such an entity allows blocking. If the aspect is True for an entity, the entity is said to be nonblocking.
- For a generic instantiation and entities declared within such an instance, the aspect is determined by the Nonblocking aspect for the corresponding entity of the generic unit, anded with the Nonblocking aspects of the actual generic parameters used by the entity. If the aspect is directly specified for an instance, the specified expression shall have the same value as the Nonblocking aspect of the instance (after anding with the aspects of the used actual parameters). In the absence of a Use_Formal aspect, all actual generic parameters are presumed to be used by an entity (see H.7.1).
- For a (protected or task) entry, the Nonblocking aspect is False.
- For an enumeration literal, the Nonblocking aspect is True.
- For a predefined operator of an elementary type, the Nonblocking aspect is True. For a predefined operator of a composite type, the Nonblocking aspect of the operator is the same as the Nonblocking aspect for the type.
- For a dereference of an access-to-subprogram type, the Nonblocking aspect of the designated subprogram is that of the access-to-subprogram type.
- For the base subtype of a scalar (sub)type, the Nonblocking aspect is True.
- For an inherited primitive dispatching subprogram that is null or abstract, the subprogram is nonblocking if and only if a corresponding subprogram of at least one ancestor is nonblocking. For any other inherited subprogram, it is nonblocking if and only if the corresponding subprogram of the parent is nonblocking.
- Unless directly specified, overridings of dispatching operations inherit this aspect.
- Unless directly specified, for a formal subtype, formal package, or formal subprogram, the Nonblocking aspect is that of the actual subtype, package, or subprogram.
- Unless directly specified, for a non-first subtype S, the Nonblocking aspect is that of the subtype identified in the subtype_indication defining S; unless directly specified for the first subtype of a derived type, the Nonblocking aspect is that of the ancestor subtype.
- Unless directly specified, for any other program unit, first subtype, or formal object, the Nonblocking aspect of the entity is determined by the Nonblocking aspect for the innermost program unit enclosing the entity.
- If not specified for a library unit, the Nonblocking aspect is True if the library unit is declared pure, or False otherwise.
The following are defined to be potentially blocking operations:
- a
select_statement
; 36/5 - an
accept_statement
; 37/5 - an
entry_call_statement
, or a call on a procedure that renames or is implemented by an entry; 38/5 - a
delay_statement
; 39/5 - an
abort_statement
; 40/5 - task creation or activation;
- during a protected action, an external call on a protected subprogram (or an external requeue) with the same target object as that of the protected action.
If a language-defined subprogram allows blocking, then a call on the subprogram is a potentially blocking operation.
Legality Rules
43/5A portion of program text is called a nonblocking region if it is anywhere within a parallel construct, or if the innermost enclosing program unit is nonblocking. A nonblocking region shall not contain any of the following:
- a
select_statement
; 45/5 - an
accept_statement
; 46/5 - a
delay_statement
; 47/5 - an
abort_statement
; 48/5 - task creation or activation.
Furthermore, a parallel construct shall neither contain a call on a callable entity for which the Nonblocking aspect is False, nor shall it contain a call on a callable entity declared within a generic unit that uses a generic formal parameter with Nonblocking aspect False (see Use_Formal aspect in H.7.1).
Finally, a nonblocking region that is outside of a parallel construct shall not contain a call on a callable entity for which the Nonblocking aspect is False, unless the region is within a generic unit and the callable entity is associated with a generic formal parameter of the generic unit, or the call is within the aspect_definition
of an assertion aspect for an entity that allows blocking.
For the purposes of the above rules, an entry_body
is considered nonblocking if the immediately enclosing protected unit is nonblocking.
For a subtype for which aspect Nonblocking is True, any predicate expression that applies to the subtype shall only contain constructs that are allowed immediately within a nonblocking program unit.
A subprogram shall be nonblocking if it overrides a nonblocking dispatching operation. An entry shall not implement a nonblocking procedure. If an inherited dispatching subprogram allows blocking, then the corresponding subprogram of each ancestor shall allow blocking.
It is illegal to directly specify aspect Nonblocking for the first subtype of the full view of a type that has a partial view. If the Nonblocking aspect of the full view is inherited, it shall have the same value as that of the partial view, or have the value True.
Aspect Nonblocking shall be directly specified for the first subtype of a derived type only if it has the same value as the Nonblocking aspect of the ancestor subtype or if it is specified True. Aspect Nonblocking shall be directly specified for a nonfirst subtype S only if it has the same value as the Nonblocking aspect of the subtype identified in the subtype_indication
defining S or if it is specified True.
For an access-to-object type that is nonblocking, the Allocate, Deallocate, and Storage_Size operations on its storage pool shall be nonblocking.
For a composite type that is nonblocking:
- All component subtypes shall be nonblocking;
- For a record type or extension, every call in the
default_expression
of a component (including discriminants) shall call an operation that is nonblocking; 60/5 - For a controlled type, the Initialize, Finalize, and Adjust (if any) subprograms shall be nonblocking.
The predefined equality operator for a composite type, unless it is for a record type or record extension and the operator is overridden by a primitive equality operator, is illegal if it is nonblocking and:
- for a record type or record extension, the parent primitive "=" allows blocking; or
- some component is of a type T, and:
- T is a record type or record extension that has a primitive "=" that allows blocking; or
- T is neither a record type nor a record extension, and T has a predefined "=" that allows blocking.
In a generic instantiation:
- the actual subprogram corresponding to a nonblocking formal subprogram shall be nonblocking [(an actual that is an entry is not permitted in this case)];
- the actual subtype corresponding to a nonblocking formal subtype shall be nonblocking;
- the actual object corresponding to a formal object of a nonblocking access-to-subprogram type shall be of a nonblocking access-to-subprogram type;
- the actual instance corresponding to a nonblocking formal package shall be nonblocking.
In addition to the places where Legality Rules normally apply (see 12.3), the above rules also apply in the private part of an instance of a generic unit.
synchronization_kind
By_Protected_Procedure implies that the operation will not block. Wording Changes from Ada 95
Extensions to Ada 2005
Wording Changes from Ada 2005
Inconsistencies With Ada 2012
Incompatibilities With Ada 2012
Extensions to Ada 2012
9.5.1 Protected Subprograms and Protected Actions
1A 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.
protected_body
is not a protected subprogram; it is an intrinsic subprogram. See 6.3.1, “Conformance Rules”. 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).]
For a type declared by a protected_type_declaration
or for the anonymous type of an object declared by a single_protected_declaration
, the following language-defined type-related representation aspect may be specified:
Exclusive_Functions
- The type of aspect Exclusive_Functions is Boolean. If not specified (including by inheritance), the aspect is False.
- A value of True for this aspect indicates that protected functions behave in the same way as protected procedures with respect to mutual exclusion and queue servicing (see below).
A protected procedure or entry is an exclusive protected operation. A protected function of a protected type P is an exclusive protected operation if the Exclusive_Functions aspect of P is True.
Dynamic Semantics
3For 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).]
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 nonexclusive protected function. This rule is expressible in terms of the execution resource associated with the protected object:
- Starting a protected action on a protected object corresponds to acquiring the execution resource associated with the protected object, either for exclusive read-write access if the protected action is for a call on an exclusive protected operation, or for concurrent read-only access otherwise;
- Completing the protected action corresponds to releasing the associated execution resource.
[After performing an exclusive protected operation on a protected object, but prior to completing the associated protected action, the entry queues (if any) of the protected object are serviced (see 9.5.3).]
If a parallel construct occurs within a protected action, no new logical threads of control are created. Instead, each element of the parallel construct that would have become a separate logical thread of control executes on the logical thread of control that is performing the protected action. If there are multiple such elements initiated at the same point, they execute in an arbitrary order.
Bounded (Run-Time) Errors
8/5 During a protected action, it is a bounded error to invoke an operation that is potentially blocking (see 9.5).
Paragraphs 9 through 16 were moved to 9.5.
If the bounded error is detected, Program_Error is raised. If not detected, the bounded error can result in deadlock or a (nested) protected action on the same target object.
During a protected action, a call on a subprogram whose body contains a potentially blocking operation is a bounded error. If the bounded error is detected, Program_Error is raised; otherwise, the call proceeds normally.
pragma
Detect_Blocking can be used to ensure that any remaining executions of potentially blocking operations during a protected action raise Program_Error. See H.5. Examples
24Examples of protected subprogram calls (see 9.4):
Shared_Array.Set_Component(N, E);
E := Shared_Array.Component(M);
Control.Release;
Wording Changes from Ada 95
pragma
Detect_Blocking. This pragma can be used to ensure portable (somewhat pessimistic) behavior of protected actions by converting the Bounded Error into a required check. Extensions to Ada 2012
Wording Changes from Ada 2012
9.5.2 Entries and Accept Statements
1Entry_declaration
s, with the corresponding entry_bodies
or accept_statement
s, are used to define potentially queued operations on tasks and protected objects.
Syntax
2/3entry_declaration
::=
[overriding_indicator
]
entry defining_identifier
[(discrete_subtype_definition
)] parameter_profile
[aspect_specification
];
3accept_statement
::=
accept entry_direct_name
[(entry_index
)] parameter_profile
[do
handled_sequence_of_statements
end [entry_identifier
]];
defining_identifier
for accept_statement
s. Although an accept_statement
is sort of like a body, it can appear nested within a block_statement
, and therefore be hidden from its own entry by an outer homograph. entry_index
::=
expression
5/5entry_body
::=
entry defining_identifier
entry_body_formal_part
[aspect_specification
]
entry_barrier
is
declarative_part
begin
handled_sequence_of_statements
end [entry_identifier
];
overriding_indicator
on an entry_body
because entries always implement procedures at the point of the type declaration; there is no late implementation. And we don't want to have to think about overriding_indicator
s on accept_statement
s. entry_body_formal_part
::=
[(entry_index_specification
)] parameter_profile
7entry_barrier
::=
when condition
8/5entry_index_specification
::=
for defining_identifier
in discrete_subtype_definition
[aspect_specification
]
9If 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
.
[An entry_declaration
is allowed only in a protected or task declaration.]
An overriding_indicator
is not allowed in an entry_declaration
that includes a discrete_subtype_definition
.
Name Resolution Rules
11In 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
.
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
].
accept_statement
are those for its formal parameters. Legality Rules
13An entry_declaration
in a task declaration shall not contain a specification for an access parameter (see 3.10).
task T is
entry E(Z : access Integer); -- Illegal!
end T;
13.ctask body T is
begin
declare
type A is access all Integer;
X : A;
Int : aliased Integer;
task Inner;
task body Inner is
begin
T.E(Int'Access);
end Inner;
begin
accept E(Z : access Integer) do
X := A(Z); -- Accessibility_Check
end E;
end;
end T;
accept_statement
for E is difficult, since one does not know whether the entry caller is calling from inside the immediately enclosing declare block or from outside it. This means that the lexical nesting level associated with the designated object is not sufficient to determine whether the Accessibility_Check should pass or fail.entry_bodies
are always nested immediately within the protected_body
; they cannot be further nested as can accept_statement
s, nor can they be called from within the protected_body
(since no entry calls are permitted inside a protected_body
). If an entry_declaration
has an overriding_indicator
, then at the point of the declaration:
- 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.
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.
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
.
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
.
Accept_statement
s are required to be immediately within the enclosing task_body
(as opposed to being in a nested subprogram) to ensure that a nested task does not attempt to accept the entry of its enclosing task. We considered relaxing this restriction, either by making the check a run-time check, or by allowing a nested task to accept an entry of its enclosing task. However, neither change seemed to provide sufficient benefit to justify the additional implementation burden.accept_statement
s for the same entry (or entry family) are prohibited to ensure that there is no ambiguity in the resolution of an expanded name for a formal parameter of the entry. This could be relaxed by allowing the inner one to hide the outer one from all visibility, but again the small added benefit didn't seem to justify making the change for Ada 95.Accept_statement
s are not permitted within asynchronous_select
statements to simplify the semantics and implementation: an accept_statement
in an abortable_part
could result in Tasking_Error being propagated from an entry call even though the target task was still callable; implementations that use multiple tasks implicitly to implement an asynchronous_select
might have trouble supporting "up-level" accepts. Furthermore, if accept_statement
s were permitted in the abortable_part
, a task could call its own entry and then accept it in the abortable_part
, leading to rather unusual and possibly difficult-to-specify semantics. 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.
entry_declaration
, unlike a subprogram_declaration
, cannot be completed with a renaming_declaration
. entry_body
is allowed. entry_bodies
. Task entries have corresponding accept_statement
s instead of having entry_bodies
, and we do not consider an accept_statement
to be a “completion,” because a task entry_declaration
is allowed to have zero, one, or more than one corresponding accept_statement
s. 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_definition
s of the entry_declaration
and the entry_index_specification
shall fully conform to one another (see 6.3.1).
A name that denotes a formal parameter of an entry_body
is not allowed within the entry_barrier
of the entry_body
.
Static Semantics
19The 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).
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.
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.
discrete_subtype_definition
of the entry_index_specification
is not elaborated; the subtype of the named constant declared is defined by the discrete_subtype_definition
of the corresponding entry_declaration
, which is elaborated, either when the type is declared, or when the object is created, if its constraint is per-object. Dynamic Semantics
22/1{8652/0002} 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.
[The actions to be performed when an entry is called are specified by the corresponding accept_statement
s (if any) for an entry of a task unit, and by the corresponding entry_body
for an entry of a protected unit.]
The interaction between a task that calls an entry and an accepting task is called a rendezvous.
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. Execution of the rendezvous consists of the execution of the handled_sequence_of_statements
, performance of any postcondition or type invariant checks associated with the entry, and any initialization or finalization associated with these checks, as described in 6.1.1 and 7.3.2. After execution of the rendezvous, the accept_statement
completes and is left. [The two tasks then proceed independently.] 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
.
accept_statement
. In other words, for a rendezvous, the raising splits in two, and continues concurrently in both tasks.handled_sequence_of_statements
of an accept_statement
”, not “propagated from an accept_statement
”. The latter would be wrong — we don't want exceptions propagated by the entry_index
to be sent to the caller (there is none yet!).This paragraph was deleted.
[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).
procedure_call_statement
using the name declared by the renaming, the entry index (if any) comes from the entry name
specified in the subprogram_renaming_declaration
. accept_statement
s is that a task can execute accept_statement
s only for its own entries.requeue_statement
(see 9.5.4) can be used to complete the execution of an accept_statement
or an entry_body
. accept_statement
need not have a handled_sequence_of_statements
even if the corresponding entry has parameters. Equally, it can have a handled_sequence_of_statements
even if the corresponding entry has no parameters. defining_identifier
. Overloading is not allowed for entry family names. A single entry or an entry of an entry family can be renamed as a procedure as explained in 8.5.4. condition
in the entry_barrier
can 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.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
32Examples of entry declarations:
entry Read(V : out Item);
entry Seize;
entry Request(Level)(D : Item); -- a family of entries
Examples of accept statements:
accept Shut_Down;
36accept Read(V : out Item) do
V := Local_Item;
end Read;
37accept Request(Low)(D : Item) do
...
end Request;
Extensions to Ada 83
entry_body
is new.Wording Changes from Ada 95
Overriding_indicator
s can be used on entries; this is only useful when a task or protected type inherits from an interface. Extensions to Ada 2005
Extensions to Ada 2012
aspect_specification
can be used in an entry_body
. All other kinds of bodies allow (only) implementation-defined aspects, we need to be consistent.aspect_specification
, allowing the specification of (implementation-defined) aspects for a named entry index. Wording Changes from Ada 2012
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_statement
s (see 9.7).]
Syntax
2entry_call_statement
::=
entry_name
[actual_parameter_part
];
Name Resolution Rules
3The 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
5Under certain circumstances (detailed below), an entry of a task or protected object is checked to see whether it is open or closed:
- 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 aselective_accept
(see 9.7.1) with an openaccept_alternative
that corresponds to the entry; otherwise, it is closed. 7/3 - An entry of a protected object is open if the
condition
of theentry_barrier
of the correspondingentry_body
evaluates to True; otherwise, it is closed. If the evaluation of thecondition
propagates an exception, the exception Program_Error is propagated to all current callers of all entries of the protected object.
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:
- 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.
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.
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)].
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:
- When the associated task reaches a corresponding
accept_statement
, or aselective_accept
with a corresponding openaccept_alternative
; 15/4 - If after performing, as part of a protected action on the associated protected object, an exclusive protected operation on the object, the entry is checked and found to be open.
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.
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 pragma
s (see D.4).
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.
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.
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).]
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/5An implementation may perform the sequence of steps of a protected action using any thread of control; it can be a thread other than 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.
When the entry of a protected object is checked to see whether it is open, the implementation can bypass reevaluating 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 call to an exclusive protected operation of the object since the condition
was last evaluated.
An implementation may evaluate the condition
s of all entry_barrier
s of a given protected object any time any entry of the object is checked to see if it is open.
When an attempt is made to cancel an entry call, the implementation can use a thread of control other than that 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 can allow the entry call to be selected in the interim.
entry_body
, it is propagated to the corresponding caller (see 11.4).condition
of an entry_barrier
is allowed to be evaluated by an implementation more often than strictly necessary, even if the evaluation can have side effects. On the other hand, an implementation can avoid reevaluating 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 is updated by an action performed from outside of a protected action. Examples
30Examples of entry calls:
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
Wording Changes from Ada 2012
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/3requeue_statement
::=
requeue procedure_or_entry_name
[with abort];
Name Resolution Rules
3/3The procedure_or_entry_name
of a requeue_statement
shall resolve to denote a procedure or an entry (the requeue target). The profile of the entry, or the profile or prefixed profile of the procedure, shall either have no parameters, or be type conformant (see 6.3.1) with the profile of the innermost enclosing entry_body
or accept_statement
.
Legality Rules
4A 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.
If the requeue target has parameters, then its (prefixed) profile shall be subtype conformant with the profile of the innermost enclosing callable construct.
Given a requeue_statement where the innermost enclosing callable construct is for an entry E1, for every [specific or class-wide ]postcondition expression P1 that applies to E1, there shall exist a postcondition expression P2 that applies to the requeue target E2 such that
- P1 is fully conformant with the expression produced by replacing each reference in P2 to a formal parameter of E2 with a reference to the corresponding formal paramter of E1; and
- if P1 is enabled, then P2 is also enabled.
The requeue target shall not have an applicable specific or class-wide postcondition that includes an Old or Index attribute_reference
.
If the requeue target is declared immediately within the task_definition
of a named task type or the protected_definition
of a named protected type, and if the requeue statement occurs within the body of that type, and if the requeue is an external requeue, then the requeue target shall not have a specific or class-wide postcondition which includes a name denoting either the current instance of that type or any entity declared within the declaration of that type.
If the target is a procedure, the name shall denote a renaming of an entry, or shall denote a view or a prefixed view of a primitive subprogram of a synchronized interface, where the first parameter of the unprefixed view of the primitive subprogram shall be a controlling parameter, and the Synchronization aspect shall be specified with synchronization_kind
By_Entry for the primitive subprogram.
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
for the entry_body
.
entry_body
case, the intent is that the target object can be global, or can be a component of the protected unit, but cannot be a local variable of the entry_body
. accept_statement
on a parameter of an outer accept_statement
, which could create some strange "long-distance" connections between an entry caller and its server.task_body
is nested inside an accept_statement
, it is permissible to requeue from an accept_statement
of the inner task_body
on parameters of the outer accept_statement
. This is not a problem because all calls on the inner task have to complete before returning from the outer accept_statement
, meaning no "dangling calls" will be created. terminate_alternative
rules remain sensible, and that explicit clearing of the entry queues of a protected object during finalization is rarely necessary. In particular, such clearing of the entry queues is necessary only (ignoring premature Unchecked_Deallocation) for protected objects declared in a task_body
(or created by an allocator for an access type declared in such a body) containing one or more requeue_statement
s. Protected objects declared in subprograms, or at the library level, will never need to have their entry queues explicitly cleared during finalization. Dynamic Semantics
7/5The execution of a requeue_statement
begins with the following sequence of steps:
a)
- The procedure_or_entry_
name
is evaluated. This includes evaluation of theprefix
(if any) identifying the target task or protected object and of theexpression
(if any) identifying the entry within an entry family. 7.2/5
b)- If the target object is not a part of a formal parameter of the innermost enclosing callable construct, a check is made that the accessibility level of the target object is not equal to or deeper than the level of the innermost enclosing callable construct. If this check fails, Program_Error is raised.
c)- Precondition checks are performed as for a call to the requeue target.
d)- The
entry_body
oraccept_statement
enclosing therequeue_statement
is then completed[, finalized, and left (see 7.6.1)].
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).
For the execution of a requeue on an entry of a target protected object, after leaving the enclosing callable construct:
- 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);
- 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).
If the requeue target named in the requeue_statement
has formal parameters, then during the execution of the accept_statement
or entry_body
corresponding to the new entry and during the checking of any preconditions of 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.]
If the requeue_statement
includes the reserved words with abort (it is a requeue-with-abort), then:
- 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;
- if the original entry call was timed (or conditional), then the original expiration time is the expiration time for the requeued call.
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
.
name
for an entry of a family. Examples
18Examples of requeue statements:
requeue Request(Medium) with abort;
-- requeue on a member of an entry family of the current task, see 9.1
20requeue Flags(I).Seize;
-- requeue on an entry of an array component, see 9.4
Extensions to Ada 83
requeue_statement
is new. Extensions to Ada 2005
Inconsistencies With Ada 2012
requeue_statement
. Original Ada 2012 did not specify this, so a program that requeues when the preconditions fail will raise an exception when none would happen in original Ada 2012. We don't expect this to be a problem, as in that case, the entry body would be called with some of its preconditions evaluating as False; the body is likely to assume that they are true and probably will have failed in some other way anyway.