Let me illustrate this question with code first:
with Ada.Text_IO;
procedure Test
is
task type Serving_Task is
entry Start;
end Serving_Task;
task body Serving_Task is begin
accept Start;
Ada.Text_IO.Put_Line ("Started");
loop
null; -- Blocking operation, eg. accepting on a socket.
null; -- Processing on blocking operation's result.
end loop;
Ada.Text_IO.Put_Line ("Stopped");
end Serving_Task;
type Server is tagged limited record
Serving : Serving_Task;
end record;
procedure Start
(S : in out Server)
is begin
Ada.Text_IO.Put_Line ("Starting");
S.Serving.Start;
end Start;
procedure Stop
(S : in out Server)
is begin
Ada.Text_IO.Put_Line ("Stopping");
-- TODO To implement.
end Stop;
Main_Server : Server;
begin
Ada.Text_IO.Put_Line ("Calling Start");
Start (Main_Server);
delay 5.0;
Ada.Text_IO.Put_Line ("Calling Stop");
Stop (Main_Server);
end Test;
This is typical server construct - there's a server task with a loop accepting incoming connections. My question is - what is the best way to implement Stop procedure for Server type.
I'd like it to wait until the serving task blocks on accepting a socket (or just before the blocking call, so every accepted request if fully handled before terminating the task) and exit the loop, so the task can clean up before terminating.
First thing that comes to mind is adding
select
accept Stop;
or
delay 0.1;
exit;
end select;
at the end of the loop, but that's 0.1 second wasted on each iteration. Seems significant.
I've looked at some examples in RM, Lovelace tutorial and Wikibook, but nothing really seemed appropriate.
What is the best practice for such (fairly common I believe) problem in Ada?
And the answer is...
Example solution based on Marc C's answer: https://gist.github.com/3413017