1

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?

hGen
  • 2,235
  • 6
  • 23
  • 43
  • You task body loop is all wrong. You are iterating over the length of the array, not over the range of its index values. – Jim Rogers Jan 06 '17 at 19:44
  • You could change the parameter to type Input_Type is <>; This will force the Input_Type to be an integer type. You will not then need to pass in a "+" function. – Jim Rogers Jan 06 '17 at 19:51
  • I understand what you mean but I thought about starting both tasks, each of them adding the current index' value to the result. By doing that I would have the complete result at the point the Loop_Var equals the length of the list and the loop would terminate as well as the task? – hGen Jan 06 '17 at 19:52
  • Wouldn't changing the input type to an integer remove the whole point of having a generic package? – hGen Jan 06 '17 at 19:53
  • Using an unprotected global variable in the two tasks can lead to a race condition where one task is incrementing the variable while the other reads from the variable. Since you defined the array type as an unconstrained type you could simply pass array slices, or the upper and lower bounds of array slices to each task. This way each task would deal with non-overlapping array values. – Jim Rogers Jan 06 '17 at 19:55
  • Remember that you can define your own integer types in Ada. My proposal would allow you to use any of those types. Note that you must ensure that the Result calculated by the tasks will always be within the range of the user-specified type. – Jim Rogers Jan 06 '17 at 19:57
  • With regard to the specific problem "Out parameter undefined" ... so define it. Set it to some appropriate value before the first time you read it. –  Jan 06 '17 at 20:07
  • Why are you using an access type to pass an array into `Parallel_Sum`? – Jacob Sparre Andersen Jan 06 '17 at 21:23

1 Answers1

4

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;
Jim Rogers
  • 4,822
  • 1
  • 11
  • 24