core.exception.InvalidMemoryOperationError@(0)

Bayan Rafeh via Digitalmars-d-learn digitalmars-d-learn at puremagic.com
Sun Jan 25 14:06:26 PST 2015


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?


More information about the Digitalmars-d-learn mailing list