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=-0.9 required=5.0 tests=BAYES_00,FORGED_GMAIL_RCVD, FREEMAIL_FROM autolearn=no autolearn_force=no version=3.4.4 X-Google-Thread: 103376,fdc38a13551814d X-Google-Attributes: gid103376,public,usenet X-Google-Language: ENGLISH,ASCII-7-bit Path: g2news1.google.com!postnews.google.com!d21g2000prf.googlegroups.com!not-for-mail From: Maciej Sobczak Newsgroups: comp.lang.ada Subject: Re: Allocators and memory reclamation Date: Mon, 28 Jan 2008 14:27:23 -0800 (PST) Organization: http://groups.google.com Message-ID: <5f1fb537-239c-4700-8280-560287ce5677@d21g2000prf.googlegroups.com> References: <4a2fe64e-ae34-4093-88ec-1dc612a9adbd@v29g2000hsf.googlegroups.com> NNTP-Posting-Host: 85.3.123.99 Mime-Version: 1.0 Content-Type: text/plain; charset=ISO-8859-1 Content-Transfer-Encoding: 7bit X-Trace: posting.google.com 1201559244 24934 127.0.0.1 (28 Jan 2008 22:27:24 GMT) X-Complaints-To: groups-abuse@google.com NNTP-Posting-Date: Mon, 28 Jan 2008 22:27:24 +0000 (UTC) Complaints-To: groups-abuse@google.com Injection-Info: d21g2000prf.googlegroups.com; posting-host=85.3.123.99; posting-account=bMuEOQoAAACUUr_ghL3RBIi5neBZ5w_S User-Agent: G2/1.0 X-HTTP-UserAgent: Mozilla/5.0 (Macintosh; U; Intel Mac OS X; en-US; rv:1.8.1.11) Gecko/20071127 Firefox/2.0.0.11,gzip(gfe),gzip(gfe) Xref: g2news1.google.com comp.lang.ada:19638 Date: 2008-01-28T14:27:23-08:00 List-Id: On 28 Sty, 16:15, "Dmitry A. Kazakov" wrote: > The language does not prescribe the effect of a call to Deallocate on the > program's environment. I'm not interested in the interaction on this level. I'm interested in the pattern of calls to Deallocate in the given program. My understanding, initially, was that Deallocate is called kind of implicitly when the storage pool is torn down together with its associated access type. It does not seem to be the case, but the storage pool itself can keep track of the allocated blocks and at the end do "something" with them. For example, the storage pool might be based on static memory block that just gives new addresses for each Allocate and have empty Deallocate - a fairly compliant implementation. When the pool goes out of scope, so does its memory. I understood that the memory that was used by the pool is deallocated even in the following simple case: type Integer_Ptr is access Integer; but the AARM claims it can be otherwise (that the memory might be bound to library-level pool). I understand that I can create my own pool with any behaviour I need. Let's try: with Ada.Text_IO; with Ada.Finalization; with System.Storage_Pools; with System.Storage_Elements; procedure Main is use System.Storage_Pools; -- used to acquire reference to the standard storage pool type Integer_Ptr is access Integer; procedure Test is type My_Type is new Ada.Finalization.Controlled with null record; procedure Finalize (T : in out My_Type) is begin Ada.Text_IO.Put_Line ("Finalize"); end Finalize; use System; type My_Pool_Type is new Root_Storage_Pool with null record; procedure Allocate (Pool : in out My_Pool_Type; Storage_Address : out Address; Size_In_Storage_Elements : in Storage_Elements.Storage_Count; Alignment : in Storage_Elements.Storage_Count); procedure Deallocate (Pool : in out My_Pool_Type; Storage_Address : in Address; Size_In_Storage_Elements : in Storage_Elements.Storage_Count; Alignment : in Storage_Elements.Storage_Count); function Storage_Size (Pool : in My_Pool_Type) return Storage_Elements.Storage_Count; procedure Allocate (Pool : in out My_Pool_Type; Storage_Address : out Address; Size_In_Storage_Elements : in Storage_Elements.Storage_Count; Alignment : in Storage_Elements.Storage_Count) is begin Allocate (Integer_Ptr'Storage_Pool, Storage_Address, Size_In_Storage_Elements, Alignment); Ada.Text_IO.Put_Line ("allocating " & Storage_Elements.Storage_Count'Image (Size_In_Storage_Elements)); end Allocate; procedure Deallocate (Pool : in out My_Pool_Type; Storage_Address : in Address; Size_In_Storage_Elements : in Storage_Elements.Storage_Count; Alignment : in Storage_Elements.Storage_Count) is begin Deallocate (Integer_Ptr'Storage_Pool, Storage_Address, Size_In_Storage_Elements, Alignment); Ada.Text_IO.Put_Line ("deallocating"); end Deallocate; function Storage_Size (Pool : in My_Pool_Type) return Storage_Elements.Storage_Count is begin return Storage_Size (Integer_Ptr'Storage_Pool); end Storage_Size; My_Pool : My_Pool_Type; type My_Type_Ptr is access My_Type; for My_Type_Ptr'Storage_Pool use My_Pool; P : My_Type_Ptr; begin Ada.Text_IO.Put_Line ("starting to allocate objects"); P := new My_Type; P := new My_Type; Ada.Text_IO.Put_Line ("finished with objects and leaving the scope"); end Test; begin Ada.Text_IO.Put_Line ("Running test"); Test; Ada.Text_IO.Put_Line ("Test finished"); end Main; (the code has lengthy lines and can get broken in your readers) In the above example I tried to create my own pool that is backed up by the standard pool acquired from some dummy access type. The new pool just forwards all primitive operations to the standard pool and prints some messages to see what's going on. It looks like I got it correctly, because it compiles and even works: Running Test Starting To Allocate Objects Allocating 12 Allocating 12 Finished With Objects And Leaving The Scope Finalize Finalize Test Finished I understand (now) that two objects are allocated with the help of my own pool. Since nobody deallocated explicitly (no Unchecked_Deallocation was used), the Deallocate procedure was not called and the memory is effectively leaked. I could have remembered the allocated addresses and deallocate the dangling blocks in the finalizer of my own pool (it is controlled after all) and this way the pool would guard the memory and prevent the leak. It didn't, so effectively there is a leak. More precisely, the allocated blocks come from the standard pool and they will be deallocated when the whole program will finish. The interesting part is that finalizers of My_Type were called and this is even before the pool gets finalized itself (I've checked it by overriding Finalize for My_Pool_Type). This is good, because after that only raw memory remained to be reclaimed. The question is - who called the finalizers of allocated objects? Any AARM paragraphs for this would be welcome. > > Another question relates to the order of finalizing objects. If the > > storage pool is torn down when the access type goes out of scope, is > > the order of finalizing objects guaranteed? > > AFAIK, it is not. Why should it be? Why not? :-) If not, then there is additional (automatic) question: how can I make it guaranteed? Since finalizers seem to be called by some magic, then I don't think there is any way for me to influence the behaviour here - at least not by providing my own pool, which does not even get a chance to have anything to say on this subject. There is a related problem which I've found while playing with the above code example: I cannot have a pointer to the standard pool. In other words, this: type Pool_Ptr is access Root_Storage_Pool'Class; PP : Pool_Ptr := Integer'Storage_Pool'Access; is not possible, because the standard storage pool is not aliased. How can I get a reference to the standard pool so that I don't have to repeat the "Integer'Storage_Pool" million times? What about this: P : Root_Storage_Pool'Class := Integer_Ptr'Storage_Pool; I expect that P will be a reference to the standard pool, not its copy (it cannot be a copy, because Root_Storage_Pool is limited). Is this reasoning correct? Last but not least: does AARM 13.11/20 mean that the above program is incorrect? How to implement it correctly, then? -- Maciej Sobczak * www.msobczak.com * www.inspirel.com