comp.lang.ada
 help / color / mirror / Atom feed
From: Maciej Sobczak <see.my.homepage@gmail.com>
Subject: Re: Allocators and memory reclamation
Date: Mon, 28 Jan 2008 14:27:23 -0800 (PST)
Date: 2008-01-28T14:27:23-08:00	[thread overview]
Message-ID: <5f1fb537-239c-4700-8280-560287ce5677@d21g2000prf.googlegroups.com> (raw)
In-Reply-To: lneim0ua86uj$.5q4owyndn3kj$.dlg@40tude.net

On 28 Sty, 16:15, "Dmitry A. Kazakov" <mail...@dmitry-kazakov.de>
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



  reply	other threads:[~2008-01-28 22:27 UTC|newest]

Thread overview: 15+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2008-01-28 13:49 Allocators and memory reclamation Maciej Sobczak
2008-01-28 14:52 ` gpriv
2008-01-28 14:53 ` Lucretia
2008-01-28 16:00   ` gpriv
2008-01-28 22:46     ` Alex R. Mosteo
2008-01-28 15:15 ` Dmitry A. Kazakov
2008-01-28 22:27   ` Maciej Sobczak [this message]
2008-01-28 23:54     ` Adam Beneschan
2008-01-29  9:38     ` Dmitry A. Kazakov
2008-01-28 23:07   ` Randy Brukardt
2008-01-28 22:00 ` Aurele
2008-01-29  0:41 ` Robert A Duff
2008-01-29 11:12   ` Maciej Sobczak
  -- strict thread matches above, loose matches on Subject: below --
2008-01-29 11:06 Grein, Christoph (Fa. ESG)
2008-01-29 12:50 ` Dmitry A. Kazakov
replies disabled

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