Jump to page: 1 2 3
Thread overview
core.exception.InvalidMemoryOperationError@(0)
Jan 24, 2015
Bayan Rafeh
Jan 24, 2015
Ali Çehreli
Jan 24, 2015
Nordlöw
Jan 25, 2015
Vladimir Panteleev
Jan 25, 2015
Bayan Rafeh
Jan 25, 2015
Bayan Rafeh
Jan 25, 2015
Vladimir Panteleev
Jan 25, 2015
ketmar
Jan 25, 2015
Bayan Rafeh
Jan 25, 2015
Ali Çehreli
Jan 25, 2015
Bayan Rafeh
Jan 26, 2015
ketmar
Jan 26, 2015
Bayan Rafeh
Jan 26, 2015
ketmar
Jan 26, 2015
Bayan Rafeh
Jan 26, 2015
Bayan Rafeh
Jan 26, 2015
Dicebot
Jan 26, 2015
ketmar
Jan 27, 2015
Bayan Rafeh
Jan 27, 2015
ketmar
Jan 26, 2015
ketmar
January 24, 2015
This problem is a tough one. I've been getting this error when I run my unittests, and apparently it is caused by attempting an allocation in the destructor from what little I could find online about the subject.

The error is triggered once all my tests are complete, so I'm assuming the garbage collector is running before termination, but I tried placing logging messages in all my destructors and I didn't get anything, so none of them are being called.

Is there any other possible reason to get this error?
January 24, 2015
On 01/24/2015 04:16 AM, Bayan Rafeh wrote:
> This problem is a tough one. I've been getting this error when I run my
> unittests, and apparently it is caused by attempting an allocation in
> the destructor from what little I could find online about the subject.
>
> The error is triggered once all my tests are complete, so I'm assuming
> the garbage collector is running before termination, but I tried placing
> logging messages in all my destructors and I didn't get anything, so
> none of them are being called.
>
> Is there any other possible reason to get this error?

The allocations may be indirect. For example, formatting a string to log a message may allocate. Try marking your destructors as @nogc to see whether the compiler agrees.

I am not sure whether it would cause the same error but another reason for the error is touching reference members of a class, which may have been finalized before the object.

Ali

January 24, 2015
On Saturday, 24 January 2015 at 22:42:51 UTC, Ali Çehreli wrote:
> The allocations may be indirect. For example, formatting a string to log a message may allocate. Try marking your destructors as @nogc to see whether the compiler agrees.
>
> I am not sure whether it would cause the same error but another reason for the error is touching reference members of a class, which may have been finalized before the object.
>
> Ali

Strange, I have no explicit destructors declared in my code (classes/structs).

I'm making rich usage of std.container.Array, thought...
January 25, 2015
On Saturday, 24 January 2015 at 12:16:38 UTC, Bayan Rafeh wrote:
> This problem is a tough one. I've been getting this error when I run my unittests, and apparently it is caused by attempting an allocation in the destructor from what little I could find online about the subject.
>
> The error is triggered once all my tests are complete, so I'm assuming the garbage collector is running before termination, but I tried placing logging messages in all my destructors and I didn't get anything, so none of them are being called.
>
> Is there any other possible reason to get this error?

Hi,

I created a wiki page which I hope will help you solve this problem:

http://wiki.dlang.org/InvalidMemoryOperationError

Hope this helps.
January 25, 2015
On Sunday, 25 January 2015 at 00:43:43 UTC, Vladimir Panteleev wrote:
> On Saturday, 24 January 2015 at 12:16:38 UTC, Bayan Rafeh wrote:
>> This problem is a tough one. I've been getting this error when I run my unittests, and apparently it is caused by attempting an allocation in the destructor from what little I could find online about the subject.
>>
>> The error is triggered once all my tests are complete, so I'm assuming the garbage collector is running before termination, but I tried placing logging messages in all my destructors and I didn't get anything, so none of them are being called.
>>
>> Is there any other possible reason to get this error?
>
> Hi,
>
> I created a wiki page which I hope will help you solve this problem:
>
> http://wiki.dlang.org/InvalidMemoryOperationError
>
> Hope this helps.

Thank you very much, this will certainly be useful in debugging it.

> The allocations may be indirect. For example, formatting a string to log a message may allocate. Try marking your destructors as @nogc to see whether the compiler agrees.

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;
}
January 25, 2015
On Sunday, 25 January 2015 at 08:39:42 UTC, Bayan Rafeh wrote:
> On Sunday, 25 January 2015 at 00:43:43 UTC, Vladimir Panteleev wrote:
>> On Saturday, 24 January 2015 at 12:16:38 UTC, Bayan Rafeh wrote:
>>> This problem is a tough one. I've been getting this error when I run my unittests, and apparently it is caused by attempting an allocation in the destructor from what little I could find online about the subject.
>>>
>>> The error is triggered once all my tests are complete, so I'm assuming the garbage collector is running before termination, but I tried placing logging messages in all my destructors and I didn't get anything, so none of them are being called.
>>>
>>> Is there any other possible reason to get this error?
>>
>> Hi,
>>
>> I created a wiki page which I hope will help you solve this problem:
>>
>> http://wiki.dlang.org/InvalidMemoryOperationError
>>
>> Hope this helps.
>
> Thank you very much, this will certainly be useful in debugging it.
>
>> The allocations may be indirect. For example, formatting a string to log a message may allocate. Try marking your destructors as @nogc to see whether the compiler agrees.
>
> 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?
January 25, 2015
On Sunday, 25 January 2015 at 08:41:25 UTC, Bayan Rafeh wrote:
> The solution was just to remove the "delete a" from the destructor if someone comes across this later. Could someone tell me why though?

The "non-reentrant" bit applies to all GC operations, really - not just allocations. Explicit deletions are forbidden as well.

Use of the "delete" keyword is discouraged, and in that context, it is also not used in a safe way. By the time B's destructor is called, the A instance might have been already collected by the garbage collector, causing a double free bug (one of the reasons to not use delete).
January 25, 2015
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.

January 25, 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?
January 25, 2015
On 01/25/2015 02:06 PM, Bayan Rafeh wrote:

> is invariant() called during the destruction phase?

Something is fishy.

import std.stdio;

void main(){
    writeln("entered main");
    auto a = new A();
    writeln("leaving main");
}

class A {
    File file;

    this() {
        writeln("this");
    }

    ~this() {
        writeln("~this");
    }

    invariant() {
        writeln("invariant");
    }
}

The program produces the following output:

entered main
this
invariant
leaving main
invariant
invariant    <-- ?
~this
invariant    <-- ?

Removing the File member changes the output. Now two of the invariant calls are missing:

entered main
this
invariant
leaving main
invariant
~this

However, the last invariant is still there and I think by design: The destructor should see a valid state.

Ali

« First   ‹ Prev
1 2 3