comp.lang.ada
 help / color / mirror / Atom feed
* Finalization and class-wide views
@ 2016-06-15 23:50 Alejandro R. Mosteo
  2016-06-16  7:34 ` Dmitry A. Kazakov
  2016-06-16 11:10 ` Alejandro R. Mosteo
  0 siblings, 2 replies; 5+ messages in thread
From: Alejandro R. Mosteo @ 2016-06-15 23:50 UTC (permalink / raw)


Dear all,

while pursuing a memory leak I've found a subtle difference in 
Controlled behavior in the latest Gnat GPL when using operators vs 
regular functions. Now, this could well be a misunderstanding on my part 
on how these things work, but the implications for a real program I'm 
working on are so that I can't really move forward without properly 
understanding it. I've tried the ARM but couldn't arrive at a firm 
conclusion.

In my program, the outcome is that, for some class-wide types, 
finalization of its controlled members is delayed past their proper 
lifetime until program termination, generating a memory leak. I have 
attempted to reproduce it in a self-contained case, but the result is 
not identical. Here, it seems a finalization is missing altogether (I 
know by now you're skeptical of me ;-)

See below for the output and explanation, if you prefer, before taking a 
look at the code (compiled with -Og):

with Ada.Finalization; use Ada.Finalization;
with Ada.Text_Io; use Ada.Text_Io;

procedure Finalize_Leak is
    generic
    package P is

       type One is interface;

       type Managed is new Controlled with record
          X : Integer := 1;
       end record;
       overriding procedure Finalize (M : in out Managed);

       type Two is new One with record
          M : Managed;
       end record;

    end P;

    package body P is
       overriding procedure Finalize (M : in out Managed) is
          pragma Unreferenced (M);
       begin
          Put_Line ("finalize M");
          M.X := 0;
       end Finalize;
    end P;

    package PP is new P; use PP;

    function Pass  (X : Two'Class) return One'Class is (X);
    function "not" (X : Two'Class) return One'Class is (X);

    A : Two;
begin
    declare
       B : constant One'Class := A;
    begin
       Put_Line ("---8<---");
       Put_Line (Two'Class (B).M.X'Img);
    end;
    Put_Line ("--->8---");

    New_Line;
    declare
       B : constant One'Class := Pass (A);
    begin
       Put_Line ("---8<---");
       Put_Line (Two'Class (B).M.X'Img);
    end;
    Put_Line ("--->8---");

    New_Line;
    A.M.X := 2;
    declare
       B : One'Class := not A;
    begin
       Put_Line ("---8<---");
       Put_Line (Two'Class (B).M.X'Img);
    end;
    Put_Line ("--->8---");

    New_Line; Put_Line ("Now A is going to finalize");
end Finalize_Leak;


*So.*

I would expect the three declare blocks to produce more or less (besides 
temporaries) the same output: One "finalize" printed between each 
matching scissor pair, corresponding to B going out of scope. In other 
words, the conversion to an ancestor does not cause the finalization of 
the full view type (this was my belief until now, otherwise how could 
containers of classwide types work?). Instead, the output I get is:

---8<---
  1
finalize M
--->8---

finalize M
---8<---
  1
finalize M
--->8---

finalize M
---8<---
  2
--->8---

Now A is going to finalize
finalize M

So the first declare is straightforward, the second one seems to involve 
a temporary somewhere, and (here is my hope that this is not normal) the 
last one, where seemingly B.M finalization seems missing. The integer 
tells me that the previous finalization is of a temporary.

I hope some of you experts can shed some light. To summarize: do 
class-wide conversions affect finalization? Do you smell something fishy 
in this case?

Thanks in advance,
Alex.

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

* Re: Finalization and class-wide views
  2016-06-15 23:50 Finalization and class-wide views Alejandro R. Mosteo
@ 2016-06-16  7:34 ` Dmitry A. Kazakov
  2016-06-16  9:11   ` Alejandro R. Mosteo
  2016-06-16 11:10 ` Alejandro R. Mosteo
  1 sibling, 1 reply; 5+ messages in thread
From: Dmitry A. Kazakov @ 2016-06-16  7:34 UTC (permalink / raw)


On 16/06/2016 01:50, Alejandro R. Mosteo wrote:

> So the first declare is straightforward, the second one seems to involve
> a temporary somewhere, and (here is my hope that this is not normal) the
> last one, where seemingly B.M finalization seems missing. The integer
> tells me that the previous finalization is of a temporary.

In my case GNAT optimizes all three B's away.

> I hope some of you experts can shed some light. To summarize: do
> class-wide conversions affect finalization?

No. A view conversion may not affect the object. The only question is 
what object.

> Do you smell something fishy
> in this case?

Nothing except for broken semantics of assignment, at least in the third 
case. Maybe it is broken per design, let's hear what Ada lawyers say.

-- 
Regards,
Dmitry A. Kazakov
http://www.dmitry-kazakov.de

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

* Re: Finalization and class-wide views
  2016-06-16  7:34 ` Dmitry A. Kazakov
@ 2016-06-16  9:11   ` Alejandro R. Mosteo
  0 siblings, 0 replies; 5+ messages in thread
From: Alejandro R. Mosteo @ 2016-06-16  9:11 UTC (permalink / raw)


On 16/06/16 09:34, Dmitry A. Kazakov wrote:
> On 16/06/2016 01:50, Alejandro R. Mosteo wrote:
>
>> So the first declare is straightforward, the second one seems to involve
>> a temporary somewhere, and (here is my hope that this is not normal) the
>> last one, where seemingly B.M finalization seems missing. The integer
>> tells me that the previous finalization is of a temporary.
>
> In my case GNAT optimizes all three B's away.
>
>> I hope some of you experts can shed some light. To summarize: do
>> class-wide conversions affect finalization?
>
> No. A view conversion may not affect the object. The only question is
> what object.

Thanks for taking the time of going over my example and confirming this 
point.

>> Do you smell something fishy
>> in this case?
>
> Nothing except for broken semantics of assignment, at least in the third
> case. Maybe it is broken per design, let's hear what Ada lawyers say.

Ok, thanks.
Alex.


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

* Re: Finalization and class-wide views
  2016-06-15 23:50 Finalization and class-wide views Alejandro R. Mosteo
  2016-06-16  7:34 ` Dmitry A. Kazakov
@ 2016-06-16 11:10 ` Alejandro R. Mosteo
  2016-06-17 10:22   ` Alejandro R. Mosteo
  1 sibling, 1 reply; 5+ messages in thread
From: Alejandro R. Mosteo @ 2016-06-16 11:10 UTC (permalink / raw)


As a follow-up I've modified the example with actual memory 
allocation/deallocation, and valgrind seems to confirm the brokenness of 
the third assignment in the original example.

valgrind --leak-check=full output:

==21058== HEAP SUMMARY:
==21058==     in use at exit: 2,664 bytes in 666 blocks
==21058==   total heap usage: 2,670 allocs, 2,004 frees, 10,680 bytes 
allocated
==21058==
==21058== 2,664 bytes in 666 blocks are definitely lost in loss record 1 
of 1
==21058==    at 0x4C2DB8F: malloc (in 
/usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==21058==    by 0x41623C: __gnat_malloc (in 
/home/jano/local/rxada/obj/finalize_leak)
==21058==    by 0x402B0A: finalize_leak__pp__adjust.3673 
(finalize_leak.adb:30)
==21058==    by 0x402B4D: finalize_leak__pp__twoDA.3946 
(finalize_leak.adb:32)
==21058==    by 0x404D96: _ada_finalize_leak (finalize_leak.adb:67)
==21058==    by 0x405887: main (b__finalize_leak.adb:241)
==21058==
==21058== LEAK SUMMARY:
==21058==    definitely lost: 2,664 bytes in 666 blocks
==21058==    indirectly lost: 0 bytes in 0 blocks
==21058==      possibly lost: 0 bytes in 0 blocks
==21058==    still reachable: 0 bytes in 0 blocks
==21058==         suppressed: 0 bytes in 0 blocks
==21058==
==21058== For counts of detected and suppressed errors, rerun with: -v
==21058== ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 0 from 0)

With this I feel confident enough to file a bug report. Here is the 
modified program. The type Managed now holds a heap-allocated integer.

with Ada.Finalization; use Ada.Finalization;
with Ada.Text_Io; use Ada.Text_Io;
with Ada.Unchecked_Deallocation;

procedure Finalize_Leak is
    generic
    package P is

       type One is interface;

       type Int_Access is access Integer;

       type Managed is new Controlled with record
          X : Int_Access;
       end record;
       overriding procedure Adjust (M : in out Managed);
       overriding procedure Finalize (M : in out Managed);
       function Build (I : Integer) return Managed;

       type Two is new One with record
          M : Managed := Build (1);
       end record;

    end P;

    package body P is
       overriding procedure Adjust (M : in out Managed) is
       begin
          if M.X /= null then
             M.X := new Integer'(M.X.all);
          end if;
       end Adjust;

       overriding procedure Finalize (M : in out Managed) is
          procedure Free is
            new Ada.Unchecked_Deallocation (Integer, Int_Access);
       begin
          if M.X /= null then
             Free (M.X);
             Put_Line ("finalize M with free");
          else
             Put_Line ("finalize M");
          end if;
       end Finalize;

       function Build (I : Integer) return Managed
       is (Managed'(Controlled with X => new Integer'(I)));
    end P;

    package PP is new P; use PP;

    function Pass (X : Two'Class) return One'Class is (X);
    function "not" (X : Two'Class) return One'Class is (X);

    A : Two;
begin
    A.M := Build (1);

    for I in 1 .. 666 loop
       Put_Line ("----------------------------");
       declare
          B : One'Class := Pass (A); -- This is properly finalized
       begin
          Put_Line ("......");
       end;
       Put_Line ("......");

       declare
          B : One'Class := not A; -- This is not
       begin
          Put_Line ("---8<---");
       end;
       Put_Line ("--->8---");
    end loop;

    New_Line; Put_Line ("Now A is going to finalize");
end Finalize_Leak;

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

* Re: Finalization and class-wide views
  2016-06-16 11:10 ` Alejandro R. Mosteo
@ 2016-06-17 10:22   ` Alejandro R. Mosteo
  0 siblings, 0 replies; 5+ messages in thread
From: Alejandro R. Mosteo @ 2016-06-17 10:22 UTC (permalink / raw)


On 16/06/16 13:10, Alejandro R. Mosteo wrote:
> With this I feel confident enough to file a bug report.

To close the matter, AdaCore has acknowledged the bug.


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

end of thread, other threads:[~2016-06-17 10:22 UTC | newest]

Thread overview: 5+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2016-06-15 23:50 Finalization and class-wide views Alejandro R. Mosteo
2016-06-16  7:34 ` Dmitry A. Kazakov
2016-06-16  9:11   ` Alejandro R. Mosteo
2016-06-16 11:10 ` Alejandro R. Mosteo
2016-06-17 10:22   ` Alejandro R. Mosteo

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