Tasks: Declaration, Activation & Termination
Create concurrent threads of control with single tasks and task types, parameterize them with discriminants, understand activation, masters and termination (the parent waits for its tasks), allocate tasks dynamically, use 'Terminated and 'Callable, and avoid data races by design.
Concurrency is part of the Ada language itself, not a library. A task is an object with its own thread of control; you declare it like a variable and it starts automatically. The enclosing scope (its master) cannot finish until all its tasks have terminated, so you never lose track of a thread. This lesson introduces tasks; the following lessons show how they communicate safely.
Task Types and Single Tasks
A task has a specification (its interface — entries, covered in the next lesson) and a body: 'task type Worker (Id : Positive); task body Worker is ... begin ... end Worker;'. Declaring 'W1 : Worker (1);' creates a task object. A single task ('task Greeter;') declares an anonymous task type with exactly one object. Discriminants pass initial parameters (an Id, a priority, an access to shared data). Arrays and records of tasks are allowed, and tasks are limited objects (no copying).
• task type T (Discriminants) [is entries end T]; task body T is ... end T; • task Name; — a single task. • Tasks are limited objects; arrays of tasks are fine.
Activation, Masters and Termination
Tasks declared in a declarative part are activated together just before the statements after 'begin' execute (after elaboration of the declarations). The subprogram, block or task in which a task is declared is its master: the master waits at its end until all its dependent tasks terminate. That is why the block containing W1 .. W4 acts like a 'join'. A task terminates when its body completes (or when it is aborted, or selects a terminate alternative). An exception unhandled inside a task silently terminates that task only — always add a handler that logs it. Task'Terminated and Task'Callable query the state.
• Activation: just after the declarations are elaborated. • Masters wait for their tasks → structured concurrency. • Unhandled exception in a task: task dies quietly — log it! • 'Terminated, 'Callable attributes.
Dynamic Tasks and Shared Data
'type Worker_Ref is access Worker; X := new Worker (1);' creates a task at run time; it is activated immediately and its master is the scope of the access type. Tasks may read and write shared variables, but unsynchronized concurrent access to the same variable is erroneous (a data race). Safe patterns: each task writes only its own data (like Results (Id)) and the parent reads after the tasks terminate; or communicate via rendezvous or protected objects. GNAT maps each Ada task to an operating-system thread.
• new Task_Type creates tasks dynamically. • Data races are erroneous — partition data or use protected objects. • GNAT: one OS thread per task.
Code Examples
Four workers each sum a quarter of 1.. 1 000 000 into their own slot. The block containing them cannot exit until all four finish, so the main program reads the results safely afterwards. Output (deterministic because only the main task prints): Main: tasks declared here start (activate) before this line runs Main: all four workers have terminated worker 1 sum = 31250125000 worker 2 sum = 93750125000 worker 3 sum = 156250125000 worker 4 sum = 218750125000 Total 1.. 1_000_000 = 500000500000 (formula: 500000500000) Dynamic worker terminated: TRUE Greeter terminated: TRUE task body Worker is begin Total:= Total + Local_Sum; -- shared variable, no protection end Worker;
-- write to Results (Id), or call a protected procedure Total.Add (Local_Sum)
Unprotected concurrent updates are data races.
task body Worker is begin ... -- may raise end Worker;
task body Worker is begin ... exception when E : others => Log (Exception_Information (E)); end Worker;
Unhandled exceptions silently kill tasks.
-- printing from several tasks and expecting a fixed order -- collect results and print from one task, or serialize output through a protected object
Interleaving of independent tasks is nondeterministic.
When does a task declared in a block start running? A. When you call Start B. At activation, just before the block's statements execute C. At program end D. When its first entry is called
Can a block end while its local tasks are still running? A. Yes B. No, the master waits for dependent tasks to terminate C. Only in Ravenscar D. Only with abort
What happens to an unhandled exception inside a task? A. Propagates to main B. The task terminates (silently) C. The program aborts D. It's ignored and the task continues
Which attribute tells whether a task has finished? A. 'Done B. 'Terminated C. 'Finished D. 'Callable
B. ('Callable is False once it is completed or terminated.) Trace the task example.
Real output: Main: tasks declared here start (activate) before this line runs Main: all four workers have terminated worker 1 sum = 31250125000 worker 2 sum = 93750125000 worker 3 sum = 156250125000 worker 4 sum = 218750125000 Total 1 .. 1_000_000 = 500000500000 (formula: 500000500000) Dynamic worker terminated: TRUE Greeter terminated: TRUE
with Ada.Text_IO; use Ada.Text_IO;
procedure L52_Tasks is
type Result_Array is array (1 .. 4) of Long_Long_Integer;
Results : Result_Array := [others => 0];
-- A task type with a discriminant: each worker sums a different slice
task type Worker (Id : Positive);
task body Worker is
Sum : Long_Long_Integer := 0;
From : constant Long_Long_Integer := Long_Long_Integer (Id - 1) * 250_000 + 1;
begin
for N in From .. From + 249_999 loop
Sum := Sum + N;
end loop;
Results (Id) := Sum; -- each task writes only its own component
end Worker;
-- A single task (anonymous task type)
task Greeter;
task body Greeter is
begin
null; -- finishes immediately
end Greeter;
Total : Long_Long_Integer := 0;
begin
Put_Line ("Main: tasks declared here start (activate) before this line runs");
declare
W1 : Worker (1);
W2 : Worker (2);
W3 : Worker (3);
W4 : Worker (4);
begin
null; -- the block cannot end until W1 .. W4 terminate
end;
Put_Line ("Main: all four workers have terminated");
for I in Results'Range loo
…