www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.bugs - [Issue 16824] New: std.experimental.allocator.dispose leaks memory

https://issues.dlang.org/show_bug.cgi?id=16824

          Issue ID: 16824
           Summary: std.experimental.allocator.dispose leaks memory for
                    arrays of more than 1 dimension
           Product: D
           Version: D2
          Hardware: x86_64
                OS: Windows
            Status: NEW
          Severity: normal
          Priority: P1
         Component: phobos
          Assignee: nobody puremagic.com
          Reporter: atila.neves gmail.com

The program below throws an AssertError due to memory leaks when calling
dispose with a 2D array. dispose works fine with a 1D array. The issue is there
for all N-D arrays where N > 1 (it's easy to get a 3D one with string[][]). A
simple fix is to check in the dispose for arrays whether or not the elements
are also arrays and recurse.

The output for the program:

+ Allocated  ptr 544880 of 16 bytes length
+ Allocated  ptr 544640 of 24 bytes length
+ Allocated  ptr 544EA0 of 24 bytes length
- Deallocate ptr 544880 of 16 bytes length

core.exception.AssertError allocator.d(42): Memory leak in TestAllocator.
Allocations: [ByteRange(544640, 24), ByteRange(544EA0, 24)]
----------------
0x0040A501 in _d_assert_msg
0x00402388 in _Dmain at C:\Users\Atila\coding\d\experiments\allocator.d(55)
0x0040B337 in D2rt6dmain211_d_run_mainUiPPaPUAAaZiZ6runAllMFZ9__lambda1MFZv
0x0040B2FB in void rt.dmain2._d_run_main(int, char**, extern (C) int
function(char[][])*).runAll()
0x0040B1FC in _d_run_main
0x00409F28 in main at C:\Users\Atila\coding\d\experiments\allocator.d(7)
0x0042804D in mainCRTStartup
0x75C838F4 in BaseThreadInitThunk
0x76F75DE3 in RtlUnicodeStringToInteger
0x76F75DAE in RtlUnicodeStringToInteger


The program:



import std.stdio;


struct TestAllocator {
    import std.experimental.allocator.common: platformAlignment;
    import std.experimental.allocator.mallocator: Mallocator;

    alias allocator = Mallocator.instance;

    private static struct ByteRange {
        void* ptr;
        size_t length;
    }
    private ByteRange[] _allocations;

    enum uint alignment = platformAlignment;

    void[] allocate(size_t numBytes) {
        auto ret = allocator.allocate(numBytes);
        writeln("+ Allocated  ptr ", ret.ptr, " of ", ret.length, " bytes
length");
        _allocations ~= ByteRange(ret.ptr, ret.length);
        return ret;
    }

    bool deallocate(void[] bytes) {
        import std.algorithm: remove, canFind;
        import std.conv: text;

        writeln("- Deallocate ptr ", bytes.ptr, " of ", bytes.length, " bytes
length");

        bool pred(ByteRange other) { return other.ptr == bytes.ptr &&
other.length == bytes.length; }

        assert(_allocations.canFind!pred,
                text("Unknown deallocate byte range. Ptr: ", bytes.ptr, "
length: ", bytes.length,
                     " allocations: ", _allocations));
        _allocations = _allocations.remove!pred;
        return allocator.deallocate(bytes);
    }

    ~this() {
        import std.conv: text;
        assert(!_allocations.length, text("Memory leak in TestAllocator.
Allocations: ", _allocations));
    }
}

void main() {
    import std.experimental.allocator: dispose, makeArray;

    TestAllocator allocator;

    auto ints2d = allocator.makeArray!(int[])(2);
    foreach(ref ints1d; ints2d)
        ints1d = allocator.makeArray!(int)(3);

    allocator.dispose(ints2d);
}

--
Nov 28 2016