I was working on some multithreaded Ada network code, which involved accepting and handling multiple connections in parallel. An ideal situation for the use of Ada tasks, no? Specifically, multiple task type variables, started in a loop.
Well, I did something like this:
with Ada.Text_IO;
use Ada.Text_IO;
Procedure foo is
task type handler is
entry Start(I: Integer);
end handler;
task body handler is
task_index: Integer;
begin
accept Start(I: Integer) do
task_index:=I;
end Start;
for I in 1..5 loop
Put_Line("Task "&task_index'Image&": "&I'Image);
end loop;
end handler;
begin -- foo
for t in 1..5 loop
declare
bar: handler;
begin
bar.Start(t);
end;
end loop;
end foo;
Expecting that the tasks, once they accepted the start
entry, would execute in parallel.
However, as in this example, the main task waited for each task to finish executing in turn:
$ gnat make foo
$ ./foo
Task 1: 1
Task 1: 2
Task 1: 3
Task 1: 4
Task 1: 5
Task 2: 1
Task 2: 2
Task 2: 3
Task 2: 4
Task 2: 5
Task 3: 1
Task 3: 2
Task 3: 3
Task 3: 4
Task 3: 5
Task 4: 1
Task 4: 2
Task 4: 3
Task 4: 4
Task 4: 5
Task 5: 1
Task 5: 2
Task 5: 3
Task 5: 4
Task 5: 5
Declaring all the tasks up-front, in an array fixed the issue, but left me curious about how this is actually working, why, and where this would be documented.
ARM2012 section 9.2 paragraph 6/3 seems to me to say that the main thread waits at the end of execution for it's child tasks to finish, but in practice it seems to be waiting when the tasks leave the current scope before continuing execution (i.e. looping around, and starting the next task).
Is this a compiler issue, a documentation issue, or a coding issue?
CodePudding user response:
The program (both versions) is working as the Ada standard says. In the version you show, the task object bar, being declared locally in the declare .. begin .. end block, depends on that block (which is a "master" of the task), and so that block statement waits for the task to terminate before execution proceeds (leaves the block statement).
The part of the RM that defines this is section 9.3, "Task Dependence - Termination of Tasks", http://www.ada-auth.org/standards/rm12_w_tc1/html/RM-9-3.html, which shows an example that is close to your code. I don't think paragraph 6/3 in section 9.2 is relevant - it describes some rather special cases in which a locally created task is never activated.
The solution, as you found, is to declare the task objects globally, so that the "master" lives longer. The example in section 9.3 shows another solution that uses access types and dynamically allocated task objects.