Ada: Tasking
Concurrency is part of the Ada language, not a library: a task is a thread declared in the source, an entry is a synchronized call, and a protected object is a monitor with compiler-checked mutual exclusion. This is why Ada dominates real-time and safety-critical domains.
Tasks
A task type declares a thread. The body runs concurrently from the moment its declaration is elaborated; the program cannot end while tasks run:
with Ada.Text_IO; use Ada.Text_IO;
procedure Task_Demo is
task Worker;
task body Worker is
begin
for I in 1 .. 3 loop
Put_Line ("worker tick" & Integer'Image (I));
delay 0.1; -- suspend this task for 0.1 seconds
end loop;
end Worker;
begin
Put_Line ("main continues in parallel");
end Task_Demo;
-- both texts interleave; the run time ends when Worker finishes
delay suspends the calling task — at least the requested time. Tasks may also be declared as task type and instantiated many times, with a discriminant configuring each.
Rendezvous with Entries
An entry is a subprogram call that synchronizes: the caller blocks until the accepting task reaches the matching accept. Data crosses in both directions inside the rendezvous:
procedure Rendezvous_Demo is
task type Counter (Max : Natural);
task body Counter is
N : Natural := 0;
begin
loop
accept Bump do -- waits for a caller
N := N + 1; -- runs inside the rendezvous
end Bump;
exit when N >= Max;
end loop;
end Counter;
C : Counter (Max => 5);
begin
Protected Objects
For shared data, a protected object is simpler and faster than hand-rolled rendezvous: its procedures run under mutual exclusion, its entries add conditions, its functions run concurrently read-only:
protected Buffer is
entry Put (Item : in Integer);
entry Get (Item : out Integer);
private
Cells : array (1 .. 8) of Integer;
Count : Natural := 0;
end Buffer;
protected body Buffer is
entry Put (Item : in Integer) when Count < 8 is
begin
Count := Count + 1;
Cells (Count) := Item; -- mutual exclusion is guaranteed
end Put;
entry Get (Item : out Integer) when Count > 0 is
begin
Item := Cells (Count);
Count := Count - 1;
end Get;
end Buffer;
The when guard is a barrier: callers of Put queue until there is room, callers of Get until there is data — a correct bounded queue in twenty lines, no mutexes, no condition variables, no race conditions.
select and Timeouts
select lets a task wait on several entries at once, with alternatives, guards, and timeouts — the shape of every robust real-time loop:
-- inside a task body:
-- loop
-- select
-- accept Shutdown do Running := False; end Shutdown;
-- or
-- accept Work (Item : in Integer) do Process (Item); end Work;
-- or
-- delay 5.0; -- nothing arrived in 5 seconds
-- Put_Line ("still alive");
-- end select;
-- exit when not Running;
-- end loop;
The Ravenscar profile restricts tasking to a statically analyzable subset (no dynamic task creation, simple barriers only) — the foundation of certified real-time systems and most SPARK concurrency proofs.
Next: Standard Library.