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

Reply via email to