Created
March 10, 2017 12:52
-
-
Save guyzmo/9791c5dd1b0e6972bb8d890e96de663f to your computer and use it in GitHub Desktop.
exercise advanced checkpoint
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
-- 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