Out parameter undefined

158 Views Asked by At

I'm currently stuck in creating two tasks inside of a procedure adding numbers of an array passed to the respective procedure.

My generic package looks like this:

generic
    type Item_Type is private;
    with function "+"(Left: Item_Type; Right: Item_Type) return Item_Type;
package Parallel_Algorithms is

    type Array_Type is array(Natural range <>) of Item_Type;
    type Array_Access_Type is access all Array_Type;

   procedure Parallel_Sum(Input: Array_Access_Type; Result: out Item_Type);

end Parallel_Algorithms;

I implemented the Parallel_Sum Method the following way, being aware that the implementation is not perfect, nor thread safe.

procedure Parallel_Sum(Input: Array_Access_Type; Result: out Item_Type) is

    Loop_Var: Integer:= 0;

    task type T;
    Task1, Task2 : T;

    task body T is
        begin
            while Loop_Var < Input'Length loop
                Result := Result + Input(Loop_Var);
                Loop_Var := Loop_Var + 1;
        end loop;
    end T;

begin
  -- Result := Temp;
end Parallel_Sum;

If I now run my main program the output of Result always ends up being something like 1918988326. Considering the elements inside of my array (1,2,3,4) that result is obviously wrong.

I read in another post that non altering an out type may result in undefined behaviour of the respective variable.

What would be the proper way to get the 'real' Result?

1

There are 1 best solutions below

0
On

Upon looking at the problem more closely I see there are several issues to overcome. The tasks must accumulate their own totals, then those totals must be combined. Adding totals to an unprotected Result variable will produce a race condition which will result in undefined results.

Following is my approach to the problem.

------------------------------------------------------------------
-- Parallel Addition of Array Elements --
------------------------------------------------------------------
generic
   type Element_Type is range <>;
package Parallel_Addition is
   type Array_Type is array(Natural range <>) of Element_Type;
   type Array_Access is access all Array_Type;

   task type Adder is
      Entry Set_Slice(Low, High : in Natural; 
                      Item : in not null Array_Access);
   end Adder;

   protected Result is
      procedure Accumulate(Item : in Element_Type);
      function Report return Element_Type;
   private
      Sum : Integer := 0;
   end Result;

end Parallel_Addition;
package body Parallel_Addition is

   -----------
   -- Adder --
   -----------

   task body Adder is
      My_Array        : Array_Access;
      Id_Low, Id_High : Natural;
      Sum             : Integer := 0;
   begin
      accept Set_Slice(Low, High : in Natural; 
                       Item : in not null Array_Access) do
         Id_Low := Low;
         Id_High := High;
         My_Array := Item;
      end Set_Slice;
      for I in Id_Low..Id_High loop
         Sum := Sum + Integer(My_Array(I));
      end loop;
      Result.Accumulate(Element_Type(Sum));
   end Adder;

   ------------
   -- Result --
   ------------

   protected body Result is

      ----------------
      -- Accumulate --
      ----------------

      procedure Accumulate (Item : in Element_Type) is
      begin
         Sum := Sum + Integer(Item);
      end Accumulate;

      ------------
      -- Report --
      ------------

      function Report return Element_Type is
      begin
         return Element_Type(Sum);
      end Report;

   end Result;

end Parallel_Addition;
------------------------------------------------------------------
-- Parallel_Addition Test --
------------------------------------------------------------------
with Ada.Text_IO; use Ada.Text_IO;
with Parallel_Addition;

procedure PA_Test is
   package adders is new Parallel_Addition(Natural);
   use adders;
   Data : aliased Array_Type := (1,2,3,4,5,6,7,8,9,10);
   T1, T2 : Adder;
begin
   T1.Set_Slice(Low => 0, High => 4, Item => Data'Access);
   T2.Set_Slice(Low => 5, High => 9, Item => Data'Access);
   loop
      if T1'Terminated and then T2'Terminated then
         exit;
      end if;
   end loop;
   put_Line("The sum is " & Integer'Image(Result.Report));
end PA_Test;