adamultitaskingpthread-barriers

Ada: behavior like pthread_barrier_wait?


I'm trying to implement a barrier in Ada which has similar functionality to C's pthread_barrier_wait. Ada 2012 has Ada.Synchronous_Barriers but that's not available on my system (gnu-gnat on debian lenny).

More specifically, how can I get all waiting tasks to be released from a barrier at ~ the same time and, ideally, have one of these tasks do something special, without using Ada 2012? Below is a very sub-optimal implementation. What might be a better approach?

with Ada.Text_IO; use Ada.Text_IO;
with Ada.Integer_Text_IO; use Ada.Integer_Text_IO;

procedure foobar is
   protected Synchronizer is
      entry Ready_For_Action; -- prepares for tasks to wait at barrier
      entry Wait_For_Release; -- barrier
      -- do work here
      entry Done;             -- signals that all tasks are done
      entry Wait_For_Others;  -- prepares for prepare to wait at barrier
   private
      ready, active: Natural := 0;  
      -- two state variables seem to be needed as entry conditions can't
      --    safely modify the condition variable as that influences wait
      --    state in other tasks
   end Synchronizer;

   NUM_OBJECTS: constant := 3;

   protected body Synchronizer is
      entry Ready_For_Action when active = 0 is
      begin
         ready := ready + 1;
      end Ready_For_Action;
      --
      entry Wait_For_Release when ready = NUM_OBJECTS is
      begin
         active := active + 1;
      end Wait_For_Release;
      --
      entry Done when active = NUM_OBJECTS is
      begin
         ready := ready - 1;
      end Done;
      --
      entry Wait_For_Others when ready = 0 is
      begin
         active := active - 1;
      end wait_for_others;
      --
   end Synchronizer;

   task type Foo(N: Natural);

   task body Foo is
      id: Natural := N;
   begin
      for iter in 1..3 loop
         Synchronizer.Ready_For_Action;
         Synchronizer.Wait_For_Release;
         -- task N doing something special
         if id = 1 then new_line; end if;
         -- do stuff here
         delay 0.1;
         put(id); new_line;
         -- re-sync
         Synchronizer.Done;
         Synchronizer.Wait_For_Others;
      end loop;
   end Foo;
   Task1: Foo(1);
   Task2: Foo(2);
   Task3: Foo(3);
begin
   Null;
end foobar;

Program output:

$ ./foobar 
  3
  1
  2

  3
  1
  2

  3
  2
  1

Solution

  • Maybe the 'count attribute on entries would be useful - is this the kind of thing you're looking for? Using task IDs to make one do something different seems sensible (or if it is sufficiently different you could just make a new task type).

    No_Of_Tasks : Natural := 3;
       --
    protected Barrier is
       entry Continue;
    private
       Released : Boolean := False;
    end Barrier
       --
    protected body Barrier is 
       entry Continue when (Released or else Continue'count = No_Of_Tasks)
          Released := Continue'count > 0; -- the last task locks the barrier again
       end Continue                       
    end Barrier