9.7 Select Statements
This Reference Manual output has not been verified, and may contain omissions or errors. Report any problems on the tracking issue
[There are four forms of the select_statement
. One form provides a selective wait for one or more select_alternative
s. Two provide timed and conditional entry calls. The fourth provides asynchronous transfer of control.]
Syntax
2select_statement
::=
selective_accept
| timed_entry_call
| conditional_entry_call
| asynchronous_select
Examples
3Example of a select statement:
select
accept Driver_Awake_Signal;
or
delay 30.0*Seconds;
Stop_The_Train;
end select;
Extensions to Ada 83
Asynchronous_select
is new.
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
2selective_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
8
A selective_accept
shall contain at least one accept_alternative
. In addition, it can contain:
- a
terminate_alternative
(only one); or 10 - one or more
delay_alternative
s; or 11 - an else part (the reserved word else followed by a
sequence_of_statements
).
These three possibilities are mutually exclusive.
Legality Rules
13If a selective_accept
contains more than one delay_alternative
, then all shall be delay_relative_statement
s, or all shall be delay_until_statement
s for the same time type.
This simplifies the implementation and the description of the semantics.
Dynamic Semantics
14A 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.
For the execution of a selective_accept
, any guard
condition
s 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.
Open accept_alternative
s 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.
Selection of the other forms of alternative or of an else part is performed as follows:
- An open
delay_alternative
is selected when its expiration time is reached if noaccept_alternative
or otherdelay_alternative
can be selected prior to the expiration time. If severaldelay_alternative
s 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 thedelay_alternative
s whose expiration time has passed. 19 - The else part is selected and its
sequence_of_statements
is executed if noaccept_alternative
can immediately be selected; in particular, if all alternatives are closed. 20/5 - An open
terminate_alternative
is selected if the conditions stated at the end of 9.3 are satisfied.
In the absence of a requeue_statement
, the conditions stated are such that a terminate_alternative
cannot be selected while there is a queued entry call for any entry of the task. In the presence of requeues from a task to one of its subtasks, it is possible that when a terminate_alternative
of the subtask is selected, requeued calls (for closed entries only) might still be queued on some entry of the subtask. Tasking_Error will be propagated to such callers, as is usual when a task completes while queued callers remain.
The exception Program_Error is raised if all alternatives are closed and there is no else part.
NOTE A selective_accept
can have several open delay_alternative
s. A selective_accept
can have several open accept_alternative
s for the same entry.
Examples
23Example of a task body with a selective accept:
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;
Wording Changes from Ada 83
The name of selective_wait
was changed to selective_accept
to better describe what is being waited for. We kept select_alternative
as is, because selective_accept_alternative
was too easily confused with accept_alternative
.
9.7.2 Timed Entry Calls
1/5[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 can be implemented by an entry. ]
Syntax
2timed_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/2If 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).
This would be a confusing way to call a procedure, so we only allow it when it is possible that the procedure is actually an entry. We could have allowed formal subprograms here, but we didn't because we'd have to allow all formal subprograms, and it would increase the difficulty of generic code sharing.
We say “statically denotes” because an access-to-subprogram cannot be primitive, and we don't have anything like access-to-entry. So only names of entries or procedures are possible.
Dynamic Semantics
4/2For 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.
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
6Example of a timed entry call:
select
Controller.Request(Medium)(Some_Item);
or
delay 45.0;
-- controller too busy, try something else
end select;
Wording Changes from Ada 83
This subclause comes before the one for Conditional Entry Calls, so we can define conditional entry calls in terms of timed entry calls.
Incompatibilities With Ada 95
A procedure call can be used as the entry_call_alternative
in a timed or conditional entry call, if the procedure might actually be an entry. Since the fact that something is an entry could be used in resolving these calls in Ada 95, it is possible for timed or conditional entry calls that resolved in Ada 95 to be ambiguous in Ada 2005. That could happen if both an entry and procedure with the same name and profile exist, which should be rare.
9.7.3 Conditional Entry Calls
1/5[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 can be implemented by an entry.]
To be honest: In the case of an entry call on a protected object, it is OK if the entry is closed at the start of the corresponding protected action, so long as it opens and the call is selected before the end of that protected action (due to changes in the Count attribute).
Syntax
2conditional_entry_call
::=
select
entry_call_alternative
else
sequence_of_statements
end select;
Dynamic Semantics
3The 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.
NOTE A conditional_entry_call
can briefly increase the Count attribute of the entry, even if the conditional call is not selected.
Examples
5Example of a conditional entry call:
procedure Spin(R : in out Resource) is -- see 9.4
begin
loop
select
R.Seize;
return;
else
null; -- busy waiting
end select;
end loop;
end;
Wording Changes from Ada 83
This subclause comes after the one for Timed Entry Calls, so we can define conditional entry calls in terms of timed entry calls. We do that so that an "expiration time" is defined for both, thereby simplifying the definition of what happens on a requeue-with-abort.
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
2asynchronous_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/2For 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].
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.
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.
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.
We currently don't specify when the by-copy [in] out parameters are assigned back into the actuals. We considered requiring that to happen after the abortable_part
is left. However, that doesn't seem useful enough to justify possibly overspecifying the implementation approach, since some of the parameters are passed by reference anyway.
In an earlier description, we required that the sequence_of_statements
of the triggering_alternative
execute after aborting the abortable_part
, but before waiting for it to complete and finalize, to provide more rapid response to the triggering event in case the finalization was unbounded. However, various reviewers felt that this created unnecessary complexity in the description, and a potential for undesirable concurrency (and nondeterminism) within a single task. We have now reverted to simpler, more deterministic semantics, but anticipate that further discussion of this issue might be appropriate during subsequent reviews. One possibility is to leave this area implementation defined, so as to encourage experimentation. The user would then have to assume the worst about what kinds of actions are appropriate for the sequence_of_statements
of the triggering_alternative
to achieve portability.
Examples
10Example of a main command loop for a command interpreter:
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:
select
delay 5.0;
Put_Line("Calculation does not converge");
then abort
-- This calculation is expected to finish in 5.0 seconds;
-- if not, it is assumed to diverge.
Horribly_Complicated_Recursive_Function(X, Y);
end select;
14/5
Note that these examples presume that there are abort completion points (see 9.8) within the execution of the abortable_part
.
Extensions to Ada 83
Asynchronous_select
is new.
Extensions to Ada 95
A procedure can be used as the triggering_statement
of an asynchronous_select
, if the procedure might actually be an entry.