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; -- A mutex variable is one that can be tested and set at the same time. -- That is, the code -- if (var = true) then var:=false; endif; -- should be executed without interruption. -- If you've done thread programming with "POSIX" threads in C on Unix -- systems. You might have encountered such variables, which are called -- pthread_mutex_t in fact. These objects offer two operations: lock and -- unlock. lock will in one lock step sees if the variable is free, and -- if so set it to not free. If the variable is not free, then the client -- calling lock will be suspended. The other operation, unlock, will set -- the variable to being free again, and wakes up any waiting threads. -- Two threads that share a critical section can use mutex object for -- synchrnonization. Each will call lock before entering the C.S. and -- call unlock afterwards. procedure testandset is X : Integer := 0; -- shared variable, need to be protected. task type Mutexvar is entry lock; entry unlock; end Mutexvar; task body Mutexvar is free : Boolean := True; begin loop select when (free) => accept Lock do Free := False; end Lock; or accept Unlock do Free := True; end Unlock; end select; end loop; end Mutexvar; -- The way this construct works is, when the select is called, only -- the cases where the "when" boolean condition is true is considered. -- So if another task calls lock, but free is false, then the call -- WILL NOT BE ACCEPTED. The calling task therefore remain suspended, -- at least until the server loops around. -- Quiz: what's the difference between checking the boolean above the accept -- and inside the body of the accept? That is, what if I modified the -- above code to say: -- select -- accept Lock do -- if (free) then free:=false; end if; -- end Lock; -- ... -- Answer: it won't work because the accept call will still be immediately -- processed. The calling task would not block-and-wait. The "when" -- version of accept, within a select, is therefore a very powerful -- combination of mechanisms for controlling threads. -------------------------- test task Mutex0 : Mutexvar; -- shared instance of mutexvar task type CR(Id : Integer); -- critical region task task body CR is begin loop Put(Id,Width=>2); Put("entering critical section"); New_Line; Mutex0.Lock; Put(Id,Width=>2); Put("entered critical section"); New_Line; X := X+1; Put(Id,Width=>2); Put("leaving critical section"); New_Line; Mutex0.Unlock; end loop; end CR; TaskA : CR(1); TaskB : CR(2); begin -- main procedure Put("nothing for main procedure to do in this program."); New_Line; end testandset;