Select: Guards, Timeouts, Conditional Calls & Asynchronous Transfer
Use all forms of select: selective accept with guards (when), delay and terminate alternatives and else parts; timed and conditional entry calls on the caller side; and asynchronous transfer of control (select ... then abort) to bound computations, plus the abort statement.
The select statement is Ada's multiplexer for concurrency. On the server side it waits for one of several entries, optionally guarded, with a timeout or a terminate option. On the client side it limits how long a caller will wait. And its asynchronous form aborts a computation when a deadline or event occurs — all expressed as structured statements rather than callbacks and flags.
Selective Accept
'select when Guard => accept A ... or accept B ... or delay 2.0; ... end select;' Each accept may have a guard; an alternative with a False guard is closed. The delay alternative runs if no call arrives within the time (a server timeout). 'or terminate;' lets a server end automatically when its master has completed and no other task can call it — the idiomatic way to stop servers without explicit shutdown entries. An 'else' part executes immediately if no caller is waiting (polling). If all alternatives are closed and there is no else, Program_Error is raised.
• when Guard => accept ... — conditional acceptance. • or delay D — timeout; or terminate — auto shutdown; else — poll. • All closed and no else → Program_Error.
Timed and Conditional Entry Calls
On the caller side: 'select Server.Request; ... or delay 0.1; ... end select;' is a timed entry call — if the rendezvous has not started within 0.1 s, the call is withdrawn. 'select Gate.Open; ... else ... end select;' is a conditional entry call — it only proceeds if the entry can be accepted immediately (here Open's guard was closed). Both also work with protected entries.
• select Call; or delay D; ... end select; — timed call. • select Call; else ... end select; — conditional call.
Asynchronous Transfer of Control and abort
'select delay 0.2; <on timeout> then abort <abortable part> end select;' runs the abortable part; if the triggering statement (a delay or an entry call) completes first, the abortable part is abandoned. This bounds computations such as iterative refinement or search. Abortion takes effect at abort completion points (such as delay statements or entry calls — GNAT may also need periodic polling in tight loops). 'abort T;' aborts a whole task; both are heavy tools, excluded from high-integrity profiles like Ravenscar.
• select trigger then abort ... end select; — ATC. • Abort happens at completion points. • abort Task_Name; — kills a task (use sparingly).
Code Examples
Gate uses guards so Open is only accepted while closed; the conditional call finds it closed. Slow_Server is busy for 1 s, so the 0.1 s timed call gives up; later the server's terminate alternative lets it finish. ATC abandons an endless loop after 0.2 s. Gate times out after 2 s idle. Output: Pass while closed: FALSE Pass while open: TRUE Conditional call: Open not available right now Timed call: Slow_Server did not accept within 0.1 s ATC: deadline reached, computation abandoned Iterations completed before abort > 0? TRUE Gate: idle for 2 s, terminating loop select accept Get... else null; -- busy polling end select; end loop;
loop select accept Get ... or terminate; end select; end loop;
An else part in a loop spins the CPU; block instead.
-- explicit Shutdown entries on every server or terminate; -- in the server's select
The terminate alternative ends servers automatically when no one can call them.
then abort Update_Shared_Data; -- may be aborted halfway -- keep abortable parts free of partial updates (protected operations are abort-deferred)
Aborting in the middle of an update leaves data inconsistent.
What does 'or terminate;' in a server's select do? A. Kills the server immediately B. Lets the server terminate when its master is done and no one can call it C. Terminates the caller D. Raises Tasking_Error
Which form tries an entry call only if it can be accepted immediately? A. Timed entry call B. Conditional entry call (select ... else) C. ATC D. accept
What does 'select delay 0.2; then abort Compute; end select;' do? A. Delays Compute B. Runs Compute and abandons it if 0.2 s pass first C. Runs Compute 0.2 times D. Syntax error
What happens in a select with all guards False and no else? A. Waits forever B. Program_Error C. Picks one anyway D. Returns Trace the select example.
Real output: Pass while closed: FALSE Pass while open: TRUE Conditional call: Open not available right now Timed call: Slow_Server did not accept within 0.1 s ATC: deadline reached, computation abandoned Iterations completed before abort > 0? TRUE Gate: idle for 2 s, terminating
with Ada.Text_IO; use Ada.Text_IO;
procedure L55_Select is
-- Guards and delay alternatives in a selective accept
task Gate is
entry Open;
entry Close;
entry Pass (Ok : out Boolean);
end Gate;
task body Gate is
Is_Open : Boolean := False;
Timeouts : Natural := 0;
begin
loop
select
when not Is_Open => -- guard: only accepted when closed
accept Open do
Is_Open := True;
end Open;
or
when Is_Open =>
accept Close do
Is_Open := False;
end Close;
or
accept Pass (Ok : out Boolean) do
Ok := Is_Open;
end Pass;
or
delay 2.0; -- no call for 2 s
Timeouts := Timeouts + 1;
exit when Timeouts = 1;
end select;
end loop;
Put_Line ("Gate: idle for 2 s, terminating");
end Gate;
task Slow_Server is
entry Request;
end Slow_Server;
task body Slow_Server is
begin
delay 1.0; -- busy: not ready t
…