[Issue 1164] Wrong order of memory deallocation

2016-12-27 Thread via Digitalmars-d-bugs
https://issues.dlang.org/show_bug.cgi?id=1164

--- Comment #15 from safety0ff.bugz  ---
(In reply to Pieter Penninckx from comment #14)
>
> This just runs normally for me. But if I understand comment #13 correctly,
> this is just luck and I shouldn't count on it, especially when the runtime
> is compiled with the MEMSTOMP option.

MEMSTOMP is essentially a tool used for debugging memory corruption type
issues.
Compliant D code runs correctly regardless whether it is enable and I was using
it as an illustration of what is allowed in the runtime.

I don't know what is the official stance on referencing other GC managed memory
from invariant code.
Since it is automatically run before the destructor/finalizer, it violates the
spec.
This restriction is quite severe, it might be better if invariants aren't
called before the destructor.

--


[Issue 1164] Wrong order of memory deallocation

2016-12-27 Thread via Digitalmars-d-bugs
https://issues.dlang.org/show_bug.cgi?id=1164

--- Comment #14 from Pieter Penninckx  ---
(In reply to comment #11)

Woops. You're right. The segfault is caused by an infinite recursion and
probably not by a GC problem. We can avoid the infinite recursion by marking
fun() as private:

class X 
{
X sibling;
private bool fun() const { return true;} // Note: this line changed.
invariant() {
if(sibling !is null) {
if (sibling.fun()) { assert(true); }
}
}
this() {sibling = new X(this); }
this(X sibling_) {sibling = sibling_;}
~this() {}
}

int main() {
X x = new X();
return 0;
}

This just runs normally for me. But if I understand comment #13 correctly, this
is just luck and I shouldn't count on it, especially when the runtime is
compiled with the MEMSTOMP option.

--


[Issue 1164] Wrong order of memory deallocation

2016-12-26 Thread via Digitalmars-d-bugs
https://issues.dlang.org/show_bug.cgi?id=1164

--- Comment #13 from safety0ff.bugz  ---
(In reply to Pieter Penninckx from comment #9)
> 
> Am I right that the garbage collector currently works as follows:


It currently works as follows:

* Mark
* For each unmarked object:
*   Finalize it if necessary
*   If it can be released without overwriting it do so
* For each unmarked unreleased object:
*   release memory of the object

However, you shouldn't rely on this:
http://dlang.org/spec/class.html#destructors

If you recompile druntime with the MEMSTOMP option, the GC and it will write
arbitrary data to finalized memory.

Therefore it follows that referencing GC managed objects from invariants of
other GC managed objects is unadvised.

I think a case could be made for being able to control insertion of invariant
calls in destructors.

--


[Issue 1164] Wrong order of memory deallocation

2016-12-26 Thread via Digitalmars-d-bugs
https://issues.dlang.org/show_bug.cgi?id=1164

--- Comment #12 from safety0ff.bugz  ---
(In reply to Pieter Penninckx from comment #7)
>
>   invariant() {
>   if(sibling !is null) {
>   if (sibling.fun())
>   { assert(true); }
>   }
>   }
>

Also:
https://dlang.org/spec/contracts.html#Invariants "The code in the invariant may
not call any public non-static members of the class or struct, either directly
or indirectly. Doing so will result in a stack overflow, as the invariant will
wind up being called in an infinitely recursive manner."

--


[Issue 1164] Wrong order of memory deallocation

2016-12-26 Thread via Digitalmars-d-bugs
https://issues.dlang.org/show_bug.cgi?id=1164

safety0ff.bugz  changed:

   What|Removed |Added

 CC||safety0ff.b...@gmail.com

--- Comment #11 from safety0ff.bugz  ---
(In reply to Pieter Penninckx from comment #7)
> 
> int main() {
>   X x = new X();
>   return 0;
> }

Your invariant gets called infinitely:
x.sibling.sibling == x
sibling's invariant() executes before and after sibling.fun() executes.
The invariant has the line: if (sibling.fun())

--


[Issue 1164] Wrong order of memory deallocation

2016-12-26 Thread via Digitalmars-d-bugs
https://issues.dlang.org/show_bug.cgi?id=1164

--- Comment #10 from Andrei Alexandrescu  ---
(In reply to Pieter Penninckx from comment #9)
> Just to be sure I understand you correctly.
> 
> Am I right that the garbage collector currently works as follows:
> 
> * Mark (= mark all reachable objects as reachable)
> * For each collectable (= non-reachable) object:
>   * Call dtor
>   * release memory of the collectable object

I don't know exactly. I am pretty certain, however, that freed objects are
currently not overwritten with .init.

--


[Issue 1164] Wrong order of memory deallocation

2016-12-26 Thread via Digitalmars-d-bugs
https://issues.dlang.org/show_bug.cgi?id=1164

--- Comment #9 from Pieter Penninckx  ---
Just to be sure I understand you correctly.

Am I right that the garbage collector currently works as follows:

* Mark (= mark all reachable objects as reachable)
* For each collectable (= non-reachable) object:
  * Call dtor
  * release memory of the collectable object

Am I right that the steps you are thinking about can also be formulated as
follows:

* Mark (= mark all reachable objects as reachable)
* For each collectable (= non-reachable) object:
  * Call dtor
  * Obliterate with .init
* For each collectable object:
  * release memory of the collectable object

--


[Issue 1164] Wrong order of memory deallocation

2016-12-25 Thread via Digitalmars-d-bugs
https://issues.dlang.org/show_bug.cgi?id=1164

--- Comment #8 from Andrei Alexandrescu  ---
Cool, was able to repro. Fortunately we have a couple of cards in our sleeve.
What we could do is:

* Mark
* For each collectable object:
  * Call dtor
  * (NEW) Obliterate with .init
* Sweep

That way objects used during destruction will at least find objects in a
deterministic state.

--


[Issue 1164] Wrong order of memory deallocation

2016-12-25 Thread via Digitalmars-d-bugs
https://issues.dlang.org/show_bug.cgi?id=1164

--- Comment #7 from Pieter Penninckx  ---
The following code still segfaults with DMD 2.072.1-0.

class X 
{
double a, b, c, d, e, f, g, h;
X sibling;
bool fun() const { return true;}
invariant() {
if(sibling !is null) {
if (sibling.fun())
{ assert(true);}
}
}
this() {sibling = new X(this); }
this(X sibling_) {sibling = sibling_;}
~this() {}
}

int main() {
X x = new X();
return 0;
}


Garbage collector + destructor (or finalizer) is a difficult combination. See
for instance this comment
(https://github.com/WebAssembly/design/issues/238#issuecomment-116877193) that
strongly opposes introducing a destructor in Javascript because this
combination can lead to object resurrection (objects made reachable again in a
destructor call).

--


[Issue 1164] Wrong order of memory deallocation

2016-12-22 Thread via Digitalmars-d-bugs
https://issues.dlang.org/show_bug.cgi?id=1164

Andrei Alexandrescu  changed:

   What|Removed |Added

 CC||and...@erdani.com

--- Comment #6 from Andrei Alexandrescu  ---
Couldn't reproduce on dmd 2.072.1. Any better code sample?

--


[Issue 1164] Wrong order of memory deallocation

2015-06-08 Thread via Digitalmars-d-bugs
https://issues.dlang.org/show_bug.cgi?id=1164

Andrei Alexandrescu  changed:

   What|Removed |Added

Version|2.040   |D2

--


[Issue 1164] Wrong order of memory deallocation

2014-07-04 Thread via Digitalmars-d-bugs
https://issues.dlang.org/show_bug.cgi?id=1164

Pieter Penninckx  changed:

   What|Removed |Added

 CC||pieter.pennin...@scarlet.be
Version|D1  |2.040

--- Comment #5 from Pieter Penninckx  ---
Reproduced with DMD version 2.065.


If the destructor cannot reference sub objects, this implies that also an
invariant cannot reference sub objects, because an invariant is called just
before the destructor. Example below triggers segfault with DMD version 2.065.


class B
{
double a, b, c, d, e, f, g, h;
bool fun() const { return true;}
}

class A
{
B b;
invariant() {
assert(b !is null);
if (b.fun()) // <- Segfault here, but b is not null.
{ assert(true);}
}
this() { b = new B(); }
~this(){}
}

int main() {
A a = new A();
return 0;
}

--