Quuxplusone updated this revision to Diff 173613.
Quuxplusone added a comment.
Herald added a subscriber: libcxx-commits.

Rebased on master. @ericwf @ldionne ping please?


Repository:
  rCXX libc++

https://reviews.llvm.org/D47358

Files:
  include/experimental/memory_resource
  src/experimental/memory_resource.cpp
  
test/libcxx/experimental/memory/memory.resource.pool/memory.resource.pool.mem/unsynchronized_buffer.pass.cpp
  
test/std/experimental/memory/memory.resource.pool/memory.resource.pool.ctor/ctor_does_not_allocate.pass.cpp
  
test/std/experimental/memory/memory.resource.pool/memory.resource.pool.ctor/sync_with_default_resource.pass.cpp
  
test/std/experimental/memory/memory.resource.pool/memory.resource.pool.ctor/unsync_with_default_resource.pass.cpp
  
test/std/experimental/memory/memory.resource.pool/memory.resource.pool.mem/equality.pass.cpp
  
test/std/experimental/memory/memory.resource.pool/memory.resource.pool.mem/sync_allocate.pass.cpp
  
test/std/experimental/memory/memory.resource.pool/memory.resource.pool.mem/sync_allocate_overaligned_request.pass.cpp
  
test/std/experimental/memory/memory.resource.pool/memory.resource.pool.mem/sync_allocate_reuse_blocks.pass.cpp
  
test/std/experimental/memory/memory.resource.pool/memory.resource.pool.mem/sync_deallocate_matches_allocate.pass.cpp
  
test/std/experimental/memory/memory.resource.pool/memory.resource.pool.mem/unsync_allocate.pass.cpp
  
test/std/experimental/memory/memory.resource.pool/memory.resource.pool.mem/unsync_allocate_overaligned_request.pass.cpp
  
test/std/experimental/memory/memory.resource.pool/memory.resource.pool.mem/unsync_allocate_reuse_blocks.pass.cpp
  
test/std/experimental/memory/memory.resource.pool/memory.resource.pool.mem/unsync_deallocate_matches_allocate.pass.cpp
  test/std/experimental/memory/memory.resource.pool/pool_options.pass.cpp
  test/support/count_new.hpp

Index: test/support/count_new.hpp
===================================================================
--- test/support/count_new.hpp
+++ test/support/count_new.hpp
@@ -211,6 +211,11 @@
         return disable_checking || n != delete_called;
     }
 
+    bool checkDeleteCalledGreaterThan(int n) const
+    {
+        return disable_checking || delete_called > n;
+    }
+
     bool checkAlignedNewCalledEq(int n) const
     {
         return disable_checking || n == aligned_new_called;
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.
+//
+//===----------------------------------------------------------------------===//
+
+// REQUIRES: c++experimental
+// UNSUPPORTED: c++98, c++03, c++11, c++14
+// UNSUPPORTED: apple-clang-7
+
+// <experimental/memory_resource>
+
+// struct pool_options
+
+#include <cassert>
+#include <experimental/memory_resource>
+
+int main()
+{
+    const std::experimental::pmr::pool_options p;
+    assert(p.max_blocks_per_chunk == 0);
+    assert(p.largest_required_pool_block == 0);
+}
Index: test/std/experimental/memory/memory.resource.pool/memory.resource.pool.mem/unsync_deallocate_matches_allocate.pass.cpp
===================================================================
--- /dev/null
+++ test/std/experimental/memory/memory.resource.pool/memory.resource.pool.mem/unsync_deallocate_matches_allocate.pass.cpp
@@ -0,0 +1,110 @@
+//===----------------------------------------------------------------------===//
+//
+//                     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 unsynchronized_pool_resource
+
+#include <experimental/memory_resource>
+#include <cassert>
+#include <vector>
+
+struct allocation_record {
+    size_t bytes;
+    size_t align;
+    explicit allocation_record(size_t b, size_t a) : bytes(b), align(a) {}
+    bool operator==(const allocation_record& rhs) const {
+        return (bytes == rhs.bytes) && (align == rhs.align);
+    }
+    bool operator<(const allocation_record& rhs) const {
+        if (bytes != rhs.bytes) return (bytes < rhs.bytes);
+        return (align < rhs.align);
+    }
+};
+
+class test_resource : public std::experimental::pmr::memory_resource {
+    void *do_allocate(size_t bytes, size_t align) override {
+        void *result = std::experimental::pmr::new_delete_resource()->allocate(bytes, align);
+        successful_allocations.emplace_back(bytes, align);
+        return result;
+    }
+    void do_deallocate(void *p, size_t bytes, size_t align) override {
+        deallocations.emplace_back(bytes, align);
+        return std::experimental::pmr::new_delete_resource()->deallocate(p, bytes, align);
+    }
+    bool do_is_equal(const std::experimental::pmr::memory_resource&) const noexcept override {
+        return false;
+    }
+public:
+    std::vector<allocation_record> successful_allocations;
+    std::vector<allocation_record> deallocations;
+};
+
+template<class F>
+void test_allocation_pattern(F do_pattern)
+{
+    test_resource tr;
+    std::experimental::pmr::pool_options opts { 0, 256 };
+    std::experimental::pmr::unsynchronized_pool_resource uspr(opts, &tr);
+
+    try {
+        do_pattern(uspr);
+    } catch (const std::bad_alloc&) {}
+    uspr.release();
+
+    assert(tr.successful_allocations.size() == tr.deallocations.size());
+    std::sort(tr.successful_allocations.begin(), tr.successful_allocations.end());
+    std::sort(tr.deallocations.begin(), tr.deallocations.end());
+    assert(tr.successful_allocations == tr.deallocations);
+}
+
+template<size_t Bytes, size_t Align>
+auto foo() {
+    return [=](auto& mr) {
+        void *p = mr.allocate(Bytes, Align);
+        mr.deallocate(p, Bytes, Align);
+    };
+}
+
+int main()
+{
+    test_allocation_pattern(foo<2, 1>());
+    test_allocation_pattern(foo<2, 8>());
+    test_allocation_pattern(foo<2, 64>());
+    test_allocation_pattern(foo<128, 1>());
+    test_allocation_pattern(foo<128, 8>());
+    test_allocation_pattern(foo<128, 64>());
+    test_allocation_pattern(foo<1024, 1>());
+    test_allocation_pattern(foo<1024, 8>());
+    test_allocation_pattern(foo<1024, 64>());
+
+    test_allocation_pattern([](auto& mr) {
+        void *p1 = mr.allocate(2, 1);
+        void *p2 = mr.allocate(2, 8);
+        void *p3 = mr.allocate(2, 64);
+        void *p4 = mr.allocate(128, 1);
+        void *p5 = mr.allocate(128, 8);
+        void *p6 = mr.allocate(128, 64);
+        void *p7 = mr.allocate(1024, 1);
+        void *p8 = mr.allocate(1024, 8);
+        void *p9 = mr.allocate(1024, 64);
+        mr.deallocate(p1, 2, 1);
+        mr.deallocate(p2, 2, 8);
+        mr.deallocate(p3, 2, 64);
+        mr.deallocate(p4, 128, 1);
+        mr.deallocate(p5, 128, 8);
+        mr.deallocate(p6, 128, 64);
+        mr.deallocate(p7, 1024, 1);
+        mr.deallocate(p8, 1024, 8);
+        mr.deallocate(p9, 1024, 64);
+    });
+}
Index: test/std/experimental/memory/memory.resource.pool/memory.resource.pool.mem/unsync_allocate_reuse_blocks.pass.cpp
===================================================================
--- /dev/null
+++ test/std/experimental/memory/memory.resource.pool/memory.resource.pool.mem/unsync_allocate_reuse_blocks.pass.cpp
@@ -0,0 +1,54 @@
+//===----------------------------------------------------------------------===//
+//
+//                     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 unsynchronized_pool_resource
+
+#include <experimental/memory_resource>
+#include <cassert>
+
+#include "count_new.hpp"
+
+static bool is_aligned_to(void *p, size_t alignment)
+{
+    void *p2 = p;
+    size_t space = 1;
+    void *result = std::align(alignment, 1, p2, space);
+    return (result == p);
+}
+
+int main()
+{
+    globalMemCounter.reset();
+    std::experimental::pmr::pool_options opts { 1, 256 };
+    std::experimental::pmr::unsynchronized_pool_resource unsync1(opts, std::experimental::pmr::new_delete_resource());
+    std::experimental::pmr::memory_resource & r1 = unsync1;
+
+    void *ret = r1.allocate(8);
+    assert(ret != nullptr);
+    assert(is_aligned_to(ret, 8));
+    assert(globalMemCounter.checkNewCalledGreaterThan(0));
+    int new_called = globalMemCounter.new_called;
+
+    // After deallocation, the pool for 8-byte blocks should have at least one vacancy.
+    r1.deallocate(ret, 8);
+    assert(globalMemCounter.new_called == new_called);
+    assert(globalMemCounter.checkDeleteCalledEq(0));
+
+    // This should return an existing block from the pool: no new allocations.
+    ret = r1.allocate(8);
+    assert(ret != nullptr);
+    assert(is_aligned_to(ret, 8));
+    assert(globalMemCounter.new_called == new_called);
+    assert(globalMemCounter.checkDeleteCalledEq(0));
+}
Index: test/std/experimental/memory/memory.resource.pool/memory.resource.pool.mem/unsync_allocate_overaligned_request.pass.cpp
===================================================================
--- /dev/null
+++ test/std/experimental/memory/memory.resource.pool/memory.resource.pool.mem/unsync_allocate_overaligned_request.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 unsynchronized_pool_resource
+
+#include <experimental/memory_resource>
+#include <cassert>
+
+#include "count_new.hpp"
+
+static bool is_aligned_to(void *p, size_t alignment)
+{
+    void *p2 = p;
+    size_t space = 1;
+    void *result = std::align(alignment, 1, p2, space);
+    return (result == p);
+}
+
+int main()
+{
+    globalMemCounter.reset();
+    std::experimental::pmr::pool_options opts { 1, 1024 };
+    std::experimental::pmr::unsynchronized_pool_resource unsync1(opts, std::experimental::pmr::new_delete_resource());
+    std::experimental::pmr::memory_resource & r1 = unsync1;
+
+    constexpr size_t big_alignment = 8 * alignof(std::max_align_t);
+    static_assert(big_alignment > 4);
+
+    assert(globalMemCounter.checkNewCalledEq(0));
+
+    void *ret = r1.allocate(2048, big_alignment);
+    assert(ret != nullptr);
+    assert(is_aligned_to(ret, big_alignment));
+    assert(globalMemCounter.checkNewCalledGreaterThan(0));
+
+    ret = r1.allocate(16, 4);
+    assert(ret != nullptr);
+    assert(is_aligned_to(ret, 4));
+    assert(globalMemCounter.checkNewCalledGreaterThan(1));
+}
Index: test/std/experimental/memory/memory.resource.pool/memory.resource.pool.mem/unsync_allocate.pass.cpp
===================================================================
--- /dev/null
+++ test/std/experimental/memory/memory.resource.pool/memory.resource.pool.mem/unsync_allocate.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 unsynchronized_pool_resource
+
+#include <experimental/memory_resource>
+#include <cassert>
+
+#include "count_new.hpp"
+
+int main()
+{
+    {
+        globalMemCounter.reset();
+
+        std::experimental::pmr::unsynchronized_pool_resource unsync1(std::experimental::pmr::new_delete_resource());
+        std::experimental::pmr::memory_resource & r1 = unsync1;
+
+        void *ret = r1.allocate(50);
+        assert(ret);
+        assert(globalMemCounter.checkNewCalledGreaterThan(0));
+        assert(globalMemCounter.checkDeleteCalledEq(0));
+
+        r1.deallocate(ret, 50);
+        unsync1.release();
+        assert(globalMemCounter.checkDeleteCalledGreaterThan(0));
+        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.checkDeleteCalledGreaterThan(0));
+    assert(globalMemCounter.checkOutstandingNewEq(0));
+}
Index: test/std/experimental/memory/memory.resource.pool/memory.resource.pool.mem/sync_deallocate_matches_allocate.pass.cpp
===================================================================
--- /dev/null
+++ test/std/experimental/memory/memory.resource.pool/memory.resource.pool.mem/sync_deallocate_matches_allocate.pass.cpp
@@ -0,0 +1,110 @@
+//===----------------------------------------------------------------------===//
+//
+//                     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 synchronized_pool_resource
+
+#include <experimental/memory_resource>
+#include <cassert>
+#include <vector>
+
+struct allocation_record {
+    size_t bytes;
+    size_t align;
+    explicit allocation_record(size_t b, size_t a) : bytes(b), align(a) {}
+    bool operator==(const allocation_record& rhs) const {
+        return (bytes == rhs.bytes) && (align == rhs.align);
+    }
+    bool operator<(const allocation_record& rhs) const {
+        if (bytes != rhs.bytes) return (bytes < rhs.bytes);
+        return (align < rhs.align);
+    }
+};
+
+class test_resource : public std::experimental::pmr::memory_resource {
+    void *do_allocate(size_t bytes, size_t align) override {
+        void *result = std::experimental::pmr::new_delete_resource()->allocate(bytes, align);
+        successful_allocations.emplace_back(bytes, align);
+        return result;
+    }
+    void do_deallocate(void *p, size_t bytes, size_t align) override {
+        deallocations.emplace_back(bytes, align);
+        return std::experimental::pmr::new_delete_resource()->deallocate(p, bytes, align);
+    }
+    bool do_is_equal(const std::experimental::pmr::memory_resource&) const noexcept override {
+        return false;
+    }
+public:
+    std::vector<allocation_record> successful_allocations;
+    std::vector<allocation_record> deallocations;
+};
+
+template<class F>
+void test_allocation_pattern(F do_pattern)
+{
+    test_resource tr;
+    std::experimental::pmr::pool_options opts { 0, 256 };
+    std::experimental::pmr::synchronized_pool_resource spr(opts, &tr);
+
+    try {
+        do_pattern(spr);
+    } catch (const std::bad_alloc&) {}
+    spr.release();
+
+    assert(tr.successful_allocations.size() == tr.deallocations.size());
+    std::sort(tr.successful_allocations.begin(), tr.successful_allocations.end());
+    std::sort(tr.deallocations.begin(), tr.deallocations.end());
+    assert(tr.successful_allocations == tr.deallocations);
+}
+
+template<size_t Bytes, size_t Align>
+auto foo() {
+    return [=](auto& mr) {
+        void *p = mr.allocate(Bytes, Align);
+        mr.deallocate(p, Bytes, Align);
+    };
+}
+
+int main()
+{
+    test_allocation_pattern(foo<2, 1>());
+    test_allocation_pattern(foo<2, 8>());
+    test_allocation_pattern(foo<2, 64>());
+    test_allocation_pattern(foo<128, 1>());
+    test_allocation_pattern(foo<128, 8>());
+    test_allocation_pattern(foo<128, 64>());
+    test_allocation_pattern(foo<1024, 1>());
+    test_allocation_pattern(foo<1024, 8>());
+    test_allocation_pattern(foo<1024, 64>());
+
+    test_allocation_pattern([](auto& mr) {
+        void *p1 = mr.allocate(2, 1);
+        void *p2 = mr.allocate(2, 8);
+        void *p3 = mr.allocate(2, 64);
+        void *p4 = mr.allocate(128, 1);
+        void *p5 = mr.allocate(128, 8);
+        void *p6 = mr.allocate(128, 64);
+        void *p7 = mr.allocate(1024, 1);
+        void *p8 = mr.allocate(1024, 8);
+        void *p9 = mr.allocate(1024, 64);
+        mr.deallocate(p1, 2, 1);
+        mr.deallocate(p2, 2, 8);
+        mr.deallocate(p3, 2, 64);
+        mr.deallocate(p4, 128, 1);
+        mr.deallocate(p5, 128, 8);
+        mr.deallocate(p6, 128, 64);
+        mr.deallocate(p7, 1024, 1);
+        mr.deallocate(p8, 1024, 8);
+        mr.deallocate(p9, 1024, 64);
+    });
+}
Index: test/std/experimental/memory/memory.resource.pool/memory.resource.pool.mem/sync_allocate_reuse_blocks.pass.cpp
===================================================================
--- /dev/null
+++ test/std/experimental/memory/memory.resource.pool/memory.resource.pool.mem/sync_allocate_reuse_blocks.pass.cpp
@@ -0,0 +1,54 @@
+//===----------------------------------------------------------------------===//
+//
+//                     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 synchronized_pool_resource
+
+#include <experimental/memory_resource>
+#include <cassert>
+
+#include "count_new.hpp"
+
+static bool is_aligned_to(void *p, size_t alignment)
+{
+    void *p2 = p;
+    size_t space = 1;
+    void *result = std::align(alignment, 1, p2, space);
+    return (result == p);
+}
+
+int main()
+{
+    globalMemCounter.reset();
+    std::experimental::pmr::pool_options opts { 1, 256 };
+    std::experimental::pmr::synchronized_pool_resource sync1(opts, std::experimental::pmr::new_delete_resource());
+    std::experimental::pmr::memory_resource & r1 = sync1;
+
+    void *ret = r1.allocate(8);
+    assert(ret != nullptr);
+    assert(is_aligned_to(ret, 8));
+    assert(globalMemCounter.checkNewCalledGreaterThan(0));
+    int new_called = globalMemCounter.new_called;
+
+    // After deallocation, the pool for 8-byte blocks should have at least one vacancy.
+    r1.deallocate(ret, 8);
+    assert(globalMemCounter.new_called == new_called);
+    assert(globalMemCounter.checkDeleteCalledEq(0));
+
+    // This should return an existing block from the pool: no new allocations.
+    ret = r1.allocate(8);
+    assert(ret != nullptr);
+    assert(is_aligned_to(ret, 8));
+    assert(globalMemCounter.new_called == new_called);
+    assert(globalMemCounter.checkDeleteCalledEq(0));
+}
Index: test/std/experimental/memory/memory.resource.pool/memory.resource.pool.mem/sync_allocate_overaligned_request.pass.cpp
===================================================================
--- /dev/null
+++ test/std/experimental/memory/memory.resource.pool/memory.resource.pool.mem/sync_allocate_overaligned_request.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 synchronized_pool_resource
+
+#include <experimental/memory_resource>
+#include <cassert>
+
+#include "count_new.hpp"
+
+static bool is_aligned_to(void *p, size_t alignment)
+{
+    void *p2 = p;
+    size_t space = 1;
+    void *result = std::align(alignment, 1, p2, space);
+    return (result == p);
+}
+
+int main()
+{
+    globalMemCounter.reset();
+    std::experimental::pmr::pool_options opts { 1, 1024 };
+    std::experimental::pmr::synchronized_pool_resource sync1(opts, std::experimental::pmr::new_delete_resource());
+    std::experimental::pmr::memory_resource & r1 = sync1;
+
+    constexpr size_t big_alignment = 8 * alignof(std::max_align_t);
+    static_assert(big_alignment > 4);
+
+    assert(globalMemCounter.checkNewCalledEq(0));
+
+    void *ret = r1.allocate(2048, big_alignment);
+    assert(ret != nullptr);
+    assert(is_aligned_to(ret, big_alignment));
+    assert(globalMemCounter.checkNewCalledGreaterThan(0));
+
+    ret = r1.allocate(16, 4);
+    assert(ret != nullptr);
+    assert(is_aligned_to(ret, 4));
+    assert(globalMemCounter.checkNewCalledGreaterThan(1));
+}
Index: test/std/experimental/memory/memory.resource.pool/memory.resource.pool.mem/sync_allocate.pass.cpp
===================================================================
--- /dev/null
+++ test/std/experimental/memory/memory.resource.pool/memory.resource.pool.mem/sync_allocate.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 synchronized_pool_resource
+
+#include <experimental/memory_resource>
+#include <cassert>
+
+#include "count_new.hpp"
+
+int main()
+{
+    {
+        globalMemCounter.reset();
+
+        std::experimental::pmr::synchronized_pool_resource sync1(std::experimental::pmr::new_delete_resource());
+        std::experimental::pmr::memory_resource & r1 = sync1;
+
+        void *ret = r1.allocate(50);
+        assert(ret);
+        assert(globalMemCounter.checkNewCalledGreaterThan(0));
+        assert(globalMemCounter.checkDeleteCalledEq(0));
+
+        r1.deallocate(ret, 50);
+        sync1.release();
+        assert(globalMemCounter.checkDeleteCalledGreaterThan(0));
+        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.checkDeleteCalledGreaterThan(0));
+    assert(globalMemCounter.checkOutstandingNewEq(0));
+}
Index: test/std/experimental/memory/memory.resource.pool/memory.resource.pool.mem/equality.pass.cpp
===================================================================
--- /dev/null
+++ test/std/experimental/memory/memory.resource.pool/memory.resource.pool.mem/equality.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
+
+// <experimental/memory_resource>
+
+// class synchronized_pool_resource
+// class unsynchronized_pool_resource
+
+#include <experimental/memory_resource>
+#include <new>
+#include <type_traits>
+#include <cassert>
+
+#include "count_new.hpp"
+
+class assert_on_compare : public std::experimental::pmr::memory_resource
+{
+    void * do_allocate(size_t, size_t) override
+    { assert(false); }
+
+    void do_deallocate(void *, size_t, size_t) override
+    { assert(false); }
+
+    bool do_is_equal(std::experimental::pmr::memory_resource const &) const noexcept override
+    { assert(false); }
+};
+
+template<class PoolResource>
+void test()
+{
+    // Same type
+    {
+        PoolResource pr1;
+        PoolResource pr2;
+        assert(pr1 == pr1);
+        assert(pr1 != pr2);
+
+        std::experimental::pmr::memory_resource & mr1 = pr1;
+        std::experimental::pmr::memory_resource & mr2 = pr2;
+        assert(mr1 == mr1);
+        assert(mr1 != mr2);
+        assert(mr1 == pr1);
+        assert(pr1 == mr1);
+        assert(mr1 != pr2);
+        assert(pr2 != mr1);
+    }
+    // Different types
+    {
+        PoolResource pr1;
+        std::experimental::pmr::memory_resource & mr1 = pr1;
+        assert_on_compare c;
+        std::experimental::pmr::memory_resource & mr2 = c;
+        assert(mr1 != mr2);
+        assert(!(mr1 == mr2));
+    }
+}
+
+int main()
+{
+    test<std::experimental::pmr::synchronized_pool_resource>();
+    test<std::experimental::pmr::unsynchronized_pool_resource>();
+}
Index: test/std/experimental/memory/memory.resource.pool/memory.resource.pool.ctor/unsync_with_default_resource.pass.cpp
===================================================================
--- /dev/null
+++ test/std/experimental/memory/memory.resource.pool/memory.resource.pool.ctor/unsync_with_default_resource.pass.cpp
@@ -0,0 +1,41 @@
+//===----------------------------------------------------------------------===//
+//
+//                     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 unsynchronized_pool_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);
+    {
+        std::experimental::pmr::pool_options opts { 0, 0 };
+        std::experimental::pmr::unsynchronized_pool_resource r1;
+        std::experimental::pmr::unsynchronized_pool_resource r2(opts);
+        assert(r1.upstream_resource() == expected);
+        assert(r2.upstream_resource() == expected);
+    }
+
+    expected = std::experimental::pmr::new_delete_resource();
+    std::experimental::pmr::set_default_resource(expected);
+    {
+        std::experimental::pmr::pool_options opts { 1024, 2048 };
+        std::experimental::pmr::unsynchronized_pool_resource r1;
+        std::experimental::pmr::unsynchronized_pool_resource r2(opts);
+        assert(r1.upstream_resource() == expected);
+        assert(r2.upstream_resource() == expected);
+    }
+}
Index: test/std/experimental/memory/memory.resource.pool/memory.resource.pool.ctor/sync_with_default_resource.pass.cpp
===================================================================
--- /dev/null
+++ test/std/experimental/memory/memory.resource.pool/memory.resource.pool.ctor/sync_with_default_resource.pass.cpp
@@ -0,0 +1,41 @@
+//===----------------------------------------------------------------------===//
+//
+//                     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 synchronized_pool_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);
+    {
+        std::experimental::pmr::pool_options opts { 0, 0 };
+        std::experimental::pmr::synchronized_pool_resource r1;
+        std::experimental::pmr::synchronized_pool_resource r2(opts);
+        assert(r1.upstream_resource() == expected);
+        assert(r2.upstream_resource() == expected);
+    }
+
+    expected = std::experimental::pmr::new_delete_resource();
+    std::experimental::pmr::set_default_resource(expected);
+    {
+        std::experimental::pmr::pool_options opts { 1024, 2048 };
+        std::experimental::pmr::synchronized_pool_resource r1;
+        std::experimental::pmr::synchronized_pool_resource r2(opts);
+        assert(r1.upstream_resource() == expected);
+        assert(r2.upstream_resource() == expected);
+    }
+}
Index: test/std/experimental/memory/memory.resource.pool/memory.resource.pool.ctor/ctor_does_not_allocate.pass.cpp
===================================================================
--- /dev/null
+++ test/std/experimental/memory/memory.resource.pool/memory.resource.pool.ctor/ctor_does_not_allocate.pass.cpp
@@ -0,0 +1,47 @@
+//===----------------------------------------------------------------------===//
+//
+//                     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 synchronized_pool_resource
+// class unsynchronized_pool_resource
+
+#include <experimental/memory_resource>
+#include <cassert>
+
+#include "count_new.hpp"
+
+template<class PoolResource>
+void test()
+{
+    // Constructing a pool 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());
+
+    PoolResource r1;
+    assert(globalMemCounter.checkNewCalledEq(0));
+
+    PoolResource r2(std::experimental::pmr::pool_options{ 1024, 2048 });
+    assert(globalMemCounter.checkNewCalledEq(0));
+
+    PoolResource r3(std::experimental::pmr::pool_options{ 1024, 2048 }, std::experimental::pmr::new_delete_resource());
+    assert(globalMemCounter.checkNewCalledEq(0));
+}
+
+int main()
+{
+    test<std::experimental::pmr::unsynchronized_pool_resource>();
+    test<std::experimental::pmr::synchronized_pool_resource>();
+}
Index: test/libcxx/experimental/memory/memory.resource.pool/memory.resource.pool.mem/unsynchronized_buffer.pass.cpp
===================================================================
--- /dev/null
+++ test/libcxx/experimental/memory/memory.resource.pool/memory.resource.pool.mem/unsynchronized_buffer.pass.cpp
@@ -0,0 +1,208 @@
+//===----------------------------------------------------------------------===//
+//
+//                     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>
+
+// struct pool_options
+// class unsynchronized_pool_resource
+// class synchronized_pool_resource
+
+#include <experimental/memory_resource>
+#include <cassert>
+
+static void assert_options(const std::experimental::pmr::pool_options& actual, const std::experimental::pmr::pool_options& expected)
+{
+    assert(actual.max_blocks_per_chunk == expected.max_blocks_per_chunk);
+    assert(actual.largest_required_pool_block == expected.largest_required_pool_block);
+}
+
+void test_pool_options(std::experimental::pmr::pool_options initial, std::experimental::pmr::pool_options expected)
+{
+    std::experimental::pmr::unsynchronized_pool_resource mr(initial, std::experimental::pmr::null_memory_resource());
+    assert_options(mr.options(), expected);
+
+    std::experimental::pmr::synchronized_pool_resource mr2(initial, std::experimental::pmr::null_memory_resource());
+    assert_options(mr2.options(), expected);
+}
+
+int main()
+{
+    test_pool_options({0, 0}, {1048576, 1048576});
+    test_pool_options({0, 1}, {1048576, 8});
+    test_pool_options({0, 2}, {1048576, 8});
+    test_pool_options({0, 4}, {1048576, 8});
+    test_pool_options({0, 8}, {1048576, 8});
+    test_pool_options({0, 16}, {1048576, 16});
+    test_pool_options({0, 32}, {1048576, 32});
+    test_pool_options({0, 1024}, {1048576, 1024});
+    test_pool_options({0, 1048576}, {1048576, 1048576});
+    test_pool_options({0, 2097152}, {1048576, 2097152});
+    test_pool_options({0, 1073741824}, {1048576, 1073741824});
+    test_pool_options({0, 2147483648}, {1048576, 1073741824});
+    test_pool_options({0, 8589934592}, {1048576, 1073741824});
+    test_pool_options({1, 0}, {16, 1048576});
+    test_pool_options({1, 1}, {16, 8});
+    test_pool_options({1, 2}, {16, 8});
+    test_pool_options({1, 4}, {16, 8});
+    test_pool_options({1, 8}, {16, 8});
+    test_pool_options({1, 16}, {16, 16});
+    test_pool_options({1, 32}, {16, 32});
+    test_pool_options({1, 1024}, {16, 1024});
+    test_pool_options({1, 1048576}, {16, 1048576});
+    test_pool_options({1, 2097152}, {16, 2097152});
+    test_pool_options({1, 1073741824}, {16, 1073741824});
+    test_pool_options({1, 2147483648}, {16, 1073741824});
+    test_pool_options({1, 8589934592}, {16, 1073741824});
+    test_pool_options({2, 0}, {16, 1048576});
+    test_pool_options({2, 1}, {16, 8});
+    test_pool_options({2, 2}, {16, 8});
+    test_pool_options({2, 4}, {16, 8});
+    test_pool_options({2, 8}, {16, 8});
+    test_pool_options({2, 16}, {16, 16});
+    test_pool_options({2, 32}, {16, 32});
+    test_pool_options({2, 1024}, {16, 1024});
+    test_pool_options({2, 1048576}, {16, 1048576});
+    test_pool_options({2, 2097152}, {16, 2097152});
+    test_pool_options({2, 1073741824}, {16, 1073741824});
+    test_pool_options({2, 2147483648}, {16, 1073741824});
+    test_pool_options({2, 8589934592}, {16, 1073741824});
+    test_pool_options({4, 0}, {16, 1048576});
+    test_pool_options({4, 1}, {16, 8});
+    test_pool_options({4, 2}, {16, 8});
+    test_pool_options({4, 4}, {16, 8});
+    test_pool_options({4, 8}, {16, 8});
+    test_pool_options({4, 16}, {16, 16});
+    test_pool_options({4, 32}, {16, 32});
+    test_pool_options({4, 1024}, {16, 1024});
+    test_pool_options({4, 1048576}, {16, 1048576});
+    test_pool_options({4, 2097152}, {16, 2097152});
+    test_pool_options({4, 1073741824}, {16, 1073741824});
+    test_pool_options({4, 2147483648}, {16, 1073741824});
+    test_pool_options({4, 8589934592}, {16, 1073741824});
+    test_pool_options({8, 0}, {16, 1048576});
+    test_pool_options({8, 1}, {16, 8});
+    test_pool_options({8, 2}, {16, 8});
+    test_pool_options({8, 4}, {16, 8});
+    test_pool_options({8, 8}, {16, 8});
+    test_pool_options({8, 16}, {16, 16});
+    test_pool_options({8, 32}, {16, 32});
+    test_pool_options({8, 1024}, {16, 1024});
+    test_pool_options({8, 1048576}, {16, 1048576});
+    test_pool_options({8, 2097152}, {16, 2097152});
+    test_pool_options({8, 1073741824}, {16, 1073741824});
+    test_pool_options({8, 2147483648}, {16, 1073741824});
+    test_pool_options({8, 8589934592}, {16, 1073741824});
+    test_pool_options({16, 0}, {16, 1048576});
+    test_pool_options({16, 1}, {16, 8});
+    test_pool_options({16, 2}, {16, 8});
+    test_pool_options({16, 4}, {16, 8});
+    test_pool_options({16, 8}, {16, 8});
+    test_pool_options({16, 16}, {16, 16});
+    test_pool_options({16, 32}, {16, 32});
+    test_pool_options({16, 1024}, {16, 1024});
+    test_pool_options({16, 1048576}, {16, 1048576});
+    test_pool_options({16, 2097152}, {16, 2097152});
+    test_pool_options({16, 1073741824}, {16, 1073741824});
+    test_pool_options({16, 2147483648}, {16, 1073741824});
+    test_pool_options({16, 8589934592}, {16, 1073741824});
+    test_pool_options({32, 0}, {32, 1048576});
+    test_pool_options({32, 1}, {32, 8});
+    test_pool_options({32, 2}, {32, 8});
+    test_pool_options({32, 4}, {32, 8});
+    test_pool_options({32, 8}, {32, 8});
+    test_pool_options({32, 16}, {32, 16});
+    test_pool_options({32, 32}, {32, 32});
+    test_pool_options({32, 1024}, {32, 1024});
+    test_pool_options({32, 1048576}, {32, 1048576});
+    test_pool_options({32, 2097152}, {32, 2097152});
+    test_pool_options({32, 1073741824}, {32, 1073741824});
+    test_pool_options({32, 2147483648}, {32, 1073741824});
+    test_pool_options({32, 8589934592}, {32, 1073741824});
+    test_pool_options({1024, 0}, {1024, 1048576});
+    test_pool_options({1024, 1}, {1024, 8});
+    test_pool_options({1024, 2}, {1024, 8});
+    test_pool_options({1024, 4}, {1024, 8});
+    test_pool_options({1024, 8}, {1024, 8});
+    test_pool_options({1024, 16}, {1024, 16});
+    test_pool_options({1024, 32}, {1024, 32});
+    test_pool_options({1024, 1024}, {1024, 1024});
+    test_pool_options({1024, 1048576}, {1024, 1048576});
+    test_pool_options({1024, 2097152}, {1024, 2097152});
+    test_pool_options({1024, 1073741824}, {1024, 1073741824});
+    test_pool_options({1024, 2147483648}, {1024, 1073741824});
+    test_pool_options({1024, 8589934592}, {1024, 1073741824});
+    test_pool_options({1048576, 0}, {1048576, 1048576});
+    test_pool_options({1048576, 1}, {1048576, 8});
+    test_pool_options({1048576, 2}, {1048576, 8});
+    test_pool_options({1048576, 4}, {1048576, 8});
+    test_pool_options({1048576, 8}, {1048576, 8});
+    test_pool_options({1048576, 16}, {1048576, 16});
+    test_pool_options({1048576, 32}, {1048576, 32});
+    test_pool_options({1048576, 1024}, {1048576, 1024});
+    test_pool_options({1048576, 1048576}, {1048576, 1048576});
+    test_pool_options({1048576, 2097152}, {1048576, 2097152});
+    test_pool_options({1048576, 1073741824}, {1048576, 1073741824});
+    test_pool_options({1048576, 2147483648}, {1048576, 1073741824});
+    test_pool_options({1048576, 8589934592}, {1048576, 1073741824});
+    test_pool_options({2097152, 0}, {1048576, 1048576});
+    test_pool_options({2097152, 1}, {1048576, 8});
+    test_pool_options({2097152, 2}, {1048576, 8});
+    test_pool_options({2097152, 4}, {1048576, 8});
+    test_pool_options({2097152, 8}, {1048576, 8});
+    test_pool_options({2097152, 16}, {1048576, 16});
+    test_pool_options({2097152, 32}, {1048576, 32});
+    test_pool_options({2097152, 1024}, {1048576, 1024});
+    test_pool_options({2097152, 1048576}, {1048576, 1048576});
+    test_pool_options({2097152, 2097152}, {1048576, 2097152});
+    test_pool_options({2097152, 1073741824}, {1048576, 1073741824});
+    test_pool_options({2097152, 2147483648}, {1048576, 1073741824});
+    test_pool_options({2097152, 8589934592}, {1048576, 1073741824});
+    test_pool_options({1073741824, 0}, {1048576, 1048576});
+    test_pool_options({1073741824, 1}, {1048576, 8});
+    test_pool_options({1073741824, 2}, {1048576, 8});
+    test_pool_options({1073741824, 4}, {1048576, 8});
+    test_pool_options({1073741824, 8}, {1048576, 8});
+    test_pool_options({1073741824, 16}, {1048576, 16});
+    test_pool_options({1073741824, 32}, {1048576, 32});
+    test_pool_options({1073741824, 1024}, {1048576, 1024});
+    test_pool_options({1073741824, 1048576}, {1048576, 1048576});
+    test_pool_options({1073741824, 2097152}, {1048576, 2097152});
+    test_pool_options({1073741824, 1073741824}, {1048576, 1073741824});
+    test_pool_options({1073741824, 2147483648}, {1048576, 1073741824});
+    test_pool_options({1073741824, 8589934592}, {1048576, 1073741824});
+    test_pool_options({2147483648, 0}, {1048576, 1048576});
+    test_pool_options({2147483648, 1}, {1048576, 8});
+    test_pool_options({2147483648, 2}, {1048576, 8});
+    test_pool_options({2147483648, 4}, {1048576, 8});
+    test_pool_options({2147483648, 8}, {1048576, 8});
+    test_pool_options({2147483648, 16}, {1048576, 16});
+    test_pool_options({2147483648, 32}, {1048576, 32});
+    test_pool_options({2147483648, 1024}, {1048576, 1024});
+    test_pool_options({2147483648, 1048576}, {1048576, 1048576});
+    test_pool_options({2147483648, 2097152}, {1048576, 2097152});
+    test_pool_options({2147483648, 1073741824}, {1048576, 1073741824});
+    test_pool_options({2147483648, 2147483648}, {1048576, 1073741824});
+    test_pool_options({2147483648, 8589934592}, {1048576, 1073741824});
+    test_pool_options({8589934592, 0}, {1048576, 1048576});
+    test_pool_options({8589934592, 1}, {1048576, 8});
+    test_pool_options({8589934592, 2}, {1048576, 8});
+    test_pool_options({8589934592, 4}, {1048576, 8});
+    test_pool_options({8589934592, 8}, {1048576, 8});
+    test_pool_options({8589934592, 16}, {1048576, 16});
+    test_pool_options({8589934592, 32}, {1048576, 32});
+    test_pool_options({8589934592, 1024}, {1048576, 1024});
+    test_pool_options({8589934592, 1048576}, {1048576, 1048576});
+    test_pool_options({8589934592, 2097152}, {1048576, 2097152});
+    test_pool_options({8589934592, 1073741824}, {1048576, 1073741824});
+    test_pool_options({8589934592, 2147483648}, {1048576, 1073741824});
+    test_pool_options({8589934592, 8589934592}, {1048576, 1073741824});
+}
Index: src/experimental/memory_resource.cpp
===================================================================
--- src/experimental/memory_resource.cpp
+++ src/experimental/memory_resource.cpp
@@ -165,14 +165,340 @@
     return __default_memory_resource(true, __new_res);
 }
 
-// 23.12.6, mem.res.monotonic.buffer
+// 23.12.5, mem.res.pool
 
 static size_t roundup(size_t count, size_t alignment)
 {
     size_t mask = alignment - 1;
     return (count + mask) & ~mask;
 }
 
+struct unsynchronized_pool_resource::__adhoc_pool::__chunk_header {
+    __chunk_header *__next_;
+    char *__start_;
+    size_t __align_;
+    size_t __allocation_size() {
+        return (reinterpret_cast<char*>(this) - __start_) + sizeof(*this);
+    }
+};
+
+void unsynchronized_pool_resource::__adhoc_pool::__release(
+    memory_resource *upstream)
+{
+    while (__first_ != nullptr) {
+        __chunk_header *next = __first_->__next_;
+        upstream->deallocate(__first_->__start_, __first_->__allocation_size(),
+                             __first_->__align_);
+        __first_ = next;
+    }
+}
+
+void *unsynchronized_pool_resource::__adhoc_pool::__do_allocate(
+    memory_resource *upstream, size_t bytes, size_t align)
+{
+    const size_t header_size = sizeof(__chunk_header);
+    const size_t header_align = alignof(__chunk_header);
+
+    if (align < header_align)
+        align = header_align;
+
+    size_t aligned_capacity = roundup(bytes, header_align) + header_size;
+
+    void *result = upstream->allocate(aligned_capacity, align);
+
+    __chunk_header *h =
+        (__chunk_header *)((char *)result + aligned_capacity - header_size);
+    h->__next_ = __first_;
+    h->__start_ = (char *)result;
+    h->__align_ = align;
+    __first_ = h;
+    return result;
+}
+
+void unsynchronized_pool_resource::__adhoc_pool::__do_deallocate(
+    memory_resource *upstream, void *p, size_t bytes, size_t align)
+{
+    _LIBCPP_ASSERT(__first_ != nullptr, "deallocating a block that was not allocated with this allocator");
+    if (__first_->__start_ == p) {
+        __chunk_header *next = __first_->__next_;
+        upstream->deallocate(p, __first_->__allocation_size(), __first_->__align_);
+        __first_ = next;
+    } else {
+        for (__chunk_header *h = __first_; h->__next_ != nullptr; h = h->__next_) {
+            if (h->__next_->__start_ == p) {
+                __chunk_header *next = h->__next_->__next_;
+                upstream->deallocate(p, h->__next_->__allocation_size(),
+                                     h->__next_->__align_);
+                h->__next_ = next;
+                return;
+            }
+        }
+        _LIBCPP_ASSERT(false, "deallocating a block that was not allocated with this allocator");
+    }
+}
+
+class unsynchronized_pool_resource::__fixed_pool {
+    struct __chunk_header {
+        __chunk_header *__next_;
+        char *__start_;
+        size_t __align_;
+        size_t __allocation_size() {
+            return (reinterpret_cast<char*>(this) - __start_) + sizeof(*this);
+        }
+    };
+
+    struct __vacancy_header {
+        __vacancy_header *__next_vacancy_;
+    };
+
+    __chunk_header *__first_chunk_ = nullptr;
+    __vacancy_header *__first_vacancy_ = nullptr;
+
+public:
+    explicit __fixed_pool() = default;
+
+    void __release(memory_resource *upstream)
+    {
+        __first_vacancy_ = nullptr;
+        while (__first_chunk_ != nullptr) {
+            __chunk_header *next = __first_chunk_->__next_;
+            upstream->deallocate(__first_chunk_->__start_,
+                __first_chunk_->__allocation_size(), __first_chunk_->__align_);
+            __first_chunk_ = next;
+        }
+    }
+
+    void *__try_allocate_from_vacancies()
+    {
+        if (__first_vacancy_ != nullptr) {
+            void *result = __first_vacancy_;
+            __first_vacancy_ = __first_vacancy_->__next_vacancy_;
+            return result;
+        }
+        return nullptr;
+    }
+
+    void *__allocate_in_new_chunk(
+        memory_resource *upstream, size_t block_size, size_t chunk_size)
+    {
+        _LIBCPP_ASSERT(chunk_size % block_size == 0, "");
+        static_assert(__default_alignment >= alignof(std::max_align_t), "");
+        static_assert(__default_alignment >= alignof(__chunk_header), "");
+        static_assert(__default_alignment >= alignof(__vacancy_header), "");
+
+        const size_t header_size = sizeof(__chunk_header);
+        const size_t header_align = alignof(__chunk_header);
+
+        size_t aligned_capacity = roundup(chunk_size, header_align) + header_size;
+
+        void *result = upstream->allocate(aligned_capacity, __default_alignment);
+
+        __chunk_header *h =
+            (__chunk_header *)((char *)result + aligned_capacity - header_size);
+        h->__next_ = __first_chunk_;
+        h->__start_ = (char *)result;
+        h->__align_ = __default_alignment;
+        __first_chunk_ = h;
+
+        if (chunk_size > block_size) {
+            __vacancy_header *last_vh = this->__first_vacancy_;
+            for (size_t i = block_size; i != chunk_size; i += block_size) {
+                __vacancy_header *vh = (__vacancy_header *)((char *)result + i);
+                vh->__next_vacancy_ = last_vh;
+                last_vh = vh;
+            }
+            this->__first_vacancy_ = last_vh;
+        }
+        return result;
+    }
+
+    void __evacuate(void *p)
+    {
+        __vacancy_header *vh = (__vacancy_header *)(p);
+        vh->__next_vacancy_ = __first_vacancy_;
+        __first_vacancy_ = vh;
+    }
+
+    size_t __previous_chunk_size_in_bytes() const
+    {
+        return __first_chunk_ ? __first_chunk_->__allocation_size() : 0;
+    }
+
+    static const size_t __default_alignment = alignof(max_align_t);
+};
+
+size_t unsynchronized_pool_resource::__pool_block_size(int i) const
+{
+    return size_t(1) << __log2_pool_block_size(i);
+}
+
+int unsynchronized_pool_resource::__log2_pool_block_size(int i) const
+{
+    return (i + __log2_smallest_block_size);
+}
+
+int unsynchronized_pool_resource::__pool_index(size_t bytes, size_t align) const
+{
+    if (align > alignof(std::max_align_t) || bytes > (1 << __num_fixed_pools_))
+        return __num_fixed_pools_;
+    else {
+        int i = 0;
+        bytes = (bytes > align) ? bytes : align;
+        bytes -= 1;
+        bytes >>= __log2_smallest_block_size;
+        while (bytes != 0) {
+            bytes >>= 1;
+            i += 1;
+        }
+        return i;
+    }
+}
+
+unsynchronized_pool_resource::unsynchronized_pool_resource(
+    const pool_options& opts, memory_resource* upstream)
+    : __res_(upstream), __fixed_pools_(nullptr)
+{
+    size_t largest_block_size;
+    if (opts.largest_required_pool_block == 0)
+        largest_block_size = __default_largest_block_size;
+    else if (opts.largest_required_pool_block < __smallest_block_size)
+        largest_block_size = __smallest_block_size;
+    else if (opts.largest_required_pool_block > __max_largest_block_size)
+        largest_block_size = __max_largest_block_size;
+    else
+        largest_block_size = opts.largest_required_pool_block;
+
+    if (opts.max_blocks_per_chunk == 0)
+        __options_max_blocks_per_chunk_ = __max_blocks_per_chunk;
+    else if (opts.max_blocks_per_chunk < __min_blocks_per_chunk)
+        __options_max_blocks_per_chunk_ = __min_blocks_per_chunk;
+    else if (opts.max_blocks_per_chunk > __max_blocks_per_chunk)
+        __options_max_blocks_per_chunk_ = __max_blocks_per_chunk;
+    else
+        __options_max_blocks_per_chunk_ = opts.max_blocks_per_chunk;
+
+    __num_fixed_pools_ = 1;
+    size_t capacity = __smallest_block_size;
+    while (capacity < largest_block_size) {
+        capacity <<= 1;
+        __num_fixed_pools_ += 1;
+    }
+}
+
+pool_options unsynchronized_pool_resource::options() const
+{
+    return {
+        __options_max_blocks_per_chunk_,
+        __pool_block_size(__num_fixed_pools_ - 1)
+    };
+}
+
+void unsynchronized_pool_resource::release()
+{
+    __adhoc_pool_.__release(__res_);
+    if (__fixed_pools_ != nullptr) {
+        const int n = __num_fixed_pools_;
+        for (int i=0; i < n; ++i)
+            __fixed_pools_[i].__release(__res_);
+        __res_->deallocate(__fixed_pools_,
+            __num_fixed_pools_ * sizeof(__fixed_pool), alignof(__fixed_pool));
+        __fixed_pools_ = nullptr;
+    }
+}
+
+void *unsynchronized_pool_resource::do_allocate(size_t bytes, size_t align)
+{
+    // A pointer to allocated storage (6.6.4.4.1) with a size of at least bytes.
+    // The size and alignment of the allocated memory shall meet the requirements for
+    // a class derived from memory_resource (23.12).
+    // If the pool selected for a block of size bytes is unable to satisfy the memory request
+    // from its own internal data structures, it will call upstream_resource()->allocate()
+    // to obtain more memory. If bytes is larger than that which the largest pool can handle,
+    // then memory will be allocated using upstream_resource()->allocate().
+
+    int i = __pool_index(bytes, align);
+    if (i == __num_fixed_pools_)
+        return __adhoc_pool_.__do_allocate(__res_, bytes, align);
+    else {
+        if (__fixed_pools_ == nullptr) {
+            __fixed_pools_ = (__fixed_pool*)__res_->allocate(
+                __num_fixed_pools_ * sizeof(__fixed_pool),
+                alignof(__fixed_pool)
+            );
+            __fixed_pool *first = __fixed_pools_;
+            __fixed_pool *last = __fixed_pools_ + __num_fixed_pools_;
+            for (__fixed_pool *pool = first; pool != last; ++pool)
+                ::new((void*)pool) __fixed_pool;
+        }
+        void *result = __fixed_pools_[i].__try_allocate_from_vacancies();
+        if (result == nullptr) {
+            static_assert(
+                (__max_bytes_per_chunk*5)/4 > __max_bytes_per_chunk,
+                "unsigned overflow is possible"
+            );
+            auto min = [](size_t a, size_t b) { return a < b ? a : b; };
+            auto max = [](size_t a, size_t b) { return a < b ? b : a; };
+
+            size_t prev_chunk_size_in_bytes = __fixed_pools_[i].__previous_chunk_size_in_bytes();
+            size_t prev_chunk_size_in_blocks = prev_chunk_size_in_bytes >> __log2_pool_block_size(i);
+
+            size_t chunk_size_in_blocks;
+
+            if (prev_chunk_size_in_blocks == 0) {
+                size_t min_blocks_per_chunk = max(
+                    __min_bytes_per_chunk >> __log2_pool_block_size(i),
+                    __min_blocks_per_chunk
+                );
+                chunk_size_in_blocks = min_blocks_per_chunk;
+            } else
+                chunk_size_in_blocks = (prev_chunk_size_in_blocks*5)/4;
+
+            size_t max_blocks_per_chunk = min(
+                (__max_bytes_per_chunk >> __log2_pool_block_size(i)),
+                min(
+                    __max_blocks_per_chunk,
+                    __options_max_blocks_per_chunk_
+                )
+            );
+            if (chunk_size_in_blocks > max_blocks_per_chunk)
+                chunk_size_in_blocks = max_blocks_per_chunk;
+
+            size_t block_size = __pool_block_size(i);
+
+            size_t chunk_size_in_bytes =
+                (chunk_size_in_blocks << __log2_pool_block_size(i));
+            result = __fixed_pools_[i].__allocate_in_new_chunk(
+                __res_, block_size, chunk_size_in_bytes
+            );
+        }
+        return result;
+    }
+}
+
+void unsynchronized_pool_resource::do_deallocate(
+    void* p, size_t bytes, size_t align)
+{
+    // Returns the memory at p to the pool. It is unspecified if,
+    // or under what circumstances, this operation will result in
+    // a call to upstream_resource()->deallocate().
+
+    int i = __pool_index(bytes, align);
+    if (i == __num_fixed_pools_)
+        return __adhoc_pool_.__do_deallocate(__res_, p, bytes, align);
+    else {
+        _LIBCPP_ASSERT(__fixed_pools_ != nullptr, "deallocating a block that was not allocated with this allocator");
+        __fixed_pools_[i].__evacuate(p);
+    }
+}
+
+bool synchronized_pool_resource::do_is_equal(
+    const memory_resource& other) const _NOEXCEPT
+{
+    return &other == this;
+}
+
+// 23.12.6, mem.res.monotonic.buffer
+
 void *monotonic_buffer_resource::__initial_header::__try_allocate_from_chunk(
     size_t bytes, size_t align)
 {
Index: include/experimental/memory_resource
===================================================================
--- include/experimental/memory_resource
+++ include/experimental/memory_resource
@@ -69,12 +69,16 @@
 #include <experimental/__memory>
 #include <limits>
 #include <memory>
+#if !defined(_LIBCPP_HAS_NO_THREADS)
+#include <mutex>
+#endif
 #include <new>
 #include <stdexcept>
 #include <__tuple>
 #include <type_traits>
 #include <utility>
 #include <cstddef>
+#include <cstdint>
 #include <cstdlib>
 #include <__debug>
 
@@ -420,6 +424,167 @@
     typename allocator_traits<_Alloc>::template rebind_alloc<char>
   >;
 
+// 23.12.5, mem.res.pool
+
+// 23.12.5.2, mem.res.pool.options
+
+struct _LIBCPP_TYPE_VIS pool_options {
+    size_t max_blocks_per_chunk = 0;
+    size_t largest_required_pool_block = 0;
+};
+
+// 23.12.5.1, mem.res.pool.overview
+
+
+class _LIBCPP_TYPE_VIS unsynchronized_pool_resource : public memory_resource
+{
+    class __fixed_pool;
+
+    class __adhoc_pool {
+        struct __chunk_header;
+        __chunk_header *__first_;
+
+    public:
+        _LIBCPP_INLINE_VISIBILITY
+        explicit __adhoc_pool() : __first_(nullptr) {}
+
+        void __release(memory_resource *__upstream);
+        void *__do_allocate(memory_resource *__upstream,
+                            size_t __bytes, size_t __align);
+        void __do_deallocate(memory_resource *__upstream,
+                             void *__p, size_t __bytes, size_t __align);
+    };
+
+    static const size_t __min_blocks_per_chunk = 16;
+    static const size_t __min_bytes_per_chunk = 1024;
+    static const size_t __max_blocks_per_chunk = (size_t(1) << 20);
+    static const size_t __max_bytes_per_chunk = (size_t(1) << 30);
+
+    static const int __log2_smallest_block_size = 3;
+    static const size_t __smallest_block_size = 8;
+    static const size_t __default_largest_block_size = (size_t(1) << 20);
+    static const size_t __max_largest_block_size = (size_t(1) << 30);
+
+    size_t __pool_block_size(int __i) const;
+    int __log2_pool_block_size(int __i) const;
+    int __pool_index(size_t __bytes, size_t __align) const;
+
+public:
+    unsynchronized_pool_resource(const pool_options& __opts,
+                                 memory_resource* __upstream);
+
+    _LIBCPP_INLINE_VISIBILITY
+    unsynchronized_pool_resource()
+        : unsynchronized_pool_resource(pool_options(), get_default_resource()) {}
+
+    _LIBCPP_INLINE_VISIBILITY
+    explicit unsynchronized_pool_resource(memory_resource* __upstream)
+        : unsynchronized_pool_resource(pool_options(), __upstream) {}
+
+    _LIBCPP_INLINE_VISIBILITY
+    explicit unsynchronized_pool_resource(const pool_options& __opts)
+        : unsynchronized_pool_resource(__opts, get_default_resource()) {}
+
+    unsynchronized_pool_resource(const unsynchronized_pool_resource&) = delete;
+
+    _LIBCPP_INLINE_VISIBILITY
+    ~unsynchronized_pool_resource() override
+        { release(); }
+
+    unsynchronized_pool_resource& operator=(const unsynchronized_pool_resource&) = delete;
+
+    void release();
+
+    _LIBCPP_INLINE_VISIBILITY
+    memory_resource* upstream_resource() const
+        { return __res_; }
+
+    pool_options options() const;
+
+protected:
+    void *do_allocate(size_t __bytes, size_t __align) override; // key function
+
+    void do_deallocate(void *__p, size_t __bytes, size_t __align) override;
+
+    _LIBCPP_INLINE_VISIBILITY
+    bool do_is_equal(const memory_resource& __other) const _NOEXCEPT override
+        { return &__other == this; }
+
+private:
+    memory_resource *__res_;
+    __adhoc_pool __adhoc_pool_;
+    __fixed_pool *__fixed_pools_;
+    int __num_fixed_pools_;
+    uint32_t __options_max_blocks_per_chunk_;
+};
+
+class _LIBCPP_TYPE_VIS synchronized_pool_resource : public memory_resource
+{
+public:
+    _LIBCPP_INLINE_VISIBILITY
+    synchronized_pool_resource(const pool_options& __opts, memory_resource *__upstream)
+        : __unsync_(__opts, __upstream) {}
+
+    _LIBCPP_INLINE_VISIBILITY
+    synchronized_pool_resource()
+        : synchronized_pool_resource(pool_options(), get_default_resource()) {}
+
+    _LIBCPP_INLINE_VISIBILITY
+    explicit synchronized_pool_resource(memory_resource *__upstream)
+        : synchronized_pool_resource(pool_options(), __upstream) {}
+
+    _LIBCPP_INLINE_VISIBILITY
+    explicit synchronized_pool_resource(const pool_options& __opts)
+        : synchronized_pool_resource(__opts, get_default_resource()) {}
+
+    synchronized_pool_resource(const synchronized_pool_resource&) = delete;
+
+    ~synchronized_pool_resource() override = default;
+
+    synchronized_pool_resource& operator=(const synchronized_pool_resource&) = delete;
+
+    _LIBCPP_INLINE_VISIBILITY
+    void release() {
+#if !defined(_LIBCPP_HAS_NO_THREADS)
+        unique_lock<mutex> __lk(__mut_);
+#endif
+        __unsync_.release();
+    }
+
+    _LIBCPP_INLINE_VISIBILITY
+    memory_resource* upstream_resource() const
+        { return __unsync_.upstream_resource(); }
+
+    _LIBCPP_INLINE_VISIBILITY
+    pool_options options() const
+        { return __unsync_.options(); }
+
+protected:
+    _LIBCPP_INLINE_VISIBILITY
+    void* do_allocate(size_t __bytes, size_t __align) override {
+#if !defined(_LIBCPP_HAS_NO_THREADS)
+        unique_lock<mutex> __lk(__mut_);
+#endif
+        return __unsync_.allocate(__bytes, __align);
+    }
+
+    _LIBCPP_INLINE_VISIBILITY
+    void do_deallocate(void *__p, size_t __bytes, size_t __align) override {
+#if !defined(_LIBCPP_HAS_NO_THREADS)
+        unique_lock<mutex> __lk(__mut_);
+#endif
+        return __unsync_.deallocate(__p, __bytes, __align);
+    }
+
+    bool do_is_equal(const memory_resource& __other) const _NOEXCEPT override; // key function
+
+private:
+#if !defined(_LIBCPP_HAS_NO_THREADS)
+    mutex __mut_;
+#endif
+    unsynchronized_pool_resource __unsync_;
+};
+
 // 23.12.6, mem.res.monotonic.buffer
 
 class _LIBCPP_TYPE_VIS monotonic_buffer_resource : public memory_resource
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
  • [PATCH] D47358: <experim... Arthur O'Dwyer via Phabricator via cfe-commits

Reply via email to