Hi, I am working to finish Ben Ari’s Ada for Software Engineers.
Anything related to concurrency is already hard enough to visualize, but the author isn’t doing much to help, to be honest.
I don’t know what is relevant so there isn’t much in the code that I can cut out.
pragma Queuing_Policy(Priority_Queuing);
with Ada.Text_IO; use Ada.Text_IO;
procedure CEOT is
type Departments is (Engineering, Finance, Marketing);
type ID_Numbers is range 0..10;
Group_Size: constant array(Departments) of
ID_Numbers range 2..ID_Numbers'Last := (
Engineering => 5, Finance => 3, Marketing => 2);
task CEO is
entry Wake(Departments);
entry Invite_In(Departments)(ID: ID_Numbers);
entry Show_Out(Departments)(ID: ID_Numbers);
end CEO;
type Door_State is (Open, Closed);
protected type Room(Department: Departments; Size: ID_Numbers) is
entry Register;
procedure Open_Door;
private
entry Wait_for_Last_Member;
Waiting: ID_Numbers := 0;
Entrance: Door_State := Open;
Exit_Door: Door_State := Closed;
end Room;
protected body Room is
entry Register when Entrance = Open is
begin
if Size = 1 then
Entrance := Closed;
requeue CEO.Wake(Department) with abort;
end if;
Waiting := Waiting + 1;
if Waiting < Size then
requeue Wait_for_Last_Member with abort;
else
Waiting := Waiting - 1;
Entrance := Closed;
Exit_Door := Open;
end if;
end Register;
entry Wait_for_Last_Member when Exit_Door = Open is
begin
Waiting := Waiting - 1;
if Waiting = 0 then
Exit_Door := Closed;
requeue CEO.Wake(Department) with abort;
end if;
end Wait_for_Last_Member;
procedure Open_Door is
begin
Entrance := Open;
end Open_Door;
end Room;
Engineering_Room: Room(Engineering, Group_Size(Engineering));
Finance_Room: Room(Finance, Group_Size(Finance));
Marketing_Room: Room(Marketing, Group_Size(Marketing));
task body CEO is
I: ID_Numbers;
begin
loop
select
accept Wake(Finance);
for N in 1..Group_Size(Finance) loop
accept Invite_In(Finance)(ID: ID_Numbers) do
I := ID;
end Invite_In;
end loop;
for N in 1..Group_Size(Finance) loop
accept Show_Out(Finance)(ID: ID_Numbers) do
I := ID;
end Show_Out;
end loop;
Finance_Room.Open_Door;
or
when Wake(Finance)'Count = 0 =>
accept Wake(Marketing);
for N in 1..Group_Size(Marketing) loop
accept Invite_In(Marketing)(ID: ID_Numbers) do
I := ID;
end Invite_In;
end loop;
for N in 1..Group_Size(Marketing) loop
accept Show_Out(Marketing)(ID: ID_Numbers) do
I := ID;
end Show_Out;
end loop;
Marketing_Room.Open_Door;
or
when Wake(Finance)'Count = 0 and
Wake(Marketing)'Count = 0 =>
accept Wake(Engineering);
for N in 1..Group_Size(Engineering) loop
accept Invite_In(Engineering)(ID: ID_Numbers) do
I := ID;
end Invite_In;
end loop;
for N in 1..Group_Size(Engineering) loop
accept Show_Out(Engineering)(ID: ID_Numbers) do
I := ID;
end Show_Out;
end loop;
Engineering_Room.Open_Door;
or
terminate;
end select;
end loop;
end CEO;
task type Engineer_Task(ID: ID_Numbers);
task body Engineer_Task is
begin
loop
delay 1.0;
Engineering_Room.Register;
CEO.Invite_In(Engineering)(ID);
CEO.Show_Out(Engineering)(ID);
end loop;
end Engineer_Task;
ds
task type Finance_Task(ID: ID_Numbers);
task body Finance_Task is
begin
loop
delay 4.0;
Finance_Room.Register;
CEO.Invite_In(Finance)(ID);
CEO.Show_Out(Finance)(ID);
end loop;
end Finance_Task;
task type Marketing_Task(ID: ID_Numbers);
task body Marketing_Task is
begin
loop
delay 2.0;
Marketing_Room.Register;
CEO.Invite_In(Marketing)(ID);
CEO.Show_Out(Marketing)(ID);
end loop;
end Marketing_Task;
type Engineer_Ptr is access Engineer_Task;
type Finance_Ptr is access Finance_Task;
type Marketing_Ptr is access Marketing_Task;
Engineers: array(1..7) of Engineer_Ptr;
Accountants: array(1..5) of Finance_Ptr;
Salespersons: array(1..8) of Marketing_Ptr;
begin
for I in Engineers'Range loop
Engineers(I) := new Engineer_Task(ID_Numbers(I));
end loop;
for I in Accountants'Range loop
Accountants(I) := new Finance_Task(ID_Numbers(I))
end loop;
for I in Salespersons'Range loop
Salespersons(I) := new Marketing_Task(ID_Numbers(I));
end loop;
delay 15.0;
for I in Engineers'Range loop
abort Engineers(I).all;
end loop;
for I in Accountants'Range loop
abort Accountants(I).all;
end loop;
for I in Salespersons'Range loop
abort Salespersons(I).all;
end loop;
end CEOT;
this is how he explains about the necessity for requeuing rather than letting the last accountant checking in to call the CEO task on its own after the PO’s completion:
Closing the entrance door ‡115 closes the barrier Entrance=Open of entry Register and prevents other employees of the same type from overtaking the group that has been formed.
Requeue is also essential to avoid overtaking when awakening the CEO. Suppose that a group of accountants has been formed, that is, the last task of the group has completed Wait_for_Last_Member. Without requeue, the last accountant task would have to complete the protected action and then call the entry CEO.Wake(Finance) from within the sequence of statements of its task body:Finance_Room.Register;
if I_Am_Last_Member then
CEO.Wake(Finance);
end if;This task could be preempted—and a group of engineers could be formed and enqueued on CEO.Wake(Engineering)—after the protected action Finance_Room.Register completes, but before the call to CEO.Wake(Finance) is issued. With requeue, the protected action of the last accountant task is not completed until the task entry call has been made and immediately selected or enqueued. If it is enqueued, the guards on the accept statements of the selective accept prevent overtaking.
I don’t get it. The point is to avoid the CEO’s guard to be re-evaluated between the completition of the PA (protected action) and the call to its Wake entry, and a lower priority group be received before Finance, right.
But, if we accept that the normal entry call and requeue would happen at the exact same time (no issue of delay or context switch or whatnot), and the atomicity of both statements, how would the PA stop the CEO from re-evaluating its guard at any point ? CEO isn’t blocked or waiting on either the PO or the last accountant’s task. I do not understand why the window of opportunity for a preemption would stop any sooner with call to CEO inside a PO or not.
I hope I made myself clear ![]()