Jump to page: 1 2
Thread overview
custom memory management
Feb 27, 2014
Simon Bürger
Feb 27, 2014
Namespace
Feb 27, 2014
Simon Bürger
Feb 27, 2014
Simon Bürger
Feb 28, 2014
Bienlein
Feb 28, 2014
Dicebot
Feb 28, 2014
Simon Bürger
Feb 28, 2014
Dicebot
Feb 28, 2014
Namespace
Feb 28, 2014
Dicebot
Feb 28, 2014
Namespace
Feb 28, 2014
Dicebot
Feb 28, 2014
Namespace
Feb 28, 2014
Dicebot
Feb 28, 2014
Namespace
February 27, 2014
I am trying to implement a structure with value semantics which uses an internal buffer. The first approach looks like this:

    struct S
    {
        byte[] buf;

        this(int size) { buf = new byte[size]; }
        this(this) { buf = buf.dup; }
        ~this(this) { delete buf; }
    }

This works fine as long as such an object is allocated on the stack (so the destructor is called at the end of the scope). However when the destructor is called by the gc, the buffer might already be collected, and freeing it a second time is obviously invalid.

My second approach was to allocate the buffer outside the gc-managed heap, like so:

    this(int size) {
        buf = (cast(byte*)core.stdc.stdlib.malloc(size))[0..size];
    }

    ~this(this) {
        core.stdc.stdlib.free(buf);
    }

Sadly, this is incorrect as well. Because if such an object is collected by the gc, but the gc decides not to run the destructor, the buffer will never be free'd.

If the gc would either always or never call struct-destructors, one of my two solutions would work. But the current situation is (in compliance with the language spec), that it is called _sometimes_, which breaks both solutions.

One way the first approach could work would be for the destructor to check wether it was called by the gc, and skip the deallocation in that case. But as far as I know, the gc does not provide such a method. It would be trivial to implement, but seems kinda hackish.

I know the suggested way in D is to not deallocate the buffer at all, but rely on the gc to collect it eventually. But it still puzzles me that it seems to be impossible to do. Anybody have an idea how I could make it work?

thanks, simon
February 27, 2014
A struct is a value type. So it is passed by value and is placed on the stack.

{
    S s;
}

S DTor is called at the end of the scope. So you can rely on RAII as long as you use structs.
February 27, 2014
On Thursday, 27 February 2014 at 22:04:50 UTC, Namespace wrote:
> A struct is a value type. So it is passed by value and is placed on the stack.
>
> {
>     S s;
> }
>
> S DTor is called at the end of the scope. So you can rely on RAII as long as you use structs.

On the stack yes. But not on the heap:

    S[] s = new S[17];
    s = null;

the GC will collect the memory eventually, but without calling any destructor.

On the other hand:

    class C { S s; }
    C c = new c;
    c = null;

in this case, when the gc collects the memory, it will call both destrcutors. The one of C as well as of S.
February 27, 2014
On Thu, 27 Feb 2014 16:46:15 -0500, Simon Bürger <simon.buerger@rwth-aachen.de> wrote:

> I know the suggested way in D is to not deallocate the buffer at all, but rely on the gc to collect it eventually. But it still puzzles me that it seems to be impossible to do. Anybody have an idea how I could make it work?

Unfortunately, nothing is foolproof. The most "correct" solution is likely to use malloc/free. Yes, if you just new one of these, you will have to destroy it.

But if you have a destructor that uses GC allocated memory such an object can NEVER be a member of a heap-allocated class.

More and more, I think a thread-local flag of "I'm in the GC collection cycle" would be hugely advantageous -- if it doesn't already exist...

-Steve
February 27, 2014
On Thursday, 27 February 2014 at 22:15:41 UTC, Steven Schveighoffer wrote:
> On Thu, 27 Feb 2014 16:46:15 -0500, Simon Bürger [...]
> More and more, I think a thread-local flag of "I'm in the GC collection cycle" would be hugely advantageous -- if it doesn't already exist...

I don't think it does, so I actually implemented it myself (not thread-local, but same locking as the rest of the gc): github.com/Krox/druntime/commit/38b718f1dcf08ab8dabb6eed10ff1073e215890f . But now that you mention it, a thread-local flag might be better.
February 28, 2014
I asked something similar some days ago. Maybe this provides some information tat is helpful to you: http://forum.dlang.org/thread/mekdjoyejtfpafpcdvrg@forum.dlang.org
February 28, 2014
On Thursday, 27 February 2014 at 21:46:17 UTC, Simon Bürger wrote:
> Sadly, this is incorrect as well. Because if such an object is collected by the gc, but the gc decides not to run the destructor, the buffer will never be free'd.

I think you misiterpret the spec. If object is collected, destructor is guaranteed to run. But not all objects are guaranteed to be collected. For example, no collection happens at program termination.

So it is OK to release resources in destructor that will be reclaimed by OS at program termination anyway. List of such resources is OS-specific but heap memory tends to be there.
February 28, 2014
On Friday, 28 February 2014 at 10:40:17 UTC, Dicebot wrote:
> On Thursday, 27 February 2014 at 21:46:17 UTC, Simon Bürger wrote:
>> Sadly, this is incorrect as well. Because if such an object is collected by the gc, but the gc decides not to run the destructor, the buffer will never be free'd.
>
> I think you misiterpret the spec. If object is collected, destructor is guaranteed to run. But not all objects are guaranteed to be collected. For example, no collection happens at program termination.
>
> So it is OK to release resources in destructor that will be reclaimed by OS at program termination anyway. List of such resources is OS-specific but heap memory tends to be there.

If you are right that would mean that the current dmd/runtime does not follow the spec. Curious. The current implementation is not aware of strcut-destructors on the heap, i.e. the GC.BlkAttr.FINALIZE flag is not set for structs (or arrays of structs).

In the struct-inside-a-class example, the struct destructor is called by the (automatically generated) class-destructor. The gc only knows about class-destrcutor and calls only that one directly.
February 28, 2014
On Friday, 28 February 2014 at 12:36:48 UTC, Simon Bürger wrote:
> If you are right that would mean that the current dmd/runtime does not follow the spec. Curious. The current implementation is not aware of strcut-destructors on the heap, i.e. the GC.BlkAttr.FINALIZE flag is not set for structs (or arrays of structs).

Ah, structs are a bit tricky. Spec has override for struct destructors that says explicitly "Destructors are called when an object goes out of scope" so one can argue heap ignorance matches it. But the very next line contradicts it : "Their purpose is to free up resources owned by the struct object".

I believe it is a DMD bug though. There is no reason why destructors can't be run here. Most likely it is just defect of current GC implementation that everyone got used to.

This bug report discussion confirms it : https://d.puremagic.com/issues/show_bug.cgi?id=2834

Looks like decision was to fix it once precise GC gets added.

> In the struct-inside-a-class example, the struct destructor is called by the (automatically generated) class-destructor. The gc only knows about class-destructor and calls only that one directly.

Yes, that matches my current observations. Did not occure before because of C coding habits :) Lucky me.
February 28, 2014
On Friday, 28 February 2014 at 12:54:32 UTC, Dicebot wrote:
> On Friday, 28 February 2014 at 12:36:48 UTC, Simon Bürger wrote:
>> If you are right that would mean that the current dmd/runtime does not follow the spec. Curious. The current implementation is not aware of strcut-destructors on the heap, i.e. the GC.BlkAttr.FINALIZE flag is not set for structs (or arrays of structs).
>
> Ah, structs are a bit tricky. Spec has override for struct destructors that says explicitly "Destructors are called when an object goes out of scope" so one can argue heap ignorance matches it. But the very next line contradicts it : "Their purpose is to free up resources owned by the struct object".
>
> I believe it is a DMD bug though. There is no reason why destructors can't be run here. Most likely it is just defect of current GC implementation that everyone got used to.
>
> This bug report discussion confirms it : https://d.puremagic.com/issues/show_bug.cgi?id=2834
>
> Looks like decision was to fix it once precise GC gets added.

What can still take a long time. It annoys me very much that with arrays I can not rely on that the struct DTors are called.
« First   ‹ Prev
1 2