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