Quuxplusone updated this revision to Diff 177540.
Quuxplusone marked 5 inline comments as done.
Quuxplusone added a comment.

@ericwf ping!


Repository:
  rCXX libc++

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D47111/new/

https://reviews.llvm.org/D47111

Files:
  include/experimental/memory_resource
  src/experimental/memory_resource.cpp
  
test/libcxx/experimental/memory/memory.resource.monotonic.buffer/monotonic.buffer.mem/allocate_in_geometric_progression.pass.cpp
  
test/std/experimental/memory/memory.resource.monotonic.buffer/monotonic.buffer.ctor/copy_move.pass.cpp
  
test/std/experimental/memory/memory.resource.monotonic.buffer/monotonic.buffer.ctor/with_default_resource.pass.cpp
  
test/std/experimental/memory/memory.resource.monotonic.buffer/monotonic.buffer.ctor/without_buffer.pass.cpp
  
test/std/experimental/memory/memory.resource.monotonic.buffer/monotonic.buffer.mem/allocate_deallocate.pass.cpp
  
test/std/experimental/memory/memory.resource.monotonic.buffer/monotonic.buffer.mem/allocate_exception_safety.pass.cpp
  
test/std/experimental/memory/memory.resource.monotonic.buffer/monotonic.buffer.mem/allocate_from_initial_buffer.pass.cpp
  
test/std/experimental/memory/memory.resource.monotonic.buffer/monotonic.buffer.mem/allocate_from_underaligned_buffer.pass.cpp
  
test/std/experimental/memory/memory.resource.monotonic.buffer/monotonic.buffer.mem/allocate_from_zero_sized_buffer.pass.cpp
  
test/std/experimental/memory/memory.resource.monotonic.buffer/monotonic.buffer.mem/allocate_in_geometric_progression.pass.cpp
  
test/std/experimental/memory/memory.resource.monotonic.buffer/monotonic.buffer.mem/allocate_overaligned_request.pass.cpp
  
test/std/experimental/memory/memory.resource.monotonic.buffer/monotonic.buffer.mem/allocate_with_initial_size.pass.cpp
  
test/std/experimental/memory/memory.resource.monotonic.buffer/monotonic.buffer.mem/equality.pass.cpp

Index: test/std/experimental/memory/memory.resource.monotonic.buffer/monotonic.buffer.mem/equality.pass.cpp
===================================================================
--- /dev/null
+++ test/std/experimental/memory/memory.resource.monotonic.buffer/monotonic.buffer.mem/equality.pass.cpp
@@ -0,0 +1,62 @@
+//===----------------------------------------------------------------------===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is dual licensed under the MIT and the University of Illinois Open
+// Source Licenses. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+// REQUIRES: c++experimental
+// UNSUPPORTED: c++98, c++03
+
+// <experimental/memory_resource>
+
+// class monotonic_buffer_resource
+
+#include <experimental/memory_resource>
+#include <new>
+#include <type_traits>
+#include <cassert>
+
+struct assert_on_compare : public std::experimental::pmr::memory_resource
+{
+protected:
+    void *do_allocate(size_t, size_t)
+    { assert(false); }
+
+    void do_deallocate(void *, size_t, size_t)
+    { assert(false); }
+
+    bool do_is_equal(std::experimental::pmr::memory_resource const &) const noexcept
+    { assert(false); }
+};
+
+int main()
+{
+    // Same object
+    {
+        std::experimental::pmr::monotonic_buffer_resource r1;
+        std::experimental::pmr::monotonic_buffer_resource r2;
+        assert(r1 == r1);
+        assert(r1 != r2);
+
+        std::experimental::pmr::memory_resource & p1 = r1;
+        std::experimental::pmr::memory_resource & p2 = r2;
+        assert(p1 == p1);
+        assert(p1 != p2);
+        assert(p1 == r1);
+        assert(r1 == p1);
+        assert(p1 != r2);
+        assert(r2 != p1);
+    }
+    // Different types
+    {
+        std::experimental::pmr::monotonic_buffer_resource mono1;
+        std::experimental::pmr::memory_resource & r1 = mono1;
+        assert_on_compare c;
+        std::experimental::pmr::memory_resource & r2 = c;
+        assert(r1 != r2);
+        assert(!(r1 == r2));
+    }
+}
Index: test/std/experimental/memory/memory.resource.monotonic.buffer/monotonic.buffer.mem/allocate_with_initial_size.pass.cpp
===================================================================
--- /dev/null
+++ test/std/experimental/memory/memory.resource.monotonic.buffer/monotonic.buffer.mem/allocate_with_initial_size.pass.cpp
@@ -0,0 +1,51 @@
+//===----------------------------------------------------------------------===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is dual licensed under the MIT and the University of Illinois Open
+// Source Licenses. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+// REQUIRES: c++experimental
+// UNSUPPORTED: c++98, c++03
+
+// <experimental/memory_resource>
+
+// class monotonic_buffer_resource
+
+#include <experimental/memory_resource>
+#include <cassert>
+
+#include "count_new.hpp"
+
+void test(size_t initial_buffer_size)
+{
+    globalMemCounter.reset();
+
+    std::experimental::pmr::monotonic_buffer_resource mono1(
+        initial_buffer_size,
+        std::experimental::pmr::new_delete_resource()
+    );
+    assert(globalMemCounter.checkNewCalledEq(0));
+
+    mono1.allocate(1, 1);
+    assert(globalMemCounter.checkNewCalledEq(1));
+    assert(globalMemCounter.last_new_size >= initial_buffer_size);
+
+    mono1.allocate(initial_buffer_size - 1, 1);
+    assert(globalMemCounter.checkNewCalledEq(1));
+}
+
+int main()
+{
+    test(1);
+    test(8);
+    test(10);
+    test(100);
+    test(256);
+    test(1000);
+    test(1024);
+    test(1000000);
+    assert(globalMemCounter.checkOutstandingNewEq(0));
+}
Index: test/std/experimental/memory/memory.resource.monotonic.buffer/monotonic.buffer.mem/allocate_overaligned_request.pass.cpp
===================================================================
--- /dev/null
+++ test/std/experimental/memory/memory.resource.monotonic.buffer/monotonic.buffer.mem/allocate_overaligned_request.pass.cpp
@@ -0,0 +1,71 @@
+//===----------------------------------------------------------------------===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is dual licensed under the MIT and the University of Illinois Open
+// Source Licenses. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+// REQUIRES: c++experimental
+// UNSUPPORTED: c++98, c++03
+
+// Libc++ defers to the underlying MSVC library to provide the new/delete
+// definitions, which does not yet provide aligned allocation
+// XFAIL: LIBCXX-WINDOWS-FIXME
+
+// Clang 10 (and older) will trigger an availability error when the deployment
+// target does not support aligned allocation, even if we pass `-faligned-allocation`.
+// XFAIL: apple-clang-10 && availability=macosx10.12
+
+// The dylibs shipped before macosx10.14 do not contain the aligned allocation
+// functions, so trying to force using those with -faligned-allocation results
+// in a link error.
+// XFAIL: with_system_cxx_lib=macosx10.13
+// XFAIL: with_system_cxx_lib=macosx10.12
+// XFAIL: with_system_cxx_lib=macosx10.11
+// XFAIL: with_system_cxx_lib=macosx10.10
+// XFAIL: with_system_cxx_lib=macosx10.9
+// XFAIL: with_system_cxx_lib=macosx10.8
+// XFAIL: with_system_cxx_lib=macosx10.7
+
+// The test will fail on deployment targets that do not support sized deallocation.
+// XFAIL: availability=macosx10.11
+// XFAIL: availability=macosx10.10
+// XFAIL: availability=macosx10.9
+// XFAIL: availability=macosx10.8
+// XFAIL: availability=macosx10.7
+
+// <experimental/memory_resource>
+
+// class monotonic_buffer_resource
+
+#include <experimental/memory_resource>
+#include <cassert>
+
+#include "count_new.hpp"
+
+int main()
+{
+    globalMemCounter.reset();
+    std::experimental::pmr::monotonic_buffer_resource mono1(1024, std::experimental::pmr::new_delete_resource());
+    std::experimental::pmr::memory_resource & r1 = mono1;
+
+    constexpr size_t big_alignment = 8 * alignof(std::max_align_t);
+    static_assert(big_alignment > 4);
+
+    void *ret = r1.allocate(2048, big_alignment);
+    assert(ret != nullptr);
+    assert(globalMemCounter.checkNewCalledEq(1));
+    assert(globalMemCounter.last_new_size >= 2048);
+    assert(globalMemCounter.last_new_align >= big_alignment);
+
+    // Check that a single highly aligned allocation request doesn't
+    // permanently "poison" the resource to allocate only super-aligned
+    // blocks of memory.
+    ret = r1.allocate(globalMemCounter.last_new_size, 4);
+    assert(ret != nullptr);
+    assert(globalMemCounter.checkNewCalledEq(2));
+    assert(globalMemCounter.last_new_align >= 4);
+    assert(globalMemCounter.last_new_align < big_alignment);
+}
Index: test/std/experimental/memory/memory.resource.monotonic.buffer/monotonic.buffer.mem/allocate_in_geometric_progression.pass.cpp
===================================================================
--- /dev/null
+++ test/std/experimental/memory/memory.resource.monotonic.buffer/monotonic.buffer.mem/allocate_in_geometric_progression.pass.cpp
@@ -0,0 +1,55 @@
+//===----------------------------------------------------------------------===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is dual licensed under the MIT and the University of Illinois Open
+// Source Licenses. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+// REQUIRES: c++experimental
+// UNSUPPORTED: c++98, c++03
+
+// <experimental/memory_resource>
+
+// class monotonic_buffer_resource
+
+#include <experimental/memory_resource>
+#include <cassert>
+
+#include "count_new.hpp"
+
+void test_geometric_progression()
+{
+    // mem.res.monotonic.buffer 1.3
+    // Each additional buffer is larger than the previous one, following a
+    // geometric progression.
+
+    globalMemCounter.reset();
+    std::experimental::pmr::monotonic_buffer_resource mono1(100, std::experimental::pmr::new_delete_resource());
+    std::experimental::pmr::memory_resource & r1 = mono1;
+
+    assert(globalMemCounter.checkNewCalledEq(0));
+    size_t next_buffer_size = 100;
+    void *ret = r1.allocate(10, 1);
+    assert(ret != nullptr);
+    assert(globalMemCounter.checkNewCalledEq(1));
+    assert(globalMemCounter.last_new_size >= next_buffer_size);
+    next_buffer_size = globalMemCounter.last_new_size + 1;
+
+    int new_called = 1;
+    while (new_called < 5) {
+        ret = r1.allocate(10, 1);
+        if (globalMemCounter.new_called > new_called) {
+            assert(globalMemCounter.new_called == new_called + 1);
+            assert(globalMemCounter.last_new_size >= next_buffer_size);
+            next_buffer_size = globalMemCounter.last_new_size + 1;
+            new_called += 1;
+        }
+    }
+}
+
+int main()
+{
+    test_geometric_progression();
+}
Index: test/std/experimental/memory/memory.resource.monotonic.buffer/monotonic.buffer.mem/allocate_from_zero_sized_buffer.pass.cpp
===================================================================
--- /dev/null
+++ test/std/experimental/memory/memory.resource.monotonic.buffer/monotonic.buffer.mem/allocate_from_zero_sized_buffer.pass.cpp
@@ -0,0 +1,46 @@
+//===----------------------------------------------------------------------===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is dual licensed under the MIT and the University of Illinois Open
+// Source Licenses. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+// REQUIRES: c++experimental
+// UNSUPPORTED: c++98, c++03
+
+// <experimental/memory_resource>
+
+// class monotonic_buffer_resource
+
+#include <experimental/memory_resource>
+#include <cassert>
+
+#include "count_new.hpp"
+
+int main()
+{
+    globalMemCounter.reset();
+    {
+        char buffer[100];
+        std::experimental::pmr::monotonic_buffer_resource mono1((void *)buffer, 0, std::experimental::pmr::new_delete_resource());
+        std::experimental::pmr::memory_resource & r1 = mono1;
+
+        void *ret = r1.allocate(1, 1);
+        assert(ret != nullptr);
+        assert(globalMemCounter.checkNewCalledEq(1));
+    }
+    assert(globalMemCounter.checkDeleteCalledEq(1));
+
+    globalMemCounter.reset();
+    {
+        std::experimental::pmr::monotonic_buffer_resource mono1(nullptr, 0, std::experimental::pmr::new_delete_resource());
+        std::experimental::pmr::memory_resource & r1 = mono1;
+
+        void *ret = r1.allocate(1, 1);
+        assert(ret != nullptr);
+        assert(globalMemCounter.checkNewCalledEq(1));
+    }
+    assert(globalMemCounter.checkDeleteCalledEq(1));
+}
Index: test/std/experimental/memory/memory.resource.monotonic.buffer/monotonic.buffer.mem/allocate_from_underaligned_buffer.pass.cpp
===================================================================
--- /dev/null
+++ test/std/experimental/memory/memory.resource.monotonic.buffer/monotonic.buffer.mem/allocate_from_underaligned_buffer.pass.cpp
@@ -0,0 +1,57 @@
+//===----------------------------------------------------------------------===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is dual licensed under the MIT and the University of Illinois Open
+// Source Licenses. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+// REQUIRES: c++experimental
+// UNSUPPORTED: c++98, c++03
+
+// <experimental/memory_resource>
+
+// class monotonic_buffer_resource
+
+#include <experimental/memory_resource>
+#include <cassert>
+
+#include "count_new.hpp"
+
+int main()
+{
+    globalMemCounter.reset();
+    {
+        alignas(4) char buffer[17];
+        std::experimental::pmr::monotonic_buffer_resource mono1(buffer + 1, 16, std::experimental::pmr::new_delete_resource());
+        std::experimental::pmr::memory_resource & r1 = mono1;
+
+        void *ret = r1.allocate(1, 1);
+        assert(ret == buffer + 1);
+        mono1.release();
+
+        ret = r1.allocate(1, 2);
+        assert(ret == buffer + 2);
+        mono1.release();
+
+        ret = r1.allocate(1, 4);
+        assert(ret == buffer + 4);
+        mono1.release();
+
+        // Test a size that is just big enough to fit in the buffer,
+        // but can't fit if it's aligned.
+        ret = r1.allocate(16, 1);
+        assert(ret == buffer + 1);
+        mono1.release();
+
+        assert(globalMemCounter.checkNewCalledEq(0));
+        ret = r1.allocate(16, 2);
+        assert(globalMemCounter.checkNewCalledEq(1));
+        assert(globalMemCounter.last_new_size >= 16);
+        // assert(globalMemCounter.last_new_align >= 2);
+        mono1.release();
+        assert(globalMemCounter.checkDeleteCalledEq(1));
+        // assert(globalMemCounter.last_new_align == globalMemCounter.last_delete_align);
+    }
+}
Index: test/std/experimental/memory/memory.resource.monotonic.buffer/monotonic.buffer.mem/allocate_from_initial_buffer.pass.cpp
===================================================================
--- /dev/null
+++ test/std/experimental/memory/memory.resource.monotonic.buffer/monotonic.buffer.mem/allocate_from_initial_buffer.pass.cpp
@@ -0,0 +1,56 @@
+//===----------------------------------------------------------------------===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is dual licensed under the MIT and the University of Illinois Open
+// Source Licenses. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+// REQUIRES: c++experimental
+// UNSUPPORTED: c++98, c++03
+
+// <experimental/memory_resource>
+
+// class monotonic_buffer_resource
+
+#include <experimental/memory_resource>
+#include <cassert>
+
+#include "count_new.hpp"
+
+int main()
+{
+    globalMemCounter.reset();
+    char buffer[100];
+    std::experimental::pmr::monotonic_buffer_resource mono1((void *)buffer, sizeof buffer, std::experimental::pmr::new_delete_resource());
+    std::experimental::pmr::memory_resource & r1 = mono1;
+
+    // Check that construction with a buffer does not allocate anything from the upstream
+    assert(globalMemCounter.checkNewCalledEq(0));
+
+    // Check that an allocation that fits in the buffer does not allocate anything from the upstream
+    void *ret = r1.allocate(50);
+    assert(ret);
+    assert(globalMemCounter.checkNewCalledEq(0));
+
+    // Check a second allocation
+    ret = r1.allocate(20);
+    assert(ret);
+    assert(globalMemCounter.checkNewCalledEq(0));
+
+    r1.deallocate(ret, 50);
+    assert(globalMemCounter.checkDeleteCalledEq(0));
+
+    // Check an allocation that doesn't fit in the original buffer
+    ret = r1.allocate(50);
+    assert(ret);
+    assert(globalMemCounter.checkNewCalledEq(1));
+
+    r1.deallocate(ret, 50);
+    assert(globalMemCounter.checkDeleteCalledEq(0));
+
+    mono1.release();
+    assert(globalMemCounter.checkDeleteCalledEq(1));
+    assert(globalMemCounter.checkOutstandingNewEq(0));
+}
Index: test/std/experimental/memory/memory.resource.monotonic.buffer/monotonic.buffer.mem/allocate_exception_safety.pass.cpp
===================================================================
--- /dev/null
+++ test/std/experimental/memory/memory.resource.monotonic.buffer/monotonic.buffer.mem/allocate_exception_safety.pass.cpp
@@ -0,0 +1,76 @@
+//===----------------------------------------------------------------------===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is dual licensed under the MIT and the University of Illinois Open
+// Source Licenses. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+// REQUIRES: c++experimental
+// UNSUPPORTED: c++98, c++03
+
+// <experimental/memory_resource>
+
+// class monotonic_buffer_resource
+
+#include <experimental/memory_resource>
+#include <cassert>
+
+#include "count_new.hpp"
+
+struct repointable_resource : public std::experimental::pmr::memory_resource
+{
+    std::experimental::pmr::memory_resource *which;
+
+    explicit repointable_resource(std::experimental::pmr::memory_resource *res) : which(res) {}
+
+protected:
+    void *do_allocate(size_t size, size_t align)
+    { return which->allocate(size, align); }
+
+    void do_deallocate(void *p, size_t size, size_t align)
+    { return which->deallocate(p, size, align); }
+
+    bool do_is_equal(std::experimental::pmr::memory_resource const &rhs) const noexcept
+    { return which->is_equal(rhs); }
+};
+
+int main()
+{
+    globalMemCounter.reset();
+    repointable_resource upstream(std::experimental::pmr::new_delete_resource());
+    alignas(16) char buffer[64];
+    std::experimental::pmr::monotonic_buffer_resource mono1(buffer, sizeof buffer, &upstream);
+    std::experimental::pmr::memory_resource & r1 = mono1;
+
+    void *res = r1.allocate(64, 16);
+    assert(res == buffer);
+    assert(globalMemCounter.checkNewCalledEq(0));
+
+    res = r1.allocate(64, 16);
+    assert(res != buffer);
+    assert(globalMemCounter.checkNewCalledEq(1));
+    assert(globalMemCounter.checkDeleteCalledEq(0));
+    const size_t last_new_size = globalMemCounter.last_new_size;
+
+    upstream.which = std::experimental::pmr::null_memory_resource();
+    try {
+        res = r1.allocate(last_new_size, 16);
+        assert(false);
+    } catch (const std::bad_alloc&) {
+        // we expect this
+    }
+    assert(globalMemCounter.checkNewCalledEq(1));
+    assert(globalMemCounter.checkDeleteCalledEq(0));
+
+    upstream.which = std::experimental::pmr::new_delete_resource();
+    res = r1.allocate(last_new_size, 16);
+    assert(res != buffer);
+    assert(globalMemCounter.checkNewCalledEq(2));
+    assert(globalMemCounter.checkDeleteCalledEq(0));
+
+    mono1.release();
+    assert(globalMemCounter.checkNewCalledEq(2));
+    assert(globalMemCounter.checkDeleteCalledEq(2));
+}
Index: test/std/experimental/memory/memory.resource.monotonic.buffer/monotonic.buffer.mem/allocate_deallocate.pass.cpp
===================================================================
--- /dev/null
+++ test/std/experimental/memory/memory.resource.monotonic.buffer/monotonic.buffer.mem/allocate_deallocate.pass.cpp
@@ -0,0 +1,57 @@
+//===----------------------------------------------------------------------===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is dual licensed under the MIT and the University of Illinois Open
+// Source Licenses. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+// REQUIRES: c++experimental
+// UNSUPPORTED: c++98, c++03
+
+// <experimental/memory_resource>
+
+// class monotonic_buffer_resource
+
+#include <experimental/memory_resource>
+#include <cassert>
+
+#include "count_new.hpp"
+
+int main()
+{
+    {
+        globalMemCounter.reset();
+
+        std::experimental::pmr::monotonic_buffer_resource mono1(std::experimental::pmr::new_delete_resource());
+        std::experimental::pmr::memory_resource & r1 = mono1;
+
+        void *ret = r1.allocate(50);
+        assert(ret);
+        assert(globalMemCounter.checkNewCalledGreaterThan(0));
+        assert(globalMemCounter.checkDeleteCalledEq(0));
+
+        // mem.res.monotonic.buffer 1.2
+        // A call to deallocate has no effect, thus the amount of memory
+        // consumed increases monotonically until the resource is destroyed.
+        // Check that deallocate is a no-op
+        r1.deallocate(ret, 50);
+        assert(globalMemCounter.checkDeleteCalledEq(0));
+
+        mono1.release();
+        assert(globalMemCounter.checkDeleteCalledEq(1));
+        assert(globalMemCounter.checkOutstandingNewEq(0));
+
+        globalMemCounter.reset();
+
+        ret = r1.allocate(500);
+        assert(ret);
+        assert(globalMemCounter.checkNewCalledGreaterThan(0));
+        assert(globalMemCounter.checkDeleteCalledEq(0));
+
+        // Check that the destructor calls release()
+    }
+    assert(globalMemCounter.checkDeleteCalledEq(1));
+    assert(globalMemCounter.checkOutstandingNewEq(0));
+}
Index: test/std/experimental/memory/memory.resource.monotonic.buffer/monotonic.buffer.ctor/without_buffer.pass.cpp
===================================================================
--- /dev/null
+++ test/std/experimental/memory/memory.resource.monotonic.buffer/monotonic.buffer.ctor/without_buffer.pass.cpp
@@ -0,0 +1,39 @@
+//===----------------------------------------------------------------------===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is dual licensed under the MIT and the University of Illinois Open
+// Source Licenses. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+// REQUIRES: c++experimental
+// UNSUPPORTED: c++98, c++03
+
+// <experimental/memory_resource>
+
+// class monotonic_buffer_resource
+
+#include <experimental/memory_resource>
+#include <cassert>
+
+#include "count_new.hpp"
+
+int main()
+{
+    // Constructing a monotonic_buffer_resource should not cause allocations
+    // by itself; the resource should wait to allocate until an allocation is
+    // requested.
+
+    globalMemCounter.reset();
+    std::experimental::pmr::set_default_resource(std::experimental::pmr::new_delete_resource());
+
+    std::experimental::pmr::monotonic_buffer_resource r1;
+    assert(globalMemCounter.checkNewCalledEq(0));
+
+    std::experimental::pmr::monotonic_buffer_resource r2(1024);
+    assert(globalMemCounter.checkNewCalledEq(0));
+
+    std::experimental::pmr::monotonic_buffer_resource r3(1024, std::experimental::pmr::new_delete_resource());
+    assert(globalMemCounter.checkNewCalledEq(0));
+}
Index: test/std/experimental/memory/memory.resource.monotonic.buffer/monotonic.buffer.ctor/with_default_resource.pass.cpp
===================================================================
--- /dev/null
+++ test/std/experimental/memory/memory.resource.monotonic.buffer/monotonic.buffer.ctor/with_default_resource.pass.cpp
@@ -0,0 +1,45 @@
+//===----------------------------------------------------------------------===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is dual licensed under the MIT and the University of Illinois Open
+// Source Licenses. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+// REQUIRES: c++experimental
+// UNSUPPORTED: c++98, c++03
+
+// <experimental/memory_resource>
+
+// class monotonic_buffer_resource
+
+#include <experimental/memory_resource>
+#include <cassert>
+
+int main()
+{
+    std::experimental::pmr::memory_resource *expected = std::experimental::pmr::null_memory_resource();
+    std::experimental::pmr::set_default_resource(expected);
+    {
+        char buffer[16];
+        std::experimental::pmr::monotonic_buffer_resource r1;
+        std::experimental::pmr::monotonic_buffer_resource r2(16);
+        std::experimental::pmr::monotonic_buffer_resource r3(buffer, sizeof buffer);
+        assert(r1.upstream_resource() == expected);
+        assert(r2.upstream_resource() == expected);
+        assert(r3.upstream_resource() == expected);
+    }
+
+    expected = std::experimental::pmr::new_delete_resource();
+    std::experimental::pmr::set_default_resource(expected);
+    {
+        char buffer[16];
+        std::experimental::pmr::monotonic_buffer_resource r1;
+        std::experimental::pmr::monotonic_buffer_resource r2(16);
+        std::experimental::pmr::monotonic_buffer_resource r3(buffer, sizeof buffer);
+        assert(r1.upstream_resource() == expected);
+        assert(r2.upstream_resource() == expected);
+        assert(r3.upstream_resource() == expected);
+    }
+}
Index: test/std/experimental/memory/memory.resource.monotonic.buffer/monotonic.buffer.ctor/copy_move.pass.cpp
===================================================================
--- /dev/null
+++ test/std/experimental/memory/memory.resource.monotonic.buffer/monotonic.buffer.ctor/copy_move.pass.cpp
@@ -0,0 +1,29 @@
+//===----------------------------------------------------------------------===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is dual licensed under the MIT and the University of Illinois Open
+// Source Licenses. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+// REQUIRES: c++experimental
+// UNSUPPORTED: c++98, c++03
+
+// <experimental/memory_resource>
+
+// monotonic_buffer_resource(monotonic_buffer_resource const&) = delete;
+// monotonic_buffer_resource& operator=(monotonic_buffer_resource const&) = delete;
+
+#include <experimental/memory_resource>
+#include <type_traits>
+#include <cassert>
+
+int main()
+{
+    using MBR = std::experimental::pmr::monotonic_buffer_resource;
+    static_assert(!std::is_copy_constructible<MBR>::value, "");
+    static_assert(!std::is_move_constructible<MBR>::value, "");
+    static_assert(!std::is_copy_assignable<MBR>::value, "");
+    static_assert(!std::is_move_assignable<MBR>::value, "");
+}
Index: test/libcxx/experimental/memory/memory.resource.monotonic.buffer/monotonic.buffer.mem/allocate_in_geometric_progression.pass.cpp
===================================================================
--- /dev/null
+++ test/libcxx/experimental/memory/memory.resource.monotonic.buffer/monotonic.buffer.mem/allocate_in_geometric_progression.pass.cpp
@@ -0,0 +1,33 @@
+//===----------------------------------------------------------------------===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is dual licensed under the MIT and the University of Illinois Open
+// Source Licenses. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+// REQUIRES: c++experimental
+// UNSUPPORTED: c++98, c++03
+
+// <experimental/memory_resource>
+
+// class monotonic_buffer_resource
+
+#include <experimental/memory_resource>
+#include <cassert>
+
+#include "count_new.hpp"
+
+int main()
+{
+    globalMemCounter.reset();
+    std::experimental::pmr::monotonic_buffer_resource mono;
+
+    for (int i=0; i < 100; ++i) {
+        mono.allocate(1);
+        assert(globalMemCounter.last_new_size < 1000000000);
+        mono.release();
+        assert(globalMemCounter.checkOutstandingNewEq(0));
+    }
+}
Index: src/experimental/memory_resource.cpp
===================================================================
--- src/experimental/memory_resource.cpp
+++ src/experimental/memory_resource.cpp
@@ -153,4 +153,83 @@
     return __default_memory_resource(true, __new_res);
 }
 
+// 23.12.6, mem.res.monotonic.buffer
+
+static size_t roundup(size_t count, size_t alignment)
+{
+    size_t mask = alignment - 1;
+    return (count + mask) & ~mask;
+}
+
+void *monotonic_buffer_resource::__initial_header::__try_allocate_from_chunk(
+    size_t bytes, size_t align)
+{
+    if (!__cur_)
+        return nullptr;
+    void *new_ptr = static_cast<void*>(__cur_);
+    size_t new_capacity = (__end_ - __cur_);
+    void *aligned_ptr = _VSTD::align(align, bytes, new_ptr, new_capacity);
+    if (aligned_ptr != nullptr)
+        __cur_ = static_cast<char*>(new_ptr) + bytes;
+    return aligned_ptr;
+}
+
+void *monotonic_buffer_resource::__chunk_header::__try_allocate_from_chunk(
+    size_t bytes, size_t align)
+{
+    void *new_ptr = static_cast<void*>(__cur_);
+    size_t new_capacity = (reinterpret_cast<char*>(this) - __cur_);
+    void *aligned_ptr = _VSTD::align(align, bytes, new_ptr, new_capacity);
+    if (aligned_ptr != nullptr)
+        __cur_ = static_cast<char*>(new_ptr) + bytes;
+    return aligned_ptr;
+}
+
+void* monotonic_buffer_resource::do_allocate(size_t bytes, size_t align)
+{
+    const size_t header_size = sizeof(__chunk_header);
+    const size_t header_align = alignof(__chunk_header);
+
+    auto previous_allocation_size = [&]() {
+        if (__chunks_ != nullptr)
+            return __chunks_->__allocation_size();
+
+        size_t newsize = (__initial_.__start_ != nullptr) ?
+            (__initial_.__end_ - __initial_.__start_) : __initial_.__size_;
+
+        return roundup(newsize, header_align) + header_size;
+    };
+
+    if (void *result = __initial_.__try_allocate_from_chunk(bytes, align))
+        return result;
+    if (__chunks_ != nullptr) {
+        if (void *result = __chunks_->__try_allocate_from_chunk(bytes, align))
+            return result;
+    }
+
+    // Allocate a brand-new chunk.
+
+    if (align < header_align)
+        align = header_align;
+
+    size_t aligned_capacity = roundup(bytes, header_align) + header_size;
+    size_t previous_capacity = previous_allocation_size();
+
+    if (aligned_capacity <= previous_capacity) {
+        size_t newsize = 2 * (previous_capacity - header_size);
+        aligned_capacity = roundup(newsize, header_align) + header_size;
+    }
+
+    char *start = (char *)__res_->allocate(aligned_capacity, align);
+    __chunk_header *header =
+        (__chunk_header *)(start + aligned_capacity - header_size);
+    header->__next_ = __chunks_;
+    header->__start_ = start;
+    header->__cur_ = start;
+    header->__align_ = align;
+    __chunks_ = header;
+
+    return __chunks_->__try_allocate_from_chunk(bytes, align);
+}
+
 _LIBCPP_END_NAMESPACE_LFTS_PMR
Index: include/experimental/memory_resource
===================================================================
--- include/experimental/memory_resource
+++ include/experimental/memory_resource
@@ -98,7 +98,7 @@
 // 8.5, memory.resource
 class _LIBCPP_TYPE_VIS memory_resource
 {
-    static const size_t __max_align = alignof(max_align_t);
+    static _LIBCPP_CONSTEXPR const size_t __max_align = alignof(max_align_t);
 
 // 8.5.2, memory.resource.public
 public:
@@ -345,7 +345,7 @@
                && is_same<typename _CTraits::pointer, char*>::value
                && is_same<typename _CTraits::void_pointer, void*>::value, "");
 
-    static const size_t _MaxAlign = alignof(max_align_t);
+    static _LIBCPP_CONSTEXPR const size_t _MaxAlign = alignof(max_align_t);
 
     using _Alloc = typename _CTraits::template rebind_alloc<
             typename aligned_storage<_MaxAlign, _MaxAlign>::type
@@ -420,6 +420,121 @@
     typename allocator_traits<_Alloc>::template rebind_alloc<char>
   >;
 
+// 23.12.6, mem.res.monotonic.buffer
+
+class _LIBCPP_TYPE_VIS monotonic_buffer_resource : public memory_resource
+{
+    static _LIBCPP_CONSTEXPR const size_t __default_buffer_capacity = 1024;
+    static _LIBCPP_CONSTEXPR const size_t __default_buffer_alignment = 16;
+
+    struct __chunk_header {
+        __chunk_header *__next_;
+        char *__start_;
+        char *__cur_;
+        size_t __align_;
+        size_t __allocation_size() {
+            return (reinterpret_cast<char*>(this) - __start_) + sizeof(*this);
+        }
+        void *__try_allocate_from_chunk(size_t, size_t);
+    };
+
+    struct __initial_header {
+        char *__start_;
+        char *__cur_;
+        union {
+            char *__end_;
+            size_t __size_;
+        };
+        void *__try_allocate_from_chunk(size_t, size_t);
+    };
+
+public:
+    _LIBCPP_INLINE_VISIBILITY
+    monotonic_buffer_resource()
+        : monotonic_buffer_resource(
+            nullptr, __default_buffer_capacity, get_default_resource()) {}
+
+    _LIBCPP_INLINE_VISIBILITY
+    explicit monotonic_buffer_resource(size_t __initial_size)
+        : monotonic_buffer_resource(
+            nullptr, __initial_size, get_default_resource()) {}
+
+    _LIBCPP_INLINE_VISIBILITY
+    monotonic_buffer_resource(void *__buffer, size_t __buffer_size)
+        : monotonic_buffer_resource(
+            __buffer, __buffer_size, get_default_resource()) {}
+
+    _LIBCPP_INLINE_VISIBILITY
+    explicit monotonic_buffer_resource(memory_resource *__upstream)
+        : monotonic_buffer_resource(
+            nullptr, __default_buffer_capacity, __upstream) {}
+
+    _LIBCPP_INLINE_VISIBILITY
+    monotonic_buffer_resource(size_t __initial_size,
+                              memory_resource *__upstream)
+        : monotonic_buffer_resource(
+            nullptr, __initial_size, __upstream) {}
+
+    _LIBCPP_INLINE_VISIBILITY
+    monotonic_buffer_resource(void *__buffer, size_t __buffer_size,
+                              memory_resource *__upstream)
+        : __res_(__upstream)
+    {
+        __initial_.__start_ = static_cast<char *>(__buffer);
+        if (__buffer != nullptr) {
+            __initial_.__cur_ = static_cast<char *>(__buffer);
+            __initial_.__end_ = static_cast<char *>(__buffer) + __buffer_size;
+        } else {
+            __initial_.__cur_ = nullptr;
+            __initial_.__size_ = __buffer_size;
+        }
+        __chunks_ = nullptr;
+    }
+
+    monotonic_buffer_resource(const monotonic_buffer_resource&) = delete;
+
+    _LIBCPP_INLINE_VISIBILITY
+    ~monotonic_buffer_resource() override
+        { release(); }
+
+    monotonic_buffer_resource& operator=(const monotonic_buffer_resource&) = delete;
+
+    _LIBCPP_INLINE_VISIBILITY
+    void release()
+    {
+        __initial_.__cur_ = __initial_.__start_;
+        while (__chunks_ != nullptr) {
+            __chunk_header *__next = __chunks_->__next_;
+            __res_->deallocate(
+                __chunks_->__start_,
+                __chunks_->__allocation_size(),
+                __chunks_->__align_
+            );
+            __chunks_ = __next;
+        }
+    }
+
+    _LIBCPP_INLINE_VISIBILITY
+    memory_resource* upstream_resource() const
+        { return __res_; }
+
+protected:
+    void *do_allocate(size_t __bytes, size_t __alignment) override; // key function
+
+    _LIBCPP_INLINE_VISIBILITY
+    void do_deallocate(void*, size_t, size_t) override
+        {}
+
+    _LIBCPP_INLINE_VISIBILITY
+    bool do_is_equal(const memory_resource& __other) const _NOEXCEPT override
+        { return this == _VSTD::addressof(__other); }
+
+private:
+    __initial_header __initial_;
+    __chunk_header *__chunks_;
+    memory_resource* __res_;
+};
+
 _LIBCPP_END_NAMESPACE_LFTS_PMR
 
 _LIBCPP_POP_MACROS
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to