Skip to content

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;
Tasks that are declare as above are automatically started and may run and/or complete in different orders. When tasks depend on a unit (as these tasks depend on the 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:

  1. Direct communication by message passing. (rendezvous)
  2. 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
Entries may have 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;
Additionally, entries may have pre- and postconditions like subprograms.

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;
If a second call to 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 as Pure are automatically Nonblocking.
Max_Entry_Queue_Length
Set the maximum number of spaces available in an entry’s queue.
A Program_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.