comp.lang.ada
 help / color / mirror / Atom feed
* Problems with controlled types, gnatmem thinks handle is leaking memory (long)
@ 2005-02-20 16:30 Luke A. Guest
  2005-02-20 18:09 ` Dmitry A. Kazakov
  0 siblings, 1 reply; 7+ messages in thread
From: Luke A. Guest @ 2005-02-20 16:30 UTC (permalink / raw)


Hi,

I need to implement a "smart pointer" in Ada and I did a few months ago
write one based on a few sources, but it had two levels of indirection in
it, so I looked at the Handles examples by Matthew Heaney on AdaPower.

I modified my code to have only one level, and an aliased item in my
"node" but according to gnatmem it leaks (now I remember running
gnatmem on the old one and it came out with similar results).

I tried this with GNAT-3.15p. I also copied over my test code to use with
Matthew's code, and it produces the same results, leaks. Is this a problem
with gnatmem?

Another thing is that there seems to be more calls to Adjust and Finalize
than I expect.

Can someone help me out? I'll post my code rather than Matthew's.

At the end, I've copied the output of the program, followed by an
annotated log where I've added comments and questions.

Thanks to everyone who helps,
Luke.


<code>
with Ada.Finalization;

generic

  -- Not an actual pointer, just the type that we will be creating an access type of.
  type Data_Type is limited private;
  
package Handle is

--  pragma Preelaborate;

  type Object is private;
  type Data_Access is access all Data_Type;

  function Create return Object;
  function Ref(Self : in Object) return Data_Access;
  
private

  type Handle_Type;
  type Handle_Access is access Handle_Type;

  type Object is new Ada.Finalization.Controlled with
    record
      Handle_Data : Handle_Access := null;
    end record;

  procedure Adjust(Self : in out Object);
  procedure Finalize(Self : in out Object);
  
end Handle;


with Text_IO;
with Unchecked_Deallocation;

package body Handle is

  type Handle_Type is
    record
      Data      : aliased Data_Type;
      Ref_Count : Natural;
    end record;
    
  -- Create.
  --
  -- Create a handle type with an access type.
  function Create return Object is
  
    Local : Handle_Access;
    
  begin
  
    Text_IO.Put_Line("Create: Enter");
    
    Local := new Handle_Type;
    
    Local.Ref_Count := 1;
  
    Text_IO.Put_Line("Create: Exit");
    
    return (Ada.Finalization.Controlled with Local);
    
  end Create;
  

  -- Ref.
  --
  -- Return the access type of the handle, so we can manipulate it.
  function Ref(Self : in Object) return Data_Access is
  
  begin
  
    Text_IO.Put_Line("Ref: Enter");
    
    return Self.Handle_Data.Data'Access;
    
  end Ref;
  
  
  -- Adjust
  procedure Adjust(Self : in out Object) is
  
  begin

    Text_IO.Put_Line("Adjust: Enter");
    
    Self.Handle_Data.Ref_Count := Self.Handle_Data.Ref_Count + 1;

    Text_IO.Put_Line("Adjust: Self.Handle_Data.Ref_Count = " & Natural'Image(Self.Handle_Data.Ref_Count));
      
    Text_IO.Put_Line("Adjust: Exit");
    
  end Adjust;
  
  
  -- Finalize
  procedure Finalize(Self : in out Object) is
  
    procedure Free_Handle is new Unchecked_Deallocation(Handle_Type, Handle_Access);
  
  begin

    Text_IO.Put_Line("Finalize: Enter");
    
    if Self.Handle_Data /= null then
    
      Self.Handle_Data.Ref_Count := Self.Handle_Data.Ref_Count - 1;
    
      Text_IO.Put_Line("Finalize: Self.Handle_Data.Ref_Count = " & Natural'Image(Self.Handle_Data.Ref_Count));
      
      if Self.Handle_Data.Ref_Count = 0 then
    
        Free_Handle(Self.Handle_Data);
          
        Self.Handle_Data := null;
      
        Text_IO.Put_Line("Finalize: Deallocated memory");
      
      end if;
      
    end if;
    
    Text_IO.Put_Line("Finalize: Exit");
    
  end Finalize;
  
end Handle;


with Handle;

package Data is

  type Object is
    record
      A : Integer;
      B : Float;
    end record;

  package Object_Handle is new Handle(Object);

end Data;


with Text_IO;
with Data;

procedure Test_Handle is

  package Integer_IO is new Text_IO.Integer_IO(Integer);
  package Float_IO is new Text_IO.Float_IO(Float);
  
  Test  : Data.Object_Handle.Object;-- := Data.Object_Handle.Create;
  Test2 : Data.Object_Handle.Object;
  
begin

  Test := Data.Object_Handle.Create;

  Data.Object_Handle.Ref(Test).A := 6;
  Data.Object_Handle.Ref(Test).B := 12.34;
  
  Text_IO.Put("A: ");
  Integer_IO.Put(Data.Object_Handle.Ref(Test).A);
  Text_IO.Put("  B: ");
  Float_IO.Put(Data.Object_Handle.Ref(Test).B, Fore => 2, Aft => 2, Exp => 0);
  Text_IO.New_Line;  
  
  Test2 := Test;
  
end Test_Handle;


-- Output.log
Create: Enter
Create: Exit
Adjust: Enter
Adjust: Self.Handle_Data.Ref_Count =  2
Adjust: Exit
Finalize: Enter
Finalize: Self.Handle_Data.Ref_Count =  1
Finalize: Exit
Finalize: Enter
Finalize: Exit
Adjust: Enter
Adjust: Self.Handle_Data.Ref_Count =  2
Adjust: Exit
Finalize: Enter
Finalize: Self.Handle_Data.Ref_Count =  1
Finalize: Exit
Ref: Enter
Ref: Enter
A: Ref: Enter
          6  B: Ref: Enter
12.34
Finalize: Enter
Finalize: Exit
Adjust: Enter
Adjust: Self.Handle_Data.Ref_Count =  2
Adjust: Exit
Finalize: Enter
Finalize: Self.Handle_Data.Ref_Count =  1
Finalize: Exit
Finalize: Enter
Finalize: Self.Handle_Data.Ref_Count =  0
Finalize: Deallocated memory
Finalize: Exit



-- Annotated Output.log
Create: Enter
Create: Exit

** This Adjust is the assignment on the line where the Create is called,
** inside the Create function the Ref_Count would be 1
Adjust: Enter
Adjust: Self.Handle_Data.Ref_Count =  2
Adjust: Exit

** This Finalize is obviously the local object inside Create
Finalize: Enter
Finalize: Self.Handle_Data.Ref_Count =  1
Finalize: Exit

** What is this Finalize, Adjust, Finalize?
Finalize: Enter
Finalize: Exit
Adjust: Enter
Adjust: Self.Handle_Data.Ref_Count =  2
Adjust: Exit
Finalize: Enter
Finalize: Self.Handle_Data.Ref_Count =  1
Finalize: Exit

** Now we're back at the two references, this is fine.
Ref: Enter
Ref: Enter
A: Ref: Enter
          6  B: Ref: Enter
12.34

** This Finalize is for Test2
Finalize: Enter
Finalize: Exit

** The object from Test has been copied to Test2 and has been Adjusted
Adjust: Enter
Adjust: Self.Handle_Data.Ref_Count =  2
Adjust: Exit

** This would be the Finalization of both Test & Test2 on exit
Finalize: Enter
Finalize: Self.Handle_Data.Ref_Count =  1
Finalize: Exit

** Notice this actually deletes the memory, so gnatmem is wrong here!
Finalize: Enter
Finalize: Self.Handle_Data.Ref_Count =  0
Finalize: Deallocated memory
Finalize: Exit

</code>




^ permalink raw reply	[flat|nested] 7+ messages in thread

end of thread, other threads:[~2005-02-21 20:59 UTC | newest]

Thread overview: 7+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2005-02-20 16:30 Problems with controlled types, gnatmem thinks handle is leaking memory (long) Luke A. Guest
2005-02-20 18:09 ` Dmitry A. Kazakov
2005-02-20 23:09   ` Luke A. Guest
2005-02-21  8:49     ` Dmitry A. Kazakov
2005-02-21 20:12     ` Simon Wright
2005-02-21 20:54       ` Dmitry A. Kazakov
2005-02-21 20:59         ` Robert A Duff

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox