with Text_Io, Ada.Integer_Text_Io; use Text_Io, Ada.Integer_Text_Io; with Ada.Float_Text_Io; use Ada.Float_Text_Io; with Ada.Strings.Unbounded; use Ada.Strings.Unbounded; -- In this program we implement a protected type object to coordinate -- the synchronization of multiple tasks. The underlining operating -- system's scheduling policy is unpredicatable (you don't even know what -- OS you're running on), and we cannot determine if it's possible for one -- thread to "choke" other threads by tying up the cpu. The mechanisms -- we've seen do not prevent this from happening. For example, using -- the "mutex" (semaphore) mechanism, suppose there are multiple threads -- running in the following loop: -- loop -- -- asynch code -- mutex0.lock; -- -- critica section code that must be synchronized -- mutex0.unlock; -- -- more asynch code -- end loop; -- -- There is no guarantee that one thread won't run through this loop -- several times before another thread gets a chance. This is possible -- when there's no task switching between an unlock and the next lock. -- The monitor implemented here ensures that each thread must synchronize -- with the monitor each time through the loop, and no thread will be -- released until all threads have called synch. procedure Multisynch is protected type Fairnessmonitor(Totalclients : Integer) is entry Synch; private Clients : Integer := 0; -- number of current clients Lock : Boolean := True; entry Wait; end Fairnessmonitor; protected body Fairnessmonitor is -- entry inc when True is ---- clients++; -- entry synch when (clients=totalclients) do ... -- entry reset -- clients:=0; entry Synch when True is begin -- if clients less than totalclients, block Clients := Clients + 1; if (Clients < Totalclients) then requeue Wait; else Clients := 0; end if; end Synch; entry Wait when (Clients = 0) is begin null; end Wait; -- implicitly, when Clients reach 0, threads on wait wake up -- before new calls to synch is accepted. This is guaranteed by -- Ada, but not by other synch mechanisms. When clients reach 0, -- all threads are released. end Fairnessmonitor; FM : Fairnessmonitor(3); -- shared instance of fairnessmonitor -- client task for demonstration: task type Client(Id:Integer); task body Client is I : Integer := 10; begin while (I>0) loop Put(Id,Width=>2); Put(" before synch"); New_Line; FM.Synch; Put(Id,Width=>4); Put(" after synch"); New_Line; delay Duration(2*Id); -- simulated delay I := I-1; end loop; end Client; C1 : Client(1); C2 : Client(2); C3 : Client(3); -- client 1 will delay 2 second, client 2 delays 4 secs, etc... begin null; end Multisynch; -- Why do we need wait in addition to synch? Because Synch needs to -- increase the counter before blocking. -- what's wrong with entry Synch when (Clients = Totalclients) is? -- This mechanism can be used in conjunction with semaphores to ensure -- mutual exclusion. That is, a client task can execute: -- loop -- -- async code -- FM.synch; -- mutex0.lock; -- -- critical section code -- mutex0.unlock; -- -- more async code -- end loop;