Skip to main content

9.7 Select Statements

danger

This Reference Manual output has not been verified, and may contain omissions or errors. Report any problems on the tracking issue

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;

Extensions to Ada 83

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 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.

13.a
reason

This simplifies the implementation and the description of the semantics.

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
20.a
ramification

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.

21

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

22/5

NOTE A selective_accept can have several open delay_alternatives. A selective_accept can 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;

Wording Changes from Ada 83

24.a

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

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).

3.a/2
reason

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.

3.b/2

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/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.

5.a/2
This paragraph was deleted.

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;

Wording Changes from Ada 83

7.a/3

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

7.b/3

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.]

1.a

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

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.

4/5

NOTE A conditional_entry_call can 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/5

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

6.a/3

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

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.

9.a
discussion

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.

9.b

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

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/5

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

Extensions to Ada 95

14.b/2

A procedure can be used as the triggering_statement of an asynchronous_select, if the procedure might actually be an entry.