EricWF created this revision.
EricWF added a reviewer: mclow.lists.
EricWF added a subscriber: cfe-commits.

Implemented from the LFTS v2 spec here: 
https://rawgit.com/cplusplus/fundamentals-ts/v2/main.html#memory.resource.monotonic.buffer.

This patch could use some finishing touches, but for the most part it is a 
complete implementation and tests.


https://reviews.llvm.org/D27402

Files:
  include/experimental/__memory
  include/experimental/memory_resource
  src/experimental/memory_resource.cpp
  
test/std/experimental/memory/memory.resource.monotonic.buffer/monotonic.buffer.ctor/buffer.pass.cpp
  
test/std/experimental/memory/memory.resource.monotonic.buffer/monotonic.buffer.ctor/buffer_resource.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/default.pass.cpp
  
test/std/experimental/memory/memory.resource.monotonic.buffer/monotonic.buffer.ctor/dtor.pass.cpp
  
test/std/experimental/memory/memory.resource.monotonic.buffer/monotonic.buffer.ctor/initial_size.pass.cpp
  
test/std/experimental/memory/memory.resource.monotonic.buffer/monotonic.buffer.ctor/resource.pass.cpp
  
test/std/experimental/memory/memory.resource.monotonic.buffer/monotonic.buffer.mem/do_allocate.pass.cpp
  
test/std/experimental/memory/memory.resource.monotonic.buffer/monotonic.buffer.mem/do_deallocate.pass.cpp
  
test/std/experimental/memory/memory.resource.monotonic.buffer/monotonic.buffer.mem/release.pass.cpp
  
test/std/experimental/memory/memory.resource.monotonic.buffer/monotonic.buffer.mem/upstream_resource.pass.cpp
  test/std/experimental/memory/memory.resource.pool/pool_options.pass.cpp
  test/support/count_new.hpp
  test/support/test_memory_resource.hpp

Index: test/support/test_memory_resource.hpp
===================================================================
--- test/support/test_memory_resource.hpp
+++ test/support/test_memory_resource.hpp
@@ -483,7 +483,8 @@
             assert(false);
 #endif
         }
-
+        next = static_cast<char*>(next) + s;
+        space -= s;
         return ret;
     }
 
Index: test/support/count_new.hpp
===================================================================
--- test/support/count_new.hpp
+++ test/support/count_new.hpp
@@ -184,6 +184,10 @@
         return disable_checking || n != last_new_size;
     }
 
+    bool checkLastNewSizeGreaterEq(int n) const {
+        return disable_checking || last_new_size >= n;
+    }
+
     bool checkOutstandingArrayNewEq(int n) const
     {
         return disable_checking || n == outstanding_array_new;
Index: test/std/experimental/memory/memory.resource.pool/pool_options.pass.cpp
===================================================================
--- /dev/null
+++ test/std/experimental/memory/memory.resource.pool/pool_options.pass.cpp
@@ -0,0 +1,26 @@
+//===----------------------------------------------------------------------===//
+//
+//                     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.
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++98, c++03
+
+// <experimental/memory_resource>
+
+// struct pool_options
+
+#include <cassert>
+#include <experimental/memory_resource>
+
+int main() {
+  using std::experimental::pmr::pool_options;
+  {
+    const pool_options p;
+    assert(p.max_blocks_per_chunk == 0);
+    assert(p.largest_required_pool_block == 0);
+  }
+}
Index: test/std/experimental/memory/memory.resource.monotonic.buffer/monotonic.buffer.mem/upstream_resource.pass.cpp
===================================================================
--- /dev/null
+++ test/std/experimental/memory/memory.resource.monotonic.buffer/monotonic.buffer.mem/upstream_resource.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.
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++98, c++03
+
+// <experimental/memory_resource>
+
+// class monotonic_buffer_resource
+
+// memory_resource *upstream_resource() const;
+
+#include <experimental/memory_resource>
+#include <type_traits>
+#include <cassert>
+
+#include "test_memory_resource.hpp"
+
+namespace ex = std::experimental::pmr;
+
+int main() {
+  using Res = TestResource;
+  Res R1;
+  {
+    ex::monotonic_buffer_resource res;
+    assert(res.upstream_resource() == ex::get_default_resource());
+    assert(ex::get_default_resource() == ex::new_delete_resource());
+
+    Res R;
+    ex::set_default_resource(&R);
+    ex::monotonic_buffer_resource res2;
+    assert(res2.upstream_resource() == &R);
+
+    ex::set_default_resource(ex::new_delete_resource());
+  }
+  {
+    Res R;
+    const ex::monotonic_buffer_resource res(&R);
+    assert(res.upstream_resource() == &R);
+  }
+}
Index: test/std/experimental/memory/memory.resource.monotonic.buffer/monotonic.buffer.mem/release.pass.cpp
===================================================================
--- /dev/null
+++ test/std/experimental/memory/memory.resource.monotonic.buffer/monotonic.buffer.mem/release.pass.cpp
@@ -0,0 +1,84 @@
+//===----------------------------------------------------------------------===//
+//
+//                     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.
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++98, c++03
+
+// <experimental/memory_resource>
+
+// class monotonic_buffer_resource
+
+// void release()
+
+#include <experimental/memory_resource>
+#include <type_traits>
+#include <cassert>
+
+#include "test_memory_resource.hpp"
+#include "count_new.hpp"
+
+namespace ex = std::experimental::pmr;
+
+int main() {
+  using Res = TestResource;
+  Res R1;
+  AllocController &P = R1.getController();
+  {
+    DisableAllocationGuard g;
+    ex::monotonic_buffer_resource res;
+    res.release();
+  }
+  {
+    ex::monotonic_buffer_resource res(&R1);
+    assert(res.allocate(1));
+    assert(P.alive == 1);
+    assert(P.alloc_count == 1);
+    assert(P.dealloc_count == 0);
+    assert(P.last_alloc_size > 1);
+    int count = 0;
+    while (P.alive == 1) {
+      ++count;
+      assert(res.allocate(1));
+    }
+    assert(count > 1);
+    assert(P.alive == 2);
+    assert(P.alloc_count == 2);
+    assert(P.dealloc_count == 0);
+    res.release();
+    assert(P.alive == 0);
+    assert(P.alloc_count == 2);
+    assert(P.dealloc_count == 2);
+  }
+  assert(P.alive == 0);
+  assert(P.alloc_count == 2);
+  assert(P.dealloc_count == 2);
+  P.reset();
+  {
+    const size_t S = 1024;
+    alignas(std::max_align_t) char Buff[S];
+    ex::monotonic_buffer_resource res(Buff, S, &R1);
+    res.allocate(1024, alignof(std::max_align_t));
+    assert(P.alloc_count == 0);
+    assert(P.dealloc_count == 0);
+
+    res.allocate(1);
+    assert(P.alive == 1);
+    assert(P.alloc_count == 1);
+    assert(P.dealloc_count == 0);
+
+    res.release();
+    assert(P.alive == 0);
+    assert(P.alloc_count == 1);
+    assert(P.dealloc_count == 1);
+    assert(P.checkDeallocMatchesAlloc());
+  }
+  assert(P.alive == 0);
+  assert(P.alloc_count == 1);
+  assert(P.dealloc_count == 1);
+  P.reset();
+}
Index: test/std/experimental/memory/memory.resource.monotonic.buffer/monotonic.buffer.mem/do_deallocate.pass.cpp
===================================================================
--- /dev/null
+++ test/std/experimental/memory/memory.resource.monotonic.buffer/monotonic.buffer.mem/do_deallocate.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.
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++98, c++03
+
+// <experimental/memory_resource>
+
+// class monotonic_buffer_resource
+
+// void do_deallocate(void *p, size_t size, size_t align)
+
+#include <experimental/memory_resource>
+#include <type_traits>
+#include <cassert>
+
+#include "test_memory_resource.hpp"
+#include "count_new.hpp"
+
+namespace ex = std::experimental::pmr;
+
+int main() {
+  using Res = TestResource;
+  Res R1;
+  AllocController &P = R1.getController();
+  {
+    ex::monotonic_buffer_resource res(&R1);
+    const size_t size = 2056;
+    void *mem = res.allocate(size);
+    assert(mem);
+    assert(P.alive == 1);
+    assert(P.alloc_count == 1);
+    assert(P.dealloc_count == 0);
+    assert(P.last_alloc_size > size);
+
+    res.deallocate(mem, size);
+    assert(P.alive == 1);
+    assert(P.alloc_count == 1);
+    assert(P.dealloc_count == 0);
+  }
+  assert(P.alive == 0);
+  assert(P.alloc_count == 1);
+  assert(P.dealloc_count == 1);
+  assert(P.checkDeallocMatchesAlloc());
+  P.reset();
+}
Index: test/std/experimental/memory/memory.resource.monotonic.buffer/monotonic.buffer.mem/do_allocate.pass.cpp
===================================================================
--- /dev/null
+++ test/std/experimental/memory/memory.resource.monotonic.buffer/monotonic.buffer.mem/do_allocate.pass.cpp
@@ -0,0 +1,105 @@
+//===----------------------------------------------------------------------===//
+//
+//                     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.
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++98, c++03
+
+// <experimental/memory_resource>
+
+// class monotonic_buffer_resource
+
+// void *do_allocate(size_t size, size_t align)
+
+#include <experimental/memory_resource>
+#include <type_traits>
+#include <cassert>
+
+#include "test_memory_resource.hpp"
+#include "count_new.hpp"
+
+namespace ex = std::experimental::pmr;
+
+void test_contiguous_allocation() {
+  using Res = TestResource;
+  Res R1;
+  AllocController &P = R1.getController();
+
+  {
+    const int initial_size = 2056;
+    ex::monotonic_buffer_resource res(initial_size, &R1);
+    void *last_ptr = res.allocate(1, 1);
+    int last_size = 1;
+    int rem_size = initial_size - 1;
+    for (int i = 0; rem_size - i >= 0; ++i) {
+      void *new_ptr = res.allocate(i, 1);
+      assert(new_ptr == (static_cast<char *>(last_ptr) + last_size));
+      last_ptr = new_ptr;
+      last_size = i;
+      rem_size -= i;
+      if (i == 64)
+        i = 0;
+    }
+    assert(P.alive == 1);
+    void *new_ptr = res.allocate(65, 1);
+    assert(P.alive >= 2);
+    assert(new_ptr != (static_cast<char *>(last_ptr) + 1));
+  }
+  assert(P.alive == 0);
+  P.reset();
+}
+
+void test_correct_alignment() {
+  using AllocT = MinAlignedAllocator<char>;
+  using Res = ex::resource_adaptor<AllocT>;
+
+  { // Test default alignment
+    AllocController P;
+    Res R((AllocT(P)));
+    ex::monotonic_buffer_resource res(&R);
+    for (int i = 0; i < 3; ++i) {
+      for (size_t size = 0; size < 1024; ++size) {
+        void *ptr = res.allocate(size);
+        assert(ptr);
+        assert((reinterpret_cast<uintptr_t>(ptr) % alignof(std::max_align_t)) ==
+               0);
+      }
+    }
+  }
+  {
+    AllocController P;
+    Res R((AllocT(P)));
+    ex::monotonic_buffer_resource res(&R);
+    for (int i = 0; i < 3; ++i) {
+      for (size_t align = 1; align <= alignof(std::max_align_t); align *= 2) {
+        void *ptr = res.allocate(1, align);
+        assert(ptr);
+        assert(reinterpret_cast<uintptr_t>(ptr) % align == 0);
+      }
+    }
+  }
+}
+
+void test_alloc_zero_size() {
+  using AllocT = MinAlignedAllocator<char>;
+  using Res = ex::resource_adaptor<AllocT>;
+  AllocController P;
+  Res R((AllocT(P)));
+  {
+    ex::monotonic_buffer_resource res(&R);
+    assert(res.allocate(0));
+    assert(P.alive == 1);
+  }
+  assert(P.alive == 0);
+  P.reset();
+}
+
+int main() {
+  test_contiguous_allocation();
+  test_correct_alignment();
+  test_alloc_zero_size();
+}
Index: test/std/experimental/memory/memory.resource.monotonic.buffer/monotonic.buffer.ctor/resource.pass.cpp
===================================================================
--- /dev/null
+++ test/std/experimental/memory/memory.resource.monotonic.buffer/monotonic.buffer.ctor/resource.pass.cpp
@@ -0,0 +1,65 @@
+//===----------------------------------------------------------------------===//
+//
+//                     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.
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++98, c++03
+
+// <experimental/memory_resource>
+
+// explicit monotonic_buffer_resource(monotonic_buffer_resource*);
+// monotonic_buffer_resource(size_t initial_size, monotonic_buffer_resource*);
+
+#include <experimental/memory_resource>
+#include <type_traits>
+#include <cassert>
+
+#include "test_memory_resource.hpp"
+
+namespace ex = std::experimental::pmr;
+
+int main() {
+  using Res = TestResource;
+  Res R1, R2;
+  AllocController &P1 = R1.getController();
+  AllocController &P2 = R2.getController();
+  {
+    static_assert(!std::is_convertible<ex::memory_resource *,
+                                       ex::monotonic_buffer_resource>::value,
+                  "");
+    static_assert(std::is_constructible<ex::monotonic_buffer_resource,
+                                        ex::monotonic_buffer_resource *>::value,
+                  "");
+  }
+  {
+    ex::monotonic_buffer_resource res(&R1);
+    ex::monotonic_buffer_resource res2(&R2);
+    assert(res.upstream_resource() == &R1);
+    assert(res2.upstream_resource() == &R2);
+    assert(P1.alive == 0);
+    assert(P2.alive == 0);
+  }
+  {
+    ex::monotonic_buffer_resource res(42, &R1);
+
+    assert(res.upstream_resource() == &R1);
+
+    assert(P1.alive == 0);
+    assert(P2.alive == 0);
+    void *mem1 = res.allocate(1, 1);
+    assert(P1.last_alloc_size >= 42);
+    const size_t default_alloc_size = P1.last_alloc_size;
+    assert(P1.alive == 1);
+
+    const size_t init_size = default_alloc_size + 42;
+    ex::monotonic_buffer_resource res2(init_size, &R2);
+    assert(res2.upstream_resource() == &R2);
+    void *mem2 = res2.allocate(1, 1);
+    assert(P2.last_alloc_size >= init_size);
+    assert(P2.alive == 1);
+  }
+}
Index: test/std/experimental/memory/memory.resource.monotonic.buffer/monotonic.buffer.ctor/initial_size.pass.cpp
===================================================================
--- /dev/null
+++ test/std/experimental/memory/memory.resource.monotonic.buffer/monotonic.buffer.ctor/initial_size.pass.cpp
@@ -0,0 +1,59 @@
+//===----------------------------------------------------------------------===//
+//
+//                     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.
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++98, c++03
+
+// <experimental/memory_resource>
+
+// explicit monotonic_buffer_resource(size_t initial_size);
+
+#include <experimental/memory_resource>
+#include <type_traits>
+#include <cassert>
+
+#include "test_memory_resource.hpp"
+#include "count_new.hpp"
+
+namespace ex = std::experimental::pmr;
+
+int main() {
+  using Res = TestResource;
+  {
+    static_assert(
+        !std::is_convertible<size_t, ex::monotonic_buffer_resource>::value, "");
+    static_assert(
+        std::is_constructible<ex::monotonic_buffer_resource, size_t>::value,
+        "");
+  }
+  {
+    Res R1;
+    auto &C = R1.getController();
+    auto *default_res = ex::get_default_resource();
+    assert(default_res == ex::new_delete_resource());
+
+    ex::monotonic_buffer_resource res(42);
+    assert(res.upstream_resource() == default_res);
+    void *mem = res.allocate(1, 1);
+    assert(globalMemCounter.checkOutstandingNewEq(1));
+    assert(globalMemCounter.checkLastNewSizeGreaterEq(42));
+    const size_t default_init_size = globalMemCounter.last_new_size;
+
+    const size_t custom_init_size = default_init_size + 42;
+    ex::set_default_resource(&R1);
+    ex::monotonic_buffer_resource res1(custom_init_size);
+    assert(res1.upstream_resource() == &R1);
+    assert(res1.upstream_resource() != default_res);
+    {
+      DisableAllocationGuard g;
+      mem = res1.allocate(1, 0);
+      assert(C.last_alloc_size >= default_init_size);
+      assert(C.alive == 1);
+    }
+  }
+}
Index: test/std/experimental/memory/memory.resource.monotonic.buffer/monotonic.buffer.ctor/dtor.pass.cpp
===================================================================
--- /dev/null
+++ test/std/experimental/memory/memory.resource.monotonic.buffer/monotonic.buffer.ctor/dtor.pass.cpp
@@ -0,0 +1,60 @@
+//===----------------------------------------------------------------------===//
+//
+//                     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.
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++98, c++03
+
+// <experimental/memory_resource>
+
+// ~monotonic_buffer_resource();
+
+#include <experimental/memory_resource>
+#include <type_traits>
+#include <cassert>
+
+#include "test_memory_resource.hpp"
+
+namespace ex = std::experimental::pmr;
+
+int main() {
+  using Res = TestResource;
+  Res R1;
+  AllocController &P = R1.getController();
+
+  {
+    ex::monotonic_buffer_resource res(&R1);
+    res.allocate(1, 1);
+    assert(P.alive == 1);
+    assert(P.alloc_count == 1);
+    assert(P.dealloc_count == 0);
+    assert(P.last_alloc_size > 1);
+    while (P.alive == 1)
+      res.allocate(1);
+  }
+  assert(P.alive == 0);
+  assert(P.alloc_count == 2);
+  assert(P.dealloc_count == 2);
+  P.reset();
+  {
+    const size_t S = 1024;
+    alignas(std::max_align_t) char Buff[S];
+    ex::monotonic_buffer_resource res(Buff, S, &R1);
+    res.allocate(1024, alignof(std::max_align_t));
+    assert(P.alloc_count == 0);
+    assert(P.dealloc_count == 0);
+
+    res.allocate(1);
+    assert(P.alive == 1);
+    assert(P.alloc_count == 1);
+    assert(P.dealloc_count == 0);
+  }
+  assert(P.alive == 0);
+  assert(P.alloc_count == 1);
+  assert(P.dealloc_count == 1);
+  P.reset();
+}
Index: test/std/experimental/memory/memory.resource.monotonic.buffer/monotonic.buffer.ctor/default.pass.cpp
===================================================================
--- /dev/null
+++ test/std/experimental/memory/memory.resource.monotonic.buffer/monotonic.buffer.ctor/default.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.
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++98, c++03
+
+// <experimental/memory_resource>
+
+// monotonic_buffer_resource();
+
+#include <experimental/memory_resource>
+#include <cassert>
+
+#include "test_memory_resource.hpp"
+
+namespace ex = std::experimental::pmr;
+
+int main() {
+  using Res = TestResource;
+  {
+    Res R1;
+    auto *default_res = ex::get_default_resource();
+    assert(default_res == ex::new_delete_resource());
+
+    ex::monotonic_buffer_resource res;
+    assert(res.upstream_resource() == default_res);
+
+    ex::set_default_resource(&R1);
+    ex::monotonic_buffer_resource res1;
+    assert(res1.upstream_resource() == &R1);
+    assert(res1.upstream_resource() != default_res);
+    assert(res.upstream_resource() != &R1);
+  }
+}
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,31 @@
+//===----------------------------------------------------------------------===//
+//
+//                     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.
+//
+//===----------------------------------------------------------------------===//
+
+// 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>
+
+#include "test_memory_resource.hpp"
+
+namespace ex = std::experimental::pmr;
+
+int main() {
+  using MBR = ex::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/std/experimental/memory/memory.resource.monotonic.buffer/monotonic.buffer.ctor/buffer_resource.pass.cpp
===================================================================
--- /dev/null
+++ test/std/experimental/memory/memory.resource.monotonic.buffer/monotonic.buffer.ctor/buffer_resource.pass.cpp
@@ -0,0 +1,79 @@
+//===----------------------------------------------------------------------===//
+//
+//                     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.
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++98, c++03
+
+// <experimental/memory_resource>
+
+// monotonic_buffer_resource(void *buffer, size_t buffer_size, monotonic_buffer_resource*);
+
+#include <experimental/memory_resource>
+#include <cassert>
+
+#include "test_memory_resource.hpp"
+
+namespace ex = std::experimental::pmr;
+
+int main() {
+  using Res = TestResource;
+  auto in_range = [](void *p, char *b, char *e) {
+    uintptr_t pint = reinterpret_cast<uintptr_t>(p);
+    uintptr_t bint = reinterpret_cast<uintptr_t>(b);
+    uintptr_t eint = reinterpret_cast<uintptr_t>(e);
+    return pint >= bint && pint < eint;
+  };
+  {
+    Res R1, R2;
+    AllocController &P1 = R1.getController();
+    AllocController &P2 = R2.getController();
+    char Buff1[42];
+    char Buff2[512];
+
+    ex::monotonic_buffer_resource res(Buff1, 42, &R1);
+    ex::monotonic_buffer_resource res2(Buff2, 512, &R2);
+    assert(res.upstream_resource() == &R1);
+    assert(res2.upstream_resource() == &R2);
+    assert(P1.alive == 0);
+    assert(P2.alive == 0);
+  }
+  {
+    const size_t Size = 64;
+    alignas(std::max_align_t) char Buff[Size];
+    char *End = Buff + Size;
+    Res R;
+    auto &P = R.getController();
+    ex::monotonic_buffer_resource res(Buff, Size, &R);
+    assert(res.upstream_resource() == &R);
+    assert(P.alive == 0);
+    void *mem1 = res.allocate(Size - 4, 1);
+    assert(in_range(mem1, Buff, End));
+    assert(P.alive == 0);
+    mem1 = res.allocate(4, 1);
+    assert(in_range(mem1, Buff, End));
+    assert(P.alive == 0);
+    mem1 = res.allocate(1, 1);
+    assert(P.alive == 1);
+    assert(!in_range(mem1, Buff, End));
+  }
+  {
+    const size_t Size = 512;
+    alignas(std::max_align_t) char Buff[Size];
+    char *End = Buff + Size;
+    Res R;
+    auto &P = R.getController();
+    ex::monotonic_buffer_resource res(Buff, Size, &R);
+    assert(res.upstream_resource() == &R);
+    assert(P.alive == 0);
+    void *mem1 = res.allocate(Size + 1, 8);
+    assert(!in_range(mem1, Buff, End));
+    assert(P.alive == 1);
+    assert(P.last_alloc_size >= Size + 1);
+    assert(P.last_alloc_align >= 8);
+  }
+}
Index: test/std/experimental/memory/memory.resource.monotonic.buffer/monotonic.buffer.ctor/buffer.pass.cpp
===================================================================
--- /dev/null
+++ test/std/experimental/memory/memory.resource.monotonic.buffer/monotonic.buffer.ctor/buffer.pass.cpp
@@ -0,0 +1,80 @@
+//===----------------------------------------------------------------------===//
+//
+//                     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.
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++98, c++03
+
+// <experimental/memory_resource>
+
+// monotonic_buffer_resource(void *buffer, size_t buffer_size, monotonic_buffer_resource*);
+
+#include <experimental/memory_resource>
+#include <cassert>
+
+#include "test_memory_resource.hpp"
+#include "count_new.hpp"
+
+namespace ex = std::experimental::pmr;
+
+int main() {
+  using Res = TestResource;
+  auto in_range = [](void *p, char *b, char *e) {
+    uintptr_t pint = reinterpret_cast<uintptr_t>(p);
+    uintptr_t bint = reinterpret_cast<uintptr_t>(b);
+    uintptr_t eint = reinterpret_cast<uintptr_t>(e);
+    return pint >= bint && pint < eint;
+  };
+  {
+    DisableAllocationGuard g;
+    Res R1;
+    AllocController &P1 = R1.getController();
+    char Buff1[42];
+    char Buff2[512];
+
+    ex::monotonic_buffer_resource res(Buff1, 42);
+    ex::set_default_resource(&R1);
+    ex::monotonic_buffer_resource res2(Buff2, 512);
+    assert(res.upstream_resource() == ex::new_delete_resource());
+    assert(res2.upstream_resource() == &R1);
+    assert(P1.alive == 0);
+  }
+  ex::set_default_resource(ex::new_delete_resource());
+  {
+    const size_t Size = 64;
+    alignas(std::max_align_t) char Buff[Size];
+    char *End = Buff + Size;
+    globalMemCounter.disableAllocations();
+    assert(globalMemCounter.checkOutstandingNewEq(0));
+    ex::monotonic_buffer_resource res(Buff, Size);
+    assert(res.upstream_resource() == ex::new_delete_resource());
+    void *mem1 = res.allocate(Size - 4, 1);
+    assert(in_range(mem1, Buff, End));
+    mem1 = res.allocate(4, 1);
+    assert(in_range(mem1, Buff, End));
+    globalMemCounter.enableAllocations();
+    mem1 = res.allocate(1, 1);
+    assert(globalMemCounter.checkOutstandingNewEq(1));
+    assert(!in_range(mem1, Buff, End));
+  }
+  {
+    const size_t Size = 512;
+    alignas(std::max_align_t) char Buff[Size];
+    char *End = Buff + Size;
+    Res R;
+    ex::set_default_resource(&R);
+    auto &P = R.getController();
+    ex::monotonic_buffer_resource res(Buff, Size);
+    assert(res.upstream_resource() == &R);
+    assert(P.alive == 0);
+    void *mem1 = res.allocate(Size + 1, 8);
+    assert(!in_range(mem1, Buff, End));
+    assert(P.alive == 1);
+    assert(P.last_alloc_size >= Size + 1);
+    assert(P.last_alloc_align >= 8);
+  }
+}
Index: src/experimental/memory_resource.cpp
===================================================================
--- src/experimental/memory_resource.cpp
+++ src/experimental/memory_resource.cpp
@@ -140,4 +140,8 @@
     return __default_memory_resource(true, __new_res);
 }
 
-_LIBCPP_END_NAMESPACE_LFTS_PMR
\ No newline at end of file
+monotonic_buffer_resource::~monotonic_buffer_resource() {
+  __alloc_.__release(__res_);
+}
+
+_LIBCPP_END_NAMESPACE_LFTS_PMR
Index: include/experimental/memory_resource
===================================================================
--- include/experimental/memory_resource
+++ include/experimental/memory_resource
@@ -78,35 +78,28 @@
 #include <cstdlib>
 #include <__debug>
 
+#include <cassert> // FIXME remove all usages of assert
+
 #if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
 #pragma GCC system_header
 #endif
 
 _LIBCPP_BEGIN_NAMESPACE_LFTS_PMR
 
-// Round __s up to next multiple of __a.
-inline _LIBCPP_INLINE_VISIBILITY
-size_t __aligned_allocation_size(size_t __s, size_t __a) _NOEXCEPT
-{
-    _LIBCPP_ASSERT(__s + __a > __s, "aligned allocation size overflows");
-    return (__s + __a - 1) & ~(__a - 1);
-}
-
 // 8.5, memory.resource
 class _LIBCPP_TYPE_VIS_ONLY memory_resource
 {
-    static const size_t __max_align = alignof(max_align_t);
 
 // 8.5.2, memory.resource.public
 public:
     virtual ~memory_resource() = default;
 
     _LIBCPP_INLINE_VISIBILITY
-    void* allocate(size_t __bytes, size_t __align = __max_align)
+    void* allocate(size_t __bytes, size_t __align = __max_align())
         { return do_allocate(__bytes, __align); }
 
     _LIBCPP_INLINE_VISIBILITY
-    void deallocate(void * __p, size_t __bytes, size_t __align = __max_align)
+    void deallocate(void * __p, size_t __bytes, size_t __align = __max_align())
         { do_deallocate(__p, __bytes, __align); }
 
     _LIBCPP_INLINE_VISIBILITY
@@ -342,10 +335,8 @@
                && is_same<typename _CTraits::pointer, char*>::value
                && is_same<typename _CTraits::void_pointer, void*>::value, "");
 
-    static const size_t _MaxAlign = alignof(max_align_t);
-
     using _Alloc = typename _CTraits::template rebind_alloc<
-            typename aligned_storage<_MaxAlign, _MaxAlign>::type
+            typename aligned_storage<__max_align(), __max_align()>::type
         >;
 
     using _ValueType = typename _Alloc::value_type;
@@ -387,15 +378,15 @@
                 "std::experimental::pmr::resource_adaptor<T>::do_allocate(size_t bytes, size_t align)"
                 " 'bytes' exceeds maximum supported size");
         }
-        size_t __s = __aligned_allocation_size(__bytes, _MaxAlign) / _MaxAlign;
+        size_t __s = __aligned_allocation_size(__bytes) / __max_align();
         return __alloc_.allocate(__s);
     }
 
     virtual void do_deallocate(void * __p, size_t __bytes, size_t)
     {
         _LIBCPP_ASSERT(__bytes <= __max_size(),
             "do_deallocate called for size which exceeds the maximum allocation size");
-        size_t __s = __aligned_allocation_size(__bytes, _MaxAlign) / _MaxAlign;
+        size_t __s = __aligned_allocation_size(__bytes) / __max_align();
         __alloc_.deallocate((_ValueType*)__p, __s);
     }
 
@@ -408,15 +399,269 @@
 private:
     _LIBCPP_INLINE_VISIBILITY
     size_t __max_size() const _NOEXCEPT {
-        return numeric_limits<size_t>::max() - _MaxAlign;
+        return numeric_limits<size_t>::max() - __max_align();
     }
 };
 
 template <class _Alloc>
 using resource_adaptor = __resource_adaptor_imp<
     typename allocator_traits<_Alloc>::template rebind_alloc<char>
   >;
 
+struct _LIBCPP_TYPE_VIS_ONLY pool_options {
+  size_t max_blocks_per_chunk = 0;
+  size_t largest_required_pool_block = 0;
+
+  // Implementation limits
+  static constexpr size_t __impl_max_blocks_per_chunk = 32;
+  static constexpr size_t __impl_smallest_block = 8;
+  static constexpr size_t __impl_largest_block = 4096;
+};
+
+inline _LIBCPP_INLINE_VISIBILITY size_t
+    __adjust_max_blocks_per_chunk(size_t __mbpc) _NOEXCEPT {
+  if (__mbpc == 0 || __mbpc > pool_options::__impl_max_blocks_per_chunk) {
+    return pool_options::__impl_max_blocks_per_chunk;
+  } else {
+    return __mbpc;
+  }
+}
+
+inline _LIBCPP_INLINE_VISIBILITY size_t
+    __adjust_largest_required_pool_block(size_t __lpb) _NOEXCEPT {
+  if (__lpb == 0 || __lpb > pool_options::__impl_largest_block) {
+    return pool_options::__impl_largest_block;
+  } else if (__lpb < pool_options::__impl_smallest_block) {
+    return pool_options::__impl_smallest_block;
+  } else {
+    return __lpb;
+  }
+}
+
+inline _LIBCPP_INLINE_VISIBILITY pool_options
+    __adjust_pool_options(pool_options __opts) _NOEXCEPT {
+  __opts.max_blocks_per_chunk =
+      __adjust_max_blocks_per_chunk(__opts.max_blocks_per_chunk);
+
+  __opts.largest_required_pool_block =
+      __adjust_largest_required_pool_block(__opts.largest_required_pool_block);
+
+  return __opts;
+}
+
+
+struct __single_linked_chunk_node {
+  __single_linked_chunk_node *__next_;
+  size_t __size_;
+  alignas(std::max_align_t) char __memory_[0];
+
+  static inline _LIBCPP_INLINE_VISIBILITY
+  void __push(__single_linked_chunk_node **__head_ptr,
+              __single_linked_chunk_node *__n) {
+    __n->__next_ = *__head_ptr;
+    *__head_ptr = __n;
+  }
+
+  static inline _LIBCPP_INLINE_VISIBILITY
+  void __remove(__single_linked_chunk_node **__head_ptr,
+                __single_linked_chunk_node *__n)
+  {
+    auto **__prev = __head_ptr;
+    auto *__idx = *__head_ptr;
+    while (__idx != __n) {
+      __prev = &__idx->__next_;
+      __idx = *__prev;
+    }
+    *__prev = __n->__next_;
+    __n->__next_ = nullptr;
+  }
+};
+
+
+struct __double_linked_chunk_node {
+  __double_linked_chunk_node *__next_;
+  __double_linked_chunk_node **__prev_next_addr_;
+  size_t __size_;
+  alignas(std::max_align_t) char __memory_[0];
+
+  static inline _LIBCPP_INLINE_VISIBILITY
+  void __push(__double_linked_chunk_node **__head_ptr,
+                     __double_linked_chunk_node *__n) {
+    __n->__next_ = *__head_ptr;
+    __n->__prev_next_addr_ = __head_ptr;
+    if (*__head_ptr)
+      (*__head_ptr)->__prev_next_addr_ = &__n->__next_;
+    *__head_ptr = __n;
+  }
+
+  static inline _LIBCPP_INLINE_VISIBILITY
+  void __remove(__double_linked_chunk_node **,
+                __double_linked_chunk_node *__n) {
+    *(__n->__prev_next_addr_) = __n->__next_;
+    if (__n->__next_)
+      __n->__next_->__prev_next_addr_ = __n->__prev_next_addr_;
+  }
+};
+
+template <class _ChunkNode>
+class _LIBCPP_TYPE_VIS_ONLY __basic_chunk_allocator {
+  static_assert(is_standard_layout<_ChunkNode>::value,
+                "_ChunkNode must be standard layout");
+  static_assert(is_trivially_destructible<_ChunkNode>::value,
+                "_ChunkNode must be trivially destructible");
+
+  static inline _LIBCPP_INLINE_VISIBILITY
+  _ChunkNode *__memory_to_chunk(void *__p) _NOEXCEPT {
+    return static_cast<_ChunkNode *>(static_cast<void *>(
+        static_cast<char *>(__p) - offsetof(_ChunkNode, __memory_)));
+  }
+public:
+  _LIBCPP_INLINE_VISIBILITY
+  __basic_chunk_allocator() : __head_(nullptr) {}
+  __basic_chunk_allocator(__basic_chunk_allocator const &) = delete;
+  __basic_chunk_allocator &operator=(__basic_chunk_allocator const &) = delete;
+
+  _LIBCPP_INLINE_VISIBILITY
+  void *__allocate(memory_resource *__res, size_t __s) {
+    if (__s == 0)
+      __s = 1;
+    const size_t __alloc_size =
+        __aligned_allocation_size(sizeof(_ChunkNode) + __s);
+    return __insert_new_chunk(__res->allocate(__alloc_size, __max_align()),
+                              __alloc_size);
+  }
+
+  _LIBCPP_INLINE_VISIBILITY
+  void __deallocate(memory_resource *__res, void *__p) _NOEXCEPT {
+    if (!__p)
+      return;
+    _ChunkNode *const __n = __memory_to_chunk(__p);
+    _ChunkNode::__remove(&__head_, __n);
+    __res->deallocate(__n, __n->__size_, __max_align());
+  }
+
+  _LIBCPP_INLINE_VISIBILITY
+  void __release(memory_resource *__res) _NOEXCEPT {
+    while (__head_) {
+      auto *__p = __head_;
+      __head_ = __head_->__next_;
+      __res->deallocate(__p, __p->__size_, __max_align());
+    }
+  }
+
+private:
+  _LIBCPP_INLINE_VISIBILITY
+  void *__insert_new_chunk(void *__p, size_t __s) _NOEXCEPT {
+    _ChunkNode *__n = ::new (__p) _ChunkNode;
+    __n->__size_ = __s;
+    _ChunkNode::__push(&__head_, __n);
+    return static_cast<void *>(&__n->__memory_);
+  }
+
+private:
+  _ChunkNode *__head_;
+};
+
+typedef __basic_chunk_allocator<__double_linked_chunk_node> __chunk_allocator;
+typedef __basic_chunk_allocator<__single_linked_chunk_node>
+    __single_linked_chunk_allocator;
+
+
+class _LIBCPP_TYPE_VIS_ONLY monotonic_buffer_resource : public memory_resource {
+  static constexpr size_t __min_buf_size = 512;
+
+public:
+  explicit monotonic_buffer_resource(memory_resource *__res)
+      : __buf_(nullptr), __left_(0), __next_buf_size_(__min_buf_size),
+        __res_(__res) { }
+
+  monotonic_buffer_resource(size_t __init_size, memory_resource *__res)
+      : __buf_(nullptr), __left_(0),
+        __next_buf_size_(__init_size), __res_(__res) {}
+
+  monotonic_buffer_resource(void *__buf, size_t __buf_size,
+                            memory_resource *__res)
+      : __buf_(__buf), __left_(__buf_size), __next_buf_size_(__min_buf_size),
+        __res_(__res) {}
+
+  monotonic_buffer_resource()
+      : monotonic_buffer_resource(get_default_resource()) {}
+
+  explicit monotonic_buffer_resource(size_t __init_size)
+      : monotonic_buffer_resource(__init_size, get_default_resource()) {}
+
+  monotonic_buffer_resource(void *__buf, size_t __buf_size)
+      : monotonic_buffer_resource(__buf, __buf_size, get_default_resource()) {}
+
+  monotonic_buffer_resource(monotonic_buffer_resource const &) = delete;
+
+  _LIBCPP_FUNC_VIS
+  virtual ~monotonic_buffer_resource(); // KEY FUNCTION
+
+  monotonic_buffer_resource &
+      operator=(monotonic_buffer_resource const &) = delete;
+
+  _LIBCPP_INLINE_VISIBILITY
+  void release() {
+    __alloc_.__release(__res_);
+    __buf_ = nullptr;
+    __left_ = 0;
+  }
+
+  _LIBCPP_INLINE_VISIBILITY
+  memory_resource *upstream_resource() const _NOEXCEPT { return __res_; }
+
+protected:
+  virtual void *do_allocate(size_t __bytes, size_t __align) {
+    if (void *__p = __take_from_buf(__bytes, __align))
+      return __p;
+    return __alloc_from_upstream(__bytes, __align);
+  }
+
+  virtual void do_deallocate(void *, size_t, size_t) { }
+
+  virtual bool do_is_equal(memory_resource const &__other) const _NOEXCEPT {
+    return this ==
+        dynamic_cast<monotonic_buffer_resource const *>(&__other);
+  }
+
+private:
+  _LIBCPP_INLINE_VISIBILITY
+  void *__take_from_buf(size_t __s, size_t __a) _NOEXCEPT {
+    void* __p = nullptr;
+    if (__buf_ && (__p = _VSTD::align(__a, __s, __buf_, __left_))) {
+        __buf_ = static_cast<void*>(static_cast<char*>(__p) + __s);
+        __left_ -= __s;
+    }
+    return __p;
+  }
+
+  _LIBCPP_INLINE_VISIBILITY
+  void *__alloc_from_upstream(size_t __s, size_t __a) {
+    size_t __alloc_size = std::max<size_t>(
+        __aligned_allocation_size(__s, __a),
+        __next_buf_size_);
+    __increment_buf_size(__alloc_size);
+    __buf_ = __alloc_.__allocate(__res_, __alloc_size);
+    __left_ = __alloc_size;
+    return __take_from_buf(__s, __a);
+  }
+
+  _LIBCPP_INLINE_VISIBILITY
+  void __increment_buf_size(size_t __s) _NOEXCEPT {
+    size_t const __possible_next =
+        _VSTD::max(__round_up_pow_2(__s), __next_buf_size_ << 1);
+    __next_buf_size_ = _VSTD::max(__possible_next, __next_buf_size_);
+  }
+
+private:
+  void *__buf_;
+  size_t __left_;
+  size_t __next_buf_size_;
+  memory_resource *__res_;
+  __single_linked_chunk_allocator __alloc_;
+};
+
 _LIBCPP_END_NAMESPACE_LFTS_PMR
 
 #endif /* _LIBCPP_EXPERIMENTAL_MEMORY_RESOURCE */
Index: include/experimental/__memory
===================================================================
--- include/experimental/__memory
+++ include/experimental/__memory
@@ -13,6 +13,7 @@
 
 #include <experimental/__config>
 #include <experimental/utility> // for erased_type
+#include <algorithm>
 #include <__functional_base>
 #include <type_traits>
 
@@ -85,6 +86,48 @@
        );
 }
 
+inline _LIBCPP_INLINE_VISIBILITY
+size_t __is_power2(size_t __bc)
+{
+    return  __bc == 1 || __bc == 2 || (__bc && !(__bc & (__bc - 1)));
+}
+
+inline _LIBCPP_INLINE_VISIBILITY
+size_t __next_pow2(size_t __n)
+{
+    return (__n < 2) ? __n + 1
+        : (size_t(1) << (std::numeric_limits<size_t>::digits - __clz(__n-1)));
+}
+
+inline _LIBCPP_INLINE_VISIBILITY
+size_t __round_up_pow_2(size_t __s) _NOEXCEPT
+{
+    return __is_power2(__s) ? __s : __next_pow2(__s);
+}
+
+inline _LIBCPP_INLINE_VISIBILITY
+constexpr size_t __max_align() _NOEXCEPT
+{
+    return alignment_of<max_align_t>::value;
+}
+
+// The alignment is 2 ^ (index of lowest set bit)
+inline _LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR
+size_t __fundamental_alignment(size_t __s) _NOEXCEPT
+{
+    return (__s | __max_align())
+        & ~((__s | __max_align()) - 1);
+}
+
+// Round __s up to next multiple of __a.
+inline _LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR
+size_t __aligned_allocation_size(size_t __s, size_t __a = __max_align()) _NOEXCEPT
+{
+    _LIBCPP_ASSERT(__s + __a > __s, "aligned allocation size overflows");
+    return (__s + __a - 1) & ~(__a - 1);
+}
+
+
 _LIBCPP_END_NAMESPACE_LFTS
 
 #endif /* _LIBCPP_EXPERIMENTAL___MEMORY */
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
  • [PATCH] D27402: [libc++] Imp... Eric Fiselier via Phabricator via cfe-commits

Reply via email to