1
votes

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

2
Do you want your server to only run for 5 seconds ? - NWS

2 Answers

2
votes

You want the else version:

loop
   -- blocking read from socket
   -- process the data received
   select
      accept Stop;
      exit;
   else
      null;
   end select;
end loop;
1
votes

The way I typically set this up is first by using AdaCore's GNAT.Sockets package, rather than directly programming the sockets. Since I'll be using the (socket) select() function (which is wrapped as Check_Selector) to be notified when data is available on the socket, GNAT.Sockets provides an Abort_Selector() procedure that can be invoked from elsewhere. With the task blocked on Check_Selector(), I just wait for data to arrive (Status = Completed) or the flag that it's time to exit (Status = Aborted).

See the start of the Monitor_Connections procedure (lines 397-416ish) in the TOMI_4_Ada package TCP_Connect. Monitor_Connections is invoked from task Connection_Monitoring (lines 469-495).