From mboxrd@z Thu Jan 1 00:00:00 1970 X-Spam-Checker-Version: SpamAssassin 3.4.4 (2020-01-24) on polar.synack.me X-Spam-Level: X-Spam-Status: No, score=-1.9 required=5.0 tests=BAYES_00 autolearn=ham autolearn_force=no version=3.4.4 X-Google-Thread: 103376,714a9fe4718c9803,start X-Google-Attributes: gid103376,public X-Google-Language: ENGLISH,ASCII-7-bit Path: g2news1.google.com!news1.google.com!proxad.net!proxad.net!194.117.148.138.MISMATCH!pe2.news.blueyonder.co.uk!blueyonder!peer-uk.news.demon.net!kibo.news.demon.net!news.demon.co.uk!demon!not-for-mail From: "Luke A. Guest" Newsgroups: comp.lang.ada Subject: Problems with controlled types, gnatmem thinks handle is leaking memory (long) Date: Sun, 20 Feb 2005 16:30:14 +0000 Message-ID: NNTP-Posting-Host: abyss2.demon.co.uk Mime-Version: 1.0 Content-Type: text/plain; charset=ISO-8859-1 Content-Transfer-Encoding: 8bit X-Trace: news.demon.co.uk 1108917014 29192 62.49.62.197 (20 Feb 2005 16:30:14 GMT) X-Complaints-To: abuse@demon.net NNTP-Posting-Date: Sun, 20 Feb 2005 16:30:14 +0000 (UTC) User-Agent: Pan/0.14.2 (This is not a psychotic episode. It's a cleansing moment of clarity.) Xref: g2news1.google.com comp.lang.ada:8431 Date: 2005-02-20T16:30:14+00:00 List-Id: 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. 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