On Sunday, 25 January 2015 at 19:15:54 UTC, ketmar wrote:
On Sun, 25 Jan 2015 08:41:24 +0000, Bayan Rafeh wrote:
I tried what you said and I think I see the problem. I
managed to
create an example program that duplicates the problem:
import std.stdio;
class A {
string path;
this(string p) {
path = p;
}
~this() {
}
void a(){}
void b(){}
}
class B {
A a;
this() {
this.a = new A("laladiv");
}
~this() {
delete a;
}
}
void main() {
B a = new B(); B b = new B(); delete b;
}
The solution was just to remove the "delete a" from the
destructor if
someone comes across this later. Could someone tell me why
though?
there is no guarantees on destruction order. and GC will not
nullify any
references to collected data. so, `a` can be already collected
and
finalized when `B.~this()` is called. yet reference is still
there, so
`delete a;` will try to delete already dead object. this will
lead to
crash.
without precise GC collector is not able to automatically
nullify all
"dead" references. and even if there will be such possibility,
it can
slow down collections alot (GC will nullifying alot of
references that
aren't used anyway), so i don't think that it do nullifying.
there is a simple rule: "dtor should not touch GC-managed
resources".
this will not give you "predictable destruction order" (that's
why most
people try to manually delete something in dtor), and this
simply will
not work at all.
if you want predictable destruction order, don't use GC at all,
use
manual memory management. it doesn't matter which hack you will
invent to
force destruction order, any hack will either be very fragile,
or will
not work. this is due to nature of GC-manged memory.
so: don't use GC-managed resources in dtors. don't use in any
way -- this
including accessing 'em. i.e. reading `a.path` in dtor is
invalid too. it
will not necessarily crash, but it's the source of "use after
free" error.
and don't even think that you can trick GC using checks from
`core.memory`! this will not work too. sure, you can check if
memory used
by `a` is still alive, but that memory can be used by completely
different object!
tl;dr:
1. don't use GC-managed objects in dtors. not even try to
access 'em.
2. don't try to trick GC. either don't use it, or cooperate
with it.
All right, I removed all my destructors(turns out I don't really
need them), but I'm still running into this very same error.
This is another problematic example program:
import std.stdio;
void main(){
auto a = new A("/tmp/invalid");
}
class A {
File f;
string path;
this(string path) {
this.path = path;
// f = File(path, "r");
}
invariant() {
File test = File(path, "r");
}
}
is invariant() called during the destruction phase?