Waitgroups in Ada
Our example demonstrates how to wait for multiple tasks to finish using a Protected_Counter
. This is similar to the concept of wait groups in other languages.
with Ada.Text_IO;
with Ada.Task_Identification;
with Ada.Calendar;
procedure Waitgroups is
use Ada.Text_IO;
use Ada.Calendar;
protected type Protected_Counter is
procedure Increment;
procedure Decrement;
entry Wait;
private
Count : Natural := 0;
end Protected_Counter;
protected body Protected_Counter is
procedure Increment is
begin
Count := Count + 1;
end Increment;
procedure Decrement is
begin
Count := Count - 1;
end Decrement;
entry Wait when Count = 0 is
begin
null;
end Wait;
end Protected_Counter;
Counter : Protected_Counter;
task type Worker is
entry Start(ID : Positive);
end Worker;
task body Worker is
Worker_ID : Positive;
begin
accept Start(ID : Positive) do
Worker_ID := ID;
end Start;
Put_Line("Worker" & Worker_ID'Image & " starting");
delay 1.0; -- Simulate an expensive task
Put_Line("Worker" & Worker_ID'Image & " done");
Counter.Decrement;
end Worker;
Workers : array (1..5) of Worker;
begin
for I in Workers'Range loop
Counter.Increment;
Workers(I).Start(I);
end loop;
Counter.Wait;
end Waitgroups;
This is the equivalent of the worker function in Ada. It’s implemented as a task type.
The Protected_Counter
type is used to safely manage the count of active workers. It provides thread-safe operations to increment, decrement, and wait for the count to reach zero.
In the main procedure:
We create an array of Worker tasks and start them, incrementing the counter for each.
for I in Workers'Range loop
Counter.Increment;
Workers(I).Start(I);
end loop;
We then wait for all workers to finish by calling Counter.Wait
. This will block until all workers have decremented the counter.
Counter.Wait;
Note that Ada’s tasking model inherently handles the termination of all tasks before the main program exits, so we don’t need to explicitly wait for task completion beyond this point.
To run the program, save it as waitgroups.adb
and use the following commands:
$ gnatmake waitgroups.adb
$ ./waitgroups
Worker 1 starting
Worker 2 starting
Worker 3 starting
Worker 4 starting
Worker 5 starting
Worker 1 done
Worker 2 done
Worker 3 done
Worker 4 done
Worker 5 done
The order of workers starting up and finishing is likely to be different for each invocation.
Ada’s tasking model provides built-in support for concurrent programming, making it well-suited for this kind of parallel execution pattern. The Protected_Counter
type ensures thread-safe operations, similar to the sync.WaitGroup
in other languages.