www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - Class deallocator not being called -- bug or oversight?

reply "Jarrett Billingsley" <kb3ctd2 yahoo.com> writes:
This code:

import tango.stdc.stdlib: cmalloc = malloc, cfree = free;

class A
{
    new(size_t sz)
    {
        Stdout.formatln("A new.");
        void* p = cmalloc(sz);

        if(p is null)
            throw new OutOfMemoryException(__FILE__, __LINE__);

        return p;
    }

    delete(void* p)
    {
        Stdout.formatln("A delete.");

        if(p is null)
            return;

        cfree(p);
    }

    this()
    {
        Stdout.formatln("A ctor.");
    }

    ~this()
    {
        Stdout.formatln("A dtor.");
    }
}

void main()
{
    A a = new A();
    delete a;
}

When compiled with at least DMD 1.018 (also happens with 1.021) and run, 
gives the output:

A new.
A ctor.
A dtor.

Where's the "A delete."?  There is none.  I've looked in the spec to see if 
I'm doing it right, and it sure looks like it.

If A is made a struct (and the ctor and dtor are removed), I get:

A new.
A delete.

As expected.

Something weird is going on. 
Sep 24 2007
next sibling parent reply "Jarrett Billingsley" <kb3ctd2 yahoo.com> writes:
"Jarrett Billingsley" <kb3ctd2 yahoo.com> wrote in message 
news:fd8f6o$126d$1 digitalmars.com...
 Something weird is going on.

Some more: Calling GC.collect() changes nothing. Adding the newly-allocated memory with GC.addRange() changes nothing, even if I then call GC.collect. The spec says that "When a delete expression .. is executed, and f is a reference to a class instance that has a deallocator, the deallocator is called with a pointer to the class instance after the destructor (if any) for the class is called." This is not happening, so I'm guessing this is a bug.
Sep 24 2007
parent "Jarrett Billingsley" <kb3ctd2 yahoo.com> writes:
"Jarrett Billingsley" <kb3ctd2 yahoo.com> wrote in message 
news:fd8gau$13qi$1 digitalmars.com...
 "Jarrett Billingsley" <kb3ctd2 yahoo.com> wrote in message 
 news:fd8f6o$126d$1 digitalmars.com...
 Something weird is going on.

Some more:

And: the earliest I can test it with is 1.010 (any earlier and I get an ICE, presumably when compiling Tango), and the problem still exists then.
Sep 24 2007
prev sibling parent reply Sean Kelly <sean f4.ca> writes:
Jarrett Billingsley wrote:
 This code:
 
 import tango.stdc.stdlib: cmalloc = malloc, cfree = free;
 
 class A
 {
     new(size_t sz)
     {
         Stdout.formatln("A new.");
         void* p = cmalloc(sz);
 
         if(p is null)
             throw new OutOfMemoryException(__FILE__, __LINE__);
 
         return p;
     }
 
     delete(void* p)
     {
         Stdout.formatln("A delete.");
 
         if(p is null)
             return;
 
         cfree(p);
     }
 
     this()
     {
         Stdout.formatln("A ctor.");
     }
 
     ~this()
     {
         Stdout.formatln("A dtor.");
     }
 }
 
 void main()
 {
     A a = new A();
     delete a;
 }
 
 When compiled with at least DMD 1.018 (also happens with 1.021) and run, 
 gives the output:
 
 A new.
 A ctor.
 A dtor.
 
 Where's the "A delete."?  There is none.  I've looked in the spec to see if 
 I'm doing it right, and it sure looks like it.
 
 If A is made a struct (and the ctor and dtor are removed), I get:
 
 A new.
 A delete.
 
 As expected.
 
 Something weird is going on. 

You're right. This was a bug in the Tango runtime. It's now fixed in the Tango trunk for both DMD and GDC. Oh for what it's worth, I can't figure out where Phobos is calling class deallocators--the block I'd expect to be used in internal/gc/gc.d is commented out. I don't suppose someone could test this with Phobos to see if it works? Sean
Sep 24 2007
next sibling parent reply Regan Heath <regan netmail.co.nz> writes:
Sean Kelly wrote:
 Oh for what it's worth, I can't figure out where Phobos is calling class 
 deallocators--the block I'd expect to be used in internal/gc/gc.d is 
 commented out.  I don't suppose someone could test this with Phobos to 
 see if it works?

Works on Windows with DMD 2.004. import std.stdio; import std.c.stdlib; class A { new(size_t sz) { writefln("A new."); void* p = malloc(sz); return p; } delete(void* p) { writefln("A delete."); if(p is null) return; free(p); } this() { writefln("A ctor."); } ~this() { writefln("A dtor."); } } void main() { A a = new A(); delete a; } Regan
Sep 24 2007
parent reply Sean Kelly <sean f4.ca> writes:
Regan Heath wrote:
 Sean Kelly wrote:
 Oh for what it's worth, I can't figure out where Phobos is calling 
 class deallocators--the block I'd expect to be used in 
 internal/gc/gc.d is commented out.  I don't suppose someone could test 
 this with Phobos to see if it works?

Works on Windows with DMD 2.004.

Darnit, version(0) will get me every time. That code is executed but it's the same as the old buggy Tango impl. Ah well, as long as it works in Phobos... Sean
Sep 24 2007
parent reply Frits van Bommel <fvbommel REMwOVExCAPSs.nl> writes:
Sean Kelly wrote:
 Regan Heath wrote:
 Sean Kelly wrote:
 Oh for what it's worth, I can't figure out where Phobos is calling 
 class deallocators--the block I'd expect to be used in 
 internal/gc/gc.d is commented out.  I don't suppose someone could 
 test this with Phobos to see if it works?

Works on Windows with DMD 2.004.

Darnit, version(0) will get me every time. That code is executed but it's the same as the old buggy Tango impl. Ah well, as long as it works in Phobos...

Actually, there's an important difference: In Phobos the finalizer hander is only called from _d_delclass directly when a deallocator is present, necessarily *after* the classinfo reference has been loaded from the vtable since the deallocator is stored in the classinfo. (If no deallocator is present, the finalizer handler is called from somewhere inside _gc.free()). In Tango, on the other hand, you always call the finalizer handler from _d_delclass. You used to do this *before* loading the classinfo reference from the vtable. Now for the important bit: in both libs, the finalizer handler _nulls the vptr_. That means that in Tango it used to do so before the lookup of the classinfo in the vtable, while in Phobos the classinfo lookup was always performed before the finalizer handler is called.
Sep 24 2007
parent Sean Kelly <sean f4.ca> writes:
Frits van Bommel wrote:
 Sean Kelly wrote:
 Regan Heath wrote:
 Sean Kelly wrote:
 Oh for what it's worth, I can't figure out where Phobos is calling 
 class deallocators--the block I'd expect to be used in 
 internal/gc/gc.d is commented out.  I don't suppose someone could 
 test this with Phobos to see if it works?

Works on Windows with DMD 2.004.

Darnit, version(0) will get me every time. That code is executed but it's the same as the old buggy Tango impl. Ah well, as long as it works in Phobos...

Actually, there's an important difference: In Phobos the finalizer hander is only called from _d_delclass directly when a deallocator is present, necessarily *after* the classinfo reference has been loaded from the vtable since the deallocator is stored in the classinfo. (If no deallocator is present, the finalizer handler is called from somewhere inside _gc.free()). In Tango, on the other hand, you always call the finalizer handler from _d_delclass. You used to do this *before* loading the classinfo reference from the vtable.

You're right of course. I should really learn to not post while I'm still waking up :-)
 Now for the important bit: in both libs, the finalizer handler _nulls 
 the vptr_. That means that in Tango it used to do so before the lookup 
 of the classinfo in the vtable, while in Phobos the classinfo lookup was 
 always performed before the finalizer handler is called.

Yup. In hindsight, the side-effect makes sense, but I'd overlooked it when changing the behavior of _d_delclass way back when. Sean
Sep 24 2007
prev sibling next sibling parent Frits van Bommel <fvbommel REMwOVExCAPSs.nl> writes:
Sean Kelly wrote:
 Oh for what it's worth, I can't figure out where Phobos is calling class 
 deallocators--the block I'd expect to be used in internal/gc/gc.d is 
 commented out.

Actually, it's not commented out; it's in a version(0) {} block (at least in my copy of Phobos) . A "version(0)" doesn't have any effect though. From the spec: --- The VersionCondition is satisfied if the Integer is greater than or equal to the current version level, or if Identifier matches a version identifier. --- Since 0 seems to be the minimum level (DMD won't let me specify a negative "version level") the code will always be executed (similar to "version(all)").
Sep 24 2007
prev sibling parent "Jarrett Billingsley" <kb3ctd2 yahoo.com> writes:
"Sean Kelly" <sean f4.ca> wrote in message 
news:fd8m9v$1cgh$1 digitalmars.com...
 You're right.  This was a bug in the Tango runtime.  It's now fixed in the 
 Tango trunk for both DMD and GDC.

Sweet, thanks.
Sep 24 2007