Surprising behaviour of std.experimental.allocator
Saurabh Das
saurabh.das at gmail.com
Sun Dec 27 08:28:18 UTC 2020
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
}
More information about the Digitalmars-d-learn
mailing list