Tasks#
Tasks define parallel activities and use a syntax like that of packages. The main subprogram where the program begins is considered the main task.
task T is
-- ...
end T;
task body T is
-- ...
end T;
Possible Outputs
3
5
7
3
7
5
procedure P is
task A;
task body A is
begin
Put_Line(Integer'Image(3));
end A;
task B;
task body B is
begin
Put_Line(Integer'Image(5));
end B;
begin
Put_Line(Integer'Image(7));
end P;
P
subprogram) the unit may not exit until all of the tasks have
completed and so, having reached end P;
, the program will wait for the tasks to complete if they are not already.
Communication#
There are two ways in which tasks can communicate with each other:
- Direct communication by message passing. (rendezvous)
- Indirect communication by common access to shared data.
Rendezvous#
A rendezvous is direct message passing between tasks and works by one task calling an entry
declared in another.
task T is
entry E(...);
end;
-- ...
task body T is
accept E(...) do
-- ...
end E;
end T
in
, out
and in out
parameters like subprograms, but they may not have access
parameters or a result like a function. Tasks may have private entries which can be called by local tasks. To call it
from another task, the typical syntax is used: T.E(...);
and the task that owns the entry will execute it. The
task that calls the entry will stall until then entry has been executed. This act of task 1 calling the entry and
waiting until task 2 has executed the entry is called a rendezvous. Both tasks continue independently after the last
statement of the accept is completed.
Entries can be called multiple times, but if an entry is called after the task has completed then a Tasking_Error
will be raised or, if an entry is called but cannot be reached because the task has passed it, it may stall. If the
entry is being called from multiple tasks simultaneously then they may have to be queued up (normally as FIFO). In order
to see how many tasks are queued, the E'Count
may be used inside of the entry’s body (but must also be inside of
the task’s body).
Entries do not have to have a body but can simply be defined as accept E;
(in which case it only functions a
synchronisation tool). They may also be renamed as a procedure:
procedure Send(X: Integer) renames T.E;
Ordering and Blocking of Entries
This example has a much stricter execution order and it can be seen that the entries add blockers which prevent
further execution. After the accept
’s statements have been executed, the statements following are also
executed until another accept
is found (or the end).
Output
Start of B
Started main
A got: 77
Post A's Put
B got: 2.50000E+01
Post B's Put
From B we got: 1.25000E+01
Post B's Get
procedure P is
task A is
entry Put(X: Integer);
end A;
task body A is
begin
accept Put(X: Integer) do
Put_Line("A got:" & Integer'Image(X));
end Put;
Put_Line("Post A's Put");
end A;
task B is
entry Put(X: Float);
entry Get(X: out Float);
end B;
task body B is
begin
Put_Line("Start of B");
accept Put(X: Float) do
Put_Line("B got:" & Float'Image(X));
end Put;
Put_Line("Post B's Put");
accept Get(X: out Float) do
X := 12.5;
end Get;
Put_Line("Post B's Get");
end B;
X: Float;
begin
Put_Line("Started main");
A.Put(77);
B.Put(25.0);
B.Get(X);
Put_Line("From B we got: " & Float'Image(X));
end P;
B.Put
were made before the Ada B.Get
then it will stall because the task does not ever
“go back” to B
’s accept Get
. A Tasking_Error
is raised if a call to any of the entries is
made at the end because the tasks will all have finished.
Multiple Entry Calls
Here, a loop is used in order to call T.Get
multiple times until the loop ends in which case it can be seen
that a Tasking_Error
is raised upon the 11th call to t.Get
.
Output
0
1
2
3
4
5
6
7
8
9
Caught the tasking error
procedure P is
task T is
entry Get(X: out Integer);
end T;
task body T is
I: Integer := 0;
begin
while I < 10 loop
accept Get(X: out Integer) do
X := I;
I := I + 1;
end Get;
end loop;
end T;
X: Integer;
begin
loop
T.Get(X);
Put_Line(Integer'Image(X));
end loop;
exception
when Tasking_Error => Put_Line("Caught ""Tasking_Error"" exception");
end P;
Aspects#
Nonblocking
- Specify that an entity does not block.
Can be applied to packages, subprograms and entries as well as various generic parameters and protected types. Packages marked asPure
are automaticallyNonblocking
. Max_Entry_Queue_Length
- Set the maximum number of spaces available in an entry’s queue.
AProgram_Error
is raised when attempting to enqueue if the queue is already full. If applied to a task then it will apply for all entries in the task.