Mutexes in Ada

In the previous example we saw how to manage simple counter state using atomic operations. For more complex state we can use a mutex to safely access data across multiple tasks.

with Ada.Text_IO;
with Ada.Containers.Indefinite_Hashed_Maps;
with Ada.Strings.Hash;
with Ada.Task_Identification;
with Ada.Synchronous_Task_Control;

procedure Mutexes is
   package String_Integer_Maps is new Ada.Containers.Indefinite_Hashed_Maps
     (Key_Type        => String,
      Element_Type    => Integer,
      Hash            => Ada.Strings.Hash,
      Equivalent_Keys => "=");

   -- Container holds a map of counters; since we want to
   -- update it concurrently from multiple tasks, we
   -- add a Protected_Type to synchronize access.
   protected type Container is
      procedure Inc (Name : String);
      function Get_Counters return String_Integer_Maps.Map;
   private
      Counters : String_Integer_Maps.Map;
   end Container;

   protected body Container is
      procedure Inc (Name : String) is
      begin
         if Counters.Contains (Name) then
            Counters (Name) := Counters (Name) + 1;
         else
            Counters.Insert (Name, 1);
         end if;
      end Inc;

      function Get_Counters return String_Integer_Maps.Map is
      begin
         return Counters;
      end Get_Counters;
   end Container;

   C : Container;

   -- This task type increments a named counter in a loop
   task type Incrementor (Name : access String; N : Integer);

   task body Incrementor is
   begin
      for I in 1 .. N loop
         C.Inc (Name.all);
      end loop;
   end Incrementor;

   A1, A2 : Incrementor (new String'("a"), 10_000);
   B : Incrementor (new String'("b"), 10_000);

begin
   -- Wait for all tasks to complete
   delay 1.0;

   -- Print the final state of counters
   declare
      Final_Counters : constant String_Integer_Maps.Map := C.Get_Counters;
   begin
      for Cursor in Final_Counters.Iterate loop
         Ada.Text_IO.Put_Line 
           (String_Integer_Maps.Key (Cursor) & ": " & 
            Integer'Image (String_Integer_Maps.Element (Cursor)));
      end loop;
   end;
end Mutexes;

This Ada program demonstrates the use of a protected type, which is Ada’s equivalent to a mutex. The Container type uses a protected type to ensure thread-safe access to its internal map.

The program creates three tasks that concurrently increment counters in the shared Container. Two tasks increment the “a” counter, and one task increments the “b” counter.

To run the program, save it as mutexes.adb and compile it with an Ada compiler:

$ gnatmake mutexes.adb
$ ./mutexes
a: 20000
b: 10000

Running the program shows that the counters are updated as expected, demonstrating safe concurrent access to shared state using Ada’s protected types.

Next, we’ll look at implementing this same state management task using only tasks and other synchronization primitives in Ada.