On Saturday, 26 December 2020 at 19:36:24 UTC, ag0aep6g wrote:
On 26.12.20 13:59, ag0aep6g wrote:
Looks like a pretty nasty bug somewhere in
std.experimental.allocator or (less likely) the GC. Further
reduced code:
----
[...]
----
Apparently, something calls deallocateAll on a Mallocator
instance after the memory of that instance has been recycled
by the GC. Maybe allocatorObject or AllocatorList keep a
reference to GC memory out of sight of the GC.
I've looked into it some more, and as far as I can tell this is
what happens:
1) allocatorObject puts the AllocatorList instance into
malloced memory.
2) The AllocatorList puts the Mallocator instance into GC
memory, because its default BookkeepingAllocator is GCAllocator.
3) The GC recycles the memory of the Mallocator instance,
because it's only reachable via the malloced AllocatorList
instance and malloced memory isn't scanned by default.
4) Hell breaks loose because that recycled memory was not
actually garbage.
I'm not so sure anymore if this qualifies as a bug in
std.experimental.allocator. Maybe AllocatorList should be
registering its GC allocations as roots?
As a solution/workaround, you can use NullAllocator for
AllocatorList's BookkeepingAllocator:
----
import
std.experimental.allocator.building_blocks.null_allocator :
NullAllocator;
alias Alloc1 = FallbackAllocator!(
AllocatorList!(n => Region!Mallocator(1024*1024),
NullAllocator),
Mallocator);
----
Okay excellent. So there is a workaround atleast.
It also works with using Mallocator as the BookkeepingAllocator
for AllocatorList.
I encountered a slightly differt seg fault too. I'm wondering
whether it is related to this one:
import std.experimental.allocator: allocatorObject, expandArray;
import std.experimental.allocator.building_blocks.allocator_list:
AllocatorList;
import std.experimental.allocator.building_blocks.region: Region;
import
std.experimental.allocator.building_blocks.fallback_allocator:
FallbackAllocator;
import std.experimental.allocator.mallocator: Mallocator;
import core.memory: GC;
import std.stdio;
void main()
{
enum MB = 1024 * 1024;
{
alias Alloc1 = Region!Mallocator;
auto a1 = Alloc1(MB);
auto p1 = a1.allocate(10);
auto a2 = a1;
auto p2 = a2.allocate(10);
writeln(a1.owns(p1)); // Prints Ternary.Yes - incorrect?
writeln(a1.owns(p2)); // Prints Ternary.Yes - incorrect?
writeln(a2.owns(p1)); // Prints Ternary.Yes
writeln(a2.owns(p2)); // Prints Ternary.Yes
writeln(4); // This is printed
}
writeln(5); // this never gets printed; segfault happens upon
exiting the above scope
}