Jump to page: 1 2
Thread overview
Destructor order
Oct 22, 2014
eles
Oct 22, 2014
eles
Oct 22, 2014
Regan Heath
Oct 22, 2014
eles
Oct 22, 2014
eles
Oct 22, 2014
anonymous
Oct 22, 2014
ketmar
Oct 22, 2014
ketmar
Oct 22, 2014
eles
Oct 22, 2014
eles
Oct 22, 2014
Ali Çehreli
Oct 23, 2014
Marc Schütz
Oct 23, 2014
Marco Leise
Oct 23, 2014
Marc Schütz
Oct 22, 2014
Ali Çehreli
Oct 22, 2014
eles
Oct 22, 2014
Ali Çehreli
October 22, 2014
C++ versions:

	{ //displays ~C~B~A
	    A foo;
	    B bar;
	    C *caz = new C();
	    delete caz;
	}

	std::cout << std::endl;

	{ //displays ~C~B~A
	    std::unique_ptr<A> foo = std::make_unique<A>();
	    std::unique_ptr<B> bar = std::make_unique<B>();
	    C *caz = new C();
	    delete caz;
	}


D version:

	{ //displays ~A~B~C
	    A foo = scoped!(A)();
	    B bar = scoped!(B)();
	    C caz = new C();
	    destroy(caz);
	}

Why the objects are not destroyed in the inverse order of their creation? Case in point, destroying foo releases a lock for bar and caz.
October 22, 2014
On Wednesday, 22 October 2014 at 15:45:02 UTC, eles wrote:

D version with structs:

	{ //display ~C~B~A
	    A foo;
	    B bar;
	    C *caz = new C();
	    delete caz;
	}

as expected.
October 22, 2014
On Wed, 22 Oct 2014 16:49:20 +0100, eles <eles@eles.com> wrote:

> On Wednesday, 22 October 2014 at 15:45:02 UTC, eles wrote:
>
> D version with structs:
>
> 	{ //display ~C~B~A
> 	    A foo;
> 	    B bar;
> 	    C *caz = new C();
> 	    delete caz;
> 	}
>
> as expected.

Structs are special, compare:
http://dlang.org/struct.html#struct-destructor

with:
http://dlang.org/class.html#destructors

Specifically:
"The garbage collector is not guaranteed to run the destructor for all unreferenced objects. Furthermore, the order in which the garbage collector calls destructors for unreference objects is not specified. This means that when the garbage collector calls a destructor for an object of a class that has members that are references to garbage collected objects, those references may no longer be valid. This means that destructors cannot reference sub objects. This rule does not apply to auto objects or objects deleted with the DeleteExpression, as the destructor is not being run by the garbage collector, meaning all references are valid."

Regan

-- 
Using Opera's revolutionary email client: http://www.opera.com/mail/
October 22, 2014
On Wednesday, 22 October 2014 at 16:55:41 UTC, Regan Heath wrote:
> "The garbage collector is not guaranteed to run the destructor for all unreferenced objects. Furthermore, the order in which the garbage collector calls destructors for unreference objects is not specified. This means that when the garbage collector calls a destructor for an object of a class that has members that are references to garbage collected objects, those references may no longer be valid. This means that destructors cannot reference sub objects. This rule does not apply to auto objects or objects deleted with the DeleteExpression, as the destructor is not being run by the garbage collector, meaning all references are valid."

But Scoped!A is on the stack?

So why wasn't the eles' destructor order in reverse if Scoped is a struct and calls explicit destroy(B) then destroy(A)?

https://github.com/D-Programming-Language/phobos/blob/master/std/typecons.d#L4628

        ~this()
        {
            .destroy(Scoped_payload);
        }
October 22, 2014
On Wednesday, 22 October 2014 at 17:13:35 UTC, Ola Fosheim Grøstad wrote:
> On Wednesday, 22 October 2014 at 16:55:41 UTC, Regan Heath wrote:

> But Scoped!A is on the stack?

Exactly. From Ali's book:

"scoped() wraps the class object inside a struct and the destructor of that struct object destroys the class object when itself goes out of scope. "

Destroy! ;) http://ddili.org/ders/d.en/destroy.html
October 22, 2014
On Wednesday, 22 October 2014 at 17:13:35 UTC, Ola Fosheim Grøstad wrote:
> On Wednesday, 22 October 2014 at 16:55:41 UTC, Regan Heath

> So why wasn't the eles' destructor order in reverse if Scoped is a struct and calls explicit destroy(B) then destroy(A)?

Maybe it's the writeln() inside the destructor which behaves badly, albeit the same should have happened for structs too.
October 22, 2014
On Wednesday, 22 October 2014 at 15:45:02 UTC, eles wrote:
> D version:
>
> 	{ //displays ~A~B~C
> 	    A foo = scoped!(A)();
> 	    B bar = scoped!(B)();
> 	    C caz = new C();
> 	    destroy(caz);
> 	}
>
> Why the objects are not destroyed in the inverse order of their creation? Case in point, destroying foo releases a lock for bar and caz.

`foo` should be a `Scoped!A`. When it's typed as `A`, the
`Scoped!A` that is returned by `scoped`, is destructed
immediately (and the reference leaks, I guess).

Compare:

import std.stdio;
import std.typecons;
class A {~this() {writeln("~A");}}
class B {~this() {writeln("~B");}}
class C {~this() {writeln("~C");}}
void main()
{
     {
         writeln("bad:");
         A foo = scoped!(A)();
         writeln("1");
         B bar = scoped!(B)();
         writeln("2");
     }
     {
         writeln("good:");
         auto foo = scoped!(A)();
         writeln("1");
         auto bar = scoped!(B)();
         writeln("2");
     }
}

prints:

bad:
~A
1
~B
2
good:
1
2
~B
~A
October 22, 2014
On 10/22/2014 08:45 AM, eles wrote:> C++ versions:
>
>      { //displays ~C~B~A
>          A foo;
>          B bar;
>          C *caz = new C();
>          delete caz;
>      }
>
>      std::cout << std::endl;
>
>      { //displays ~C~B~A
>          std::unique_ptr<A> foo = std::make_unique<A>();
>          std::unique_ptr<B> bar = std::make_unique<B>();
>          C *caz = new C();
>          delete caz;
>      }
>
>
> D version:
>
>      { //displays ~A~B~C
>          A foo = scoped!(A)();
>          B bar = scoped!(B)();
>          C caz = new C();
>          destroy(caz);
>      }
>
> Why the objects are not destroyed in the inverse order of their
> creation? Case in point, destroying foo releases a lock for bar and caz.

I confirm this. If you put writeln() expressions inside typecons.scoped, you will realize that the destroys are happening right after each scoped line:

constructed A
destroying
~A
constructed B
destroying
~B
~C

Extremely dangerous and very tricky! The solution? Define the objects as 'auto' (or 'const', etc.), which you want anyway:

    auto foo = scoped!(A)();
    auto bar = scoped!(B)();

You do not want to be left with A or B. You want Scoped!A and Scoped!B.

Ali

October 22, 2014
On Wed, 22 Oct 2014 18:03:44 +0000
anonymous via Digitalmars-d-learn <digitalmars-d-learn@puremagic.com>
wrote:

> On Wednesday, 22 October 2014 at 15:45:02 UTC, eles wrote:
> > D version:
> >
> > 	{ //displays ~A~B~C
> > 	    A foo = scoped!(A)();
> > 	    B bar = scoped!(B)();
> > 	    C caz = new C();
> > 	    destroy(caz);
> > 	}
> >
> > Why the objects are not destroyed in the inverse order of their creation? Case in point, destroying foo releases a lock for bar and caz.
> 
> `foo` should be a `Scoped!A`. When it's typed as `A`, the `Scoped!A` that is returned by `scoped`, is destructed immediately (and the reference leaks, I guess).
yes, this is the case. i believe that this should be explicitly documented in `scoped` ddocs. documentation examples using 'auto', but there is no mention that this is what *must* be used.


October 22, 2014
On Wednesday, 22 October 2014 at 18:14:54 UTC, ketmar via Digitalmars-d-learn wrote:
> yes, this is the case. i believe that this should be explicitly
> documented in `scoped` ddocs. documentation examples using 'auto', but there is no mention that this is what *must* be used.

This is too dangerous to be used in real programs…
« First   ‹ Prev
1 2