On 06/04/2017 03:08 AM, Stanislav Blinov wrote:
---

import core.memory : GC;
import std.stdio;
import std.typecons;

class C {
     int i;
     this(int i) {
         this.i = i;
     }

     ~this() {
         writeln("C(", i, ") dtor");
     }
}
[...]
auto selective(int numCs, int numBytes) {
[...]
     auto memory = GC.malloc(numCs*C.sizeof + numBytes*byte.sizeof,
             GC.BlkAttr.NO_SCAN);
     auto cs = (cast(C*)memory)[0..numCs];
     cs[] = C.init;
     // add scanning range for references
     GC.addRange(cs.ptr, cs.length*C.sizeof, typeid(C));
     auto bytes = (cast(byte*)(memory + numCs*C.sizeof))[0..numBytes];
     bytes[] = byte.init;
     return tuple!("Cs", "bytes")(cs, bytes);
}

void main() {

     int numCs = 4; // comes at runtime from elsewhere
     int numBytes = 32; // comes at runtime from elsewhere
[...]
     int counter;
[...]
     auto arrays3 = selective(numCs, numBytes);
     foreach (ref e; arrays3.Cs)
         e = new C(counter++); // dtors will not be called
}

---

Should this work, and if not, why?

As far as I can tell, the `addRange` call works correctly, but maybe too well in a sense. It keeps the `new`ed `C`s alive as long as `arrays3.Cs` has pointers to them. And `arrays3.Cs` has those pointers until the very end.

If you add `GC.removeRange(arrays3.Cs.ptr);` at the of `main`, the dtors show up. Overwriting `arrays3.Cs`'s elements with `null`s also works. My guess is that the `removeRange` call isn't done automatically before the final run of the GC. Maybe it should be?

But I have a vague memory that the GC isn't required to call destructors on everything in the final run. Or maybe it's not guaranteed that there is a final run when the program ends? Anyway, that would mean everything's working as intended, and you just can't rely on destructors like that.

Reply via email to