Index: include/experimental/memory_resource =================================================================== --- include/experimental/memory_resource +++ include/experimental/memory_resource @@ -69,12 +69,16 @@ #include #include #include +#if !defined(_LIBCPP_HAS_NO_THREADS) +#include +#endif #include #include #include <__tuple> #include #include #include +#include #include #include <__debug> @@ -420,6 +424,167 @@ typename allocator_traits<_Alloc>::template rebind_alloc >; +// 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 __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 __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 __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 Index: src/experimental/memory_resource.cpp =================================================================== --- src/experimental/memory_resource.cpp +++ src/experimental/memory_resource.cpp @@ -161,7 +161,7 @@ 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) { @@ -169,6 +169,332 @@ 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(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(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: 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 + +// + +// struct pool_options +// class unsynchronized_pool_resource +// class synchronized_pool_resource + +#include +#include + +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: 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 + +// + +// class synchronized_pool_resource +// class unsynchronized_pool_resource + +#include +#include + +#include "count_new.hpp" + +template +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(); + test(); +} 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 + +// + +// class synchronized_pool_resource + +#include +#include + +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/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 + +// + +// class unsynchronized_pool_resource + +#include +#include + +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.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 + +// + +// class synchronized_pool_resource +// class unsynchronized_pool_resource + +#include +#include +#include +#include + +#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 +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(); + test(); +} 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 + +// + +// class synchronized_pool_resource + +#include +#include + +#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/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 + +// + +// class synchronized_pool_resource + +#include +#include + +#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_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 + +// + +// class synchronized_pool_resource + +#include +#include + +#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_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 + +// + +// class synchronized_pool_resource + +#include +#include +#include + +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 successful_allocations; + std::vector deallocations; +}; + +template +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 +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.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 + +// + +// class unsynchronized_pool_resource + +#include +#include + +#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/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 + +// + +// class unsynchronized_pool_resource + +#include +#include + +#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_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 + +// + +// class unsynchronized_pool_resource + +#include +#include + +#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_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 + +// + +// class unsynchronized_pool_resource + +#include +#include +#include + +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 successful_allocations; + std::vector deallocations; +}; + +template +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 +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/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 + +// + +// struct pool_options + +#include +#include + +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/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;