Thread overview
Dynamic arrays, emplace and GC
Jul 05, 2016
Claude
Jul 05, 2016
ketmar
Jul 05, 2016
Claude
Jul 05, 2016
Rene Zwanenburg
July 05, 2016
Hello,

I've been working on some kind of allocator using a dynamic array as a memory pool. I used emplace to allocate class instances within that array, and I was surprised to see I had to use GC.addRange() to avoid the GC to destroy stuff referenced in that array.

Here's a chunk of code[1]:

struct Pool(T)
{
public:
    T alloc(Args...)(Args args)
    {
        mData.length++;
        import core.memory : GC;
        //GC.addRange(mData[$ - 1].data.ptr, mData[$ - 1].data.length);
        import std.conv : emplace;
        auto t = emplace!T(mData[$ - 1].data, args);
        return t;
    }

private:
    struct Storage
    {
        ubyte[__traits(classInstanceSize, T)] data;
    }

    Storage[] mData;
}

class Foo
{
    this(int a)
    {
        aa = a;
    }
    ~this()
    {
        import std.stdio; writefln("DTOR");
        aa = 0;
    }
    int aa;
}

class Blob
{
    this(int b)
    {
        foo = new Foo(b);
    }

    Foo foo;
}

void main()
{
    Pool!Blob pool;

    Blob blob;
    foreach(a; 0 .. 10000)
    {
        blob = pool.alloc(6);
    }
    while(true){}
    import std.stdio; writefln("END");
}

Basically Blob instances are allocated in the array using emplace. And Blob creates references to Foo. If I comment out GC.addRange(), I see that Foo destructor is called by the GC[2]. If I leave it uncommented, the GC leaves the array alone.

So here's my question: Is it normal???
I thought that allocating memory in a dynamic array using "mData.length++;" was GC-compliant (unlike core.stdc.stdlib.malloc()), and I did not have to explictly use GC.addRange().


[1] I left out alignment management code. It's not the issue here.
[2] I used the helpful destructor tracker function of p0nce there: https://p0nce.github.io/d-idioms/#GC-proof-resource-class
July 05, 2016
On Tuesday, 5 July 2016 at 10:04:05 UTC, Claude wrote:
> So here's my question: Is it normal???

yes. `ubyte` arrays by definition cannot hold pointers, so GC doesn't bother to scan 'em.
July 05, 2016
On Tuesday, 5 July 2016 at 12:43:14 UTC, ketmar wrote:
> On Tuesday, 5 July 2016 at 10:04:05 UTC, Claude wrote:
>> So here's my question: Is it normal???
>
> yes. `ubyte` arrays by definition cannot hold pointers, so GC doesn't bother to scan 'em.

Ah ok. I tried using void[size] static array and it seems to work without having to use GC.addRange().
July 05, 2016
On Tuesday, 5 July 2016 at 13:48:46 UTC, Claude wrote:
> Ah ok. I tried using void[size] static array and it seems to work without having to use GC.addRange().

Correct. void[] means the type of the data is unknown, so the GC has to assume it can contain pointers.

This also means that _everything_ in any void buffer has to be treated as a potential pointer. In other words, if you allocate a void buffer and fill it with ints, and one of those ints happens to have a value equal to the address of a GC-allocated object, the GC will assume the int is a pointer to that object and not free it.