Worker Pools in Ada
Our example demonstrates how to implement a worker pool using tasks and protected objects in Ada.
with Ada.Text_IO;
with Ada.Calendar;
procedure Worker_Pools is
use Ada.Text_IO;
use Ada.Calendar;
Job_Count : constant Integer := 5;
protected type Job_Queue is
entry Add_Job(J : in Integer);
entry Get_Job(J : out Integer);
procedure Close;
private
Jobs : array (1..Job_Count) of Integer;
Count : Natural := 0;
Closed : Boolean := False;
end Job_Queue;
protected body Job_Queue is
entry Add_Job(J : in Integer) when Count < Job_Count is
begin
Count := Count + 1;
Jobs(Count) := J;
end Add_Job;
entry Get_Job(J : out Integer) when (Count > 0) or Closed is
begin
if Count = 0 then
raise Program_Error with "Queue is empty and closed";
end if;
J := Jobs(1);
Jobs(1..Count-1) := Jobs(2..Count);
Count := Count - 1;
end Get_Job;
procedure Close is
begin
Closed := True;
end Close;
end Job_Queue;
protected type Result_Queue is
procedure Add_Result(R : in Integer);
entry Get_Result(R : out Integer);
private
Results : array (1..Job_Count) of Integer;
Count : Natural := 0;
end Result_Queue;
protected body Result_Queue is
procedure Add_Result(R : in Integer) is
begin
Count := Count + 1;
Results(Count) := R;
end Add_Result;
entry Get_Result(R : out Integer) when Count > 0 is
begin
R := Results(1);
Results(1..Count-1) := Results(2..Count);
Count := Count - 1;
end Get_Result;
end Result_Queue;
Jobs : Job_Queue;
Results : Result_Queue;
task type Worker is
entry Start(ID : in Integer);
end Worker;
task body Worker is
Worker_ID : Integer;
Job : Integer;
begin
accept Start(ID : in Integer) do
Worker_ID := ID;
end Start;
loop
select
Jobs.Get_Job(Job);
Put_Line("worker" & Integer'Image(Worker_ID) & " started job" & Integer'Image(Job));
delay 1.0; -- Simulate an expensive task
Put_Line("worker" & Integer'Image(Worker_ID) & " finished job" & Integer'Image(Job));
Results.Add_Result(Job * 2);
or
terminate;
end select;
end loop;
end Worker;
Workers : array (1..3) of Worker;
begin
for I in Workers'Range loop
Workers(I).Start(I);
end loop;
for J in 1..Job_Count loop
Jobs.Add_Job(J);
end loop;
Jobs.Close;
declare
Result : Integer;
begin
for A in 1..Job_Count loop
Results.Get_Result(Result);
end loop;
end;
end Worker_Pools;
This Ada program implements a worker pool using tasks and protected objects. Here’s a breakdown of the main components:
We define a
Job_Queue
protected type to safely manage job distribution among workers.A
Result_Queue
protected type is used to collect results from workers.The
Worker
task type represents our workers. Each worker continuously tries to get a job, process it (simulated by a 1-second delay), and add the result to the result queue.In the main procedure, we create 3 worker tasks, add 5 jobs to the job queue, and then collect all the results.
To run this program, save it as worker_pools.adb
and compile it using an Ada compiler:
$ gnatmake worker_pools.adb
$ ./worker_pools
The output will show the 5 jobs being executed by various workers. The program takes about 2 seconds to complete despite doing about 5 seconds of total work because there are 3 workers operating concurrently.
This example demonstrates Ada’s built-in support for concurrent programming using tasks and protected objects, which provide a safe and structured way to implement worker pools and other concurrent patterns.