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
}


Reply via email to