Skip to content

Instantly share code, notes, and snippets.

@guyzmo
Created March 10, 2017 12:52
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save guyzmo/9791c5dd1b0e6972bb8d890e96de663f to your computer and use it in GitHub Desktop.
Save guyzmo/9791c5dd1b0e6972bb8d890e96de663f to your computer and use it in GitHub Desktop.
exercise advanced checkpoint
-- Consider a workshop where several workers (tasks) assembly details of some mechanism. When each of them completes his work they put the details together. There is no store, so a worker who finished its part first must wait for others before starting another one. Putting details together is the checkpoint at which tasks synchronize themselves before going their paths apart.
--
-- The task
-- Implement checkpoint synchronization in your language.
-- Make sure that the solution is race condition-free.
-- Note that a straightforward solution based on events is exposed
-- to race condition. Let two tasks A and B need to be synchronized
-- at a checkpoint. Each signals its event (EA and EB correspondingly),
-- then waits for the AND-combination of the events (EA&EB) and resets
-- its event. Consider the following scenario: A signals EA first and
-- gets blocked waiting for EA&EB. Then B signals EB and loses the
-- processor. Then A is released (both events are signaled) and resets
-- EA. Now if B returns and enters waiting for EA&EB, it gets lost.
-- When a worker is ready it shall not continue before others finish. A typical implementation bug is when a worker is counted twice within one working cycle causing its premature completion. This happens when the quickest worker serves its cycle two times while the laziest one is lagging behind.
-- If you can, implement workers joining and leaving.
with Ada.Calendar; use Ada.Calendar;
with Ada.Numerics.Float_Random;
with Ada.Text_IO; use Ada.Text_IO;
procedure Exercise_Advanced_Checkpoint is
package FR renames Ada.Numerics.Float_Random;
No_Of_Cubicles: constant Positive := 3;
-- That many workers can work in parallel
No_Of_Workers: constant Positive := 6;
-- That many workers are potentially available
-- some will join the team when others quit the job
type Activity_Array is array(Character) of Boolean;
-- we want to know who is currently working
protected Checkpoint is
entry Deliver;
entry Join (Label : out Character; Tolerance: out Float);
entry Leave(Label : in Character);
private
Signaling : Boolean := False;
Ready_Count : Natural := 0;
Worker_Count : Natural := 0;
Unused_Label : Character := 'A';
Likelyhood_To_Quit: Float := 1.0;
Active : Activity_Array := (others => false);
entry Lodge;
end Checkpoint;
protected body Checkpoint is
entry Join (Label : out Character; Tolerance: out Float)
when not Signaling and Worker_Count < No_Of_Cubicles is
begin
Label := Unused_Label;
Active(Label):= True;
Unused_Label := Character'Succ (Unused_Label);
Worker_Count := Worker_Count + 1;
Likelyhood_To_Quit := Likelyhood_To_Quit / 2.0;
Tolerance := Likelyhood_To_Quit;
end Join;
entry Leave(Label: in Character) when not Signaling is
begin
Worker_Count := Worker_Count - 1;
Active(Label) := False;
end Leave;
entry Deliver when not Signaling is
begin
Ready_Count := Ready_Count + 1;
requeue Lodge;
end Deliver;
entry Lodge when Ready_Count = Worker_Count or Signaling is
begin
if Ready_Count = Worker_Count then
Put("---Sync Point [");
for C in Character loop
if Active(C) then
Put(C);
end if;
end loop;
Put_Line("]---");
end if;
Ready_Count := Ready_Count - 1;
Signaling := Ready_Count /= 0;
end Lodge;
end Checkpoint;
task type Worker;
task body Worker is
Dice : FR.Generator;
Label : Character;
Tolerance : Float;
Shift_End : Time := Clock + 2.0;
-- Trade unions are hard!
begin
FR.Reset (Dice);
Checkpoint.Join (Label, Tolerance);
Put_Line(Label & " joins the team");
loop
Put_Line (Label & " is working");
delay Duration (FR.Random (Dice) * 0.500);
Put_Line (Label & " is ready");
Checkpoint.Deliver;
if FR.Random(Dice) < Tolerance then
Put_Line(Label & " leaves the team");
exit;
elsif Clock >= Shift_End then
Put_Line(Label & " ends shift");
exit;
end if;
end loop;
Checkpoint.Leave(Label);
end Worker;
Set : array (1..No_Of_Workers) of Worker;
begin
null; -- Nothing to do here
end Exercise_Advanced_Checkpoint;
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment