Search code examples
ada

Ada.Containers.Formal_Indefinite_Vectors memory leak


I'm using an instance of package Ada.Containers.Formal_Indefinite_Vectors to store two kinds of polimorphic objects.

I have the following package where I instantiate the container:

with Interfaces.C;
with Root.Classes.Concrete_1;
with Root.Classes.Concrete_2;

package Root.Vectors is

  type vector_t is tagged limited private;
  subtype vectorIndex_t is Interfaces.C.int range 1 .. Interfaces.C.int'Last;

  procedure pAppend (this : in out vector_t;
                     New_Item : Root.Classes.Parent_t'Class);

  procedure pClear (this : in out vector_t);

private
  --TODO: I have to define it correctly, it could be the problem
  function "=" (Left, Right : Root.Classes.Parent_t'Class)
                return Boolean is (True); 

  MaxSize : constant Natural := Natural'Max 
    (Root.Classes.Concrete_1.Concrete_1_t'Size,
     Root.Classes.Concrete_2.Concrete_2_t'Size);

  package polimorphicVector_pck is new
    Ada.Containers.Formal_Indefinite_Vectors
      (Index_Type                   => vectorIndex_t,
       Element_Type                 => Root.Classes.Parent_t'Class,
       "="                          => "=",
       Max_Size_In_Storage_Elements => MaxSize,
       Bounded                      => True);

  type vector_t is tagged limited
    record
      v : polimorphicVector_pck.Vector (Capacity => 1000); --TODO: magic number
    end record;

end Root.Vectors;
package body Root.Vectors is

  procedure pAppend (this : in out vector_t;
                     New_Item : Root.Classes.Parent_t'Class) is

  begin
    polimorphicVector_pck.Append (Container => this.v,
                                  New_Item  => New_Item);
  end pAppend;

  procedure pClear (this : in out vector_t) is

  begin
    polimorphicVector_pck.Clear (Container => this.v);
  end pClear;

end Root.Vectors;

Then I test it with the following main:

with Root.Classes.Concrete_1;
with Root.Vectors;

procedure Main is

  aVector : Root.Vectors.Vector_t;

begin

  for idx in Natural range 1 .. 1000 loop

    declare
      --Concrete_1_t is an unconstrained tagged type that requires constructor
      obj : Root.Classes.Concrete_1.Concrete_1_t :=
        Root.Classes.Concrete_1.fConstructor (Argument => idx);
    begin
      aVector.pAppend (New_Item => obj);
    end;

  end loop;

  -- Trying to clear the vector after all appends; this does not seem to work
  aVector.pClear;

end Main;

Then, I have used gnatmem to check if I have any memory leak, showing the following:

Global information
------------------
   Total number of allocations        :779831
   Total number of deallocations      :5080
   Final Water Mark (non freed mem)   : 26.71 Megabytes
   High Water Mark                    : 26.71 Megabytes

Allocation Root # 1
-------------------
 Number of non freed allocations    :764550
 Final Water Mark (non freed mem)   : 17.50 Megabytes
 High Water Mark                    : 17.50 Megabytes
 Backtrace                          :
   ??:0 ??

Allocation Root # 2
-------------------
 Number of non freed allocations    :5100
 Final Water Mark (non freed mem)   : 119.53 Kilobytes
 High Water Mark                    : 119.53 Kilobytes
 Backtrace                          :
   a-cfinve.adb:220 root.vectors.polimorphicVector_pck.copy

Allocation Root # 3
-------------------
 Number of non freed allocations    :3390
 Final Water Mark (non freed mem)   : 7.78 Megabytes
 High Water Mark                    : 7.78 Megabytes
 Backtrace                          :
   a-cfinve.adb:466 root.vectors.polimorphicVector_pck.find_index

Allocation Root # 4
-------------------
 Number of non freed allocations    :1710
 Final Water Mark (non freed mem)   : 1.32 Megabytes
 High Water Mark                    : 1.32 Megabytes
 Backtrace                          :
   a-cfinve.adb:219 root.vectors.polimorphicVector_pck.copy

Allocation Root # 5
-------------------
 Number of non freed allocations    :   1
 Final Water Mark (non freed mem)   : 8 Bytes
 High Water Mark                    : 8 Bytes
 Backtrace                          :
   ??:0 system.stream_attributes.xdr.i_ssi

Why is it leaking? It can be due to the "=" that always return True?


Solution

  • You instantiate the container as Bounded:

    Bounded => True);

    Bounded containers are allocated on stack. This is also documented in the spec-file:

           Bounded : Boolean := True;
           --  If True, the containers are bounded; the initial capacity is the maximum
           --  size, and heap allocation will be avoided. If False, the containers can
           --  grow via heap allocation.