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.