diff --git a/include/foonathan/memory/detail/free_list.hpp b/include/foonathan/memory/detail/free_list.hpp index 74222cdd..64515d51 100644 --- a/include/foonathan/memory/detail/free_list.hpp +++ b/include/foonathan/memory/detail/free_list.hpp @@ -10,6 +10,7 @@ #include "align.hpp" #include "utility.hpp" +#include "debug_helpers.hpp" #include "../config.hpp" namespace foonathan @@ -29,6 +30,11 @@ namespace foonathan // alignment static constexpr auto min_element_alignment = alignof(char*); + static constexpr std::size_t actual_node_size(std::size_t node_size) noexcept + { + return adjusted_node_size(node_size) + 2 * fence_size(); + } + //=== constructor ===// free_memory_list(std::size_t node_size) noexcept; @@ -92,7 +98,16 @@ namespace foonathan } private: - std::size_t fence_size() const noexcept; + + static constexpr std::size_t adjusted_node_size(std::size_t requested_size) noexcept + { + return requested_size > min_element_size ? requested_size : min_element_size; + } + static constexpr std::size_t fence_size() noexcept + { + return detail::debug_fence_size ? max_alignment : 0u; + } + void insert_impl(void* mem, std::size_t size) noexcept; char* first_; @@ -112,6 +127,11 @@ namespace foonathan // alignment static constexpr auto min_element_alignment = alignof(char*); + static constexpr std::size_t actual_node_size(std::size_t node_size) noexcept + { + return adjusted_node_size(node_size) + 2 * fence_size(adjusted_node_size(node_size)); + } + //=== constructor ===// ordered_free_memory_list(std::size_t node_size) noexcept; @@ -186,7 +206,14 @@ namespace foonathan } private: - std::size_t fence_size() const noexcept; + static constexpr std::size_t adjusted_node_size(std::size_t requested_size) noexcept + { + return requested_size > min_element_size ? requested_size : min_element_size; + } + static constexpr std::size_t fence_size(std::size_t node_size) noexcept + { + return detail::debug_fence_size ? node_size : 0u; + } // returns previous pointer char* insert_impl(void* mem, std::size_t size) noexcept; diff --git a/include/foonathan/memory/detail/small_free_list.hpp b/include/foonathan/memory/detail/small_free_list.hpp index 8835ca00..215dd24c 100644 --- a/include/foonathan/memory/detail/small_free_list.hpp +++ b/include/foonathan/memory/detail/small_free_list.hpp @@ -9,6 +9,7 @@ #include "../config.hpp" #include "utility.hpp" +#include "debug_helpers.hpp" namespace foonathan { @@ -47,6 +48,11 @@ namespace foonathan // alignment static constexpr std::size_t min_element_alignment = 1; + static constexpr std::size_t actual_node_size(std::size_t node_size) noexcept + { + return adjusted_node_size(node_size) + 2 * fence_size(adjusted_node_size(node_size)); + } + //=== constructor ===// small_free_memory_list(std::size_t node_size) noexcept; @@ -127,7 +133,16 @@ namespace foonathan } private: - std::size_t fence_size() const noexcept; + + static constexpr std::size_t adjusted_node_size(std::size_t requested_size) noexcept + { + // mainly here for consistency with other free_list implementaions; min_element_size=1 makes it a no-op. + return requested_size; + } + static constexpr std::size_t fence_size(std::size_t node_size) noexcept + { + return detail::debug_fence_size ? node_size : 0u; + } chunk* find_chunk_impl(std::size_t n = 1) noexcept; chunk* find_chunk_impl(unsigned char* node, chunk_base* first, diff --git a/include/foonathan/memory/memory_pool.hpp b/include/foonathan/memory/memory_pool.hpp index f38a5c81..5a0392f6 100644 --- a/include/foonathan/memory/memory_pool.hpp +++ b/include/foonathan/memory/memory_pool.hpp @@ -55,8 +55,9 @@ namespace foonathan using allocator_type = make_block_allocator_t; using pool_type = PoolType; + // actual minimum node size, including any debug fence additions. static constexpr std::size_t min_node_size = - FOONATHAN_IMPL_DEFINED(free_list::min_element_size); + FOONATHAN_IMPL_DEFINED(free_list::actual_node_size(free_list::min_element_size)); /// \returns The minimum block size required for certain number of \concept{concept_node,node}. /// \requires \c node_size must be a valid \concept{concept_node,node size} @@ -65,9 +66,7 @@ namespace foonathan std::size_t number_of_nodes) noexcept { return detail::memory_block_stack::implementation_offset() - + number_of_nodes - * (((node_size > min_node_size) ? node_size : min_node_size) - + (detail::debug_fence_size ? 2 * detail::max_alignment : 0)); + + number_of_nodes * free_list::actual_node_size(node_size); } /// \effects Creates it by specifying the size each \concept{concept_node,node} will have, diff --git a/src/detail/free_list.cpp b/src/detail/free_list.cpp index 97fb851e..32cb7013 100644 --- a/src/detail/free_list.cpp +++ b/src/detail/free_list.cpp @@ -126,7 +126,7 @@ constexpr std::size_t free_memory_list::min_element_alignment; free_memory_list::free_memory_list(std::size_t node_size) noexcept : first_(nullptr), - node_size_(node_size > min_element_size ? node_size : min_element_size), + node_size_(adjusted_node_size(node_size)), capacity_(0u) { } @@ -186,9 +186,9 @@ void* free_memory_list::allocate(std::size_t n) noexcept if (n <= node_size_) return allocate(); - auto actual_size = node_size_ + 2 * fence_size(); + auto actual_size = actual_node_size(node_size_); - auto i = list_search_array(first_, n + 2 * fence_size(), actual_size); + auto i = list_search_array(first_, actual_node_size(n), actual_size); if (i.first == nullptr) return nullptr; @@ -217,7 +217,7 @@ void free_memory_list::deallocate(void* ptr, std::size_t n) noexcept else { auto mem = debug_fill_free(ptr, n, fence_size()); - insert_impl(mem, n + 2 * fence_size()); + insert_impl(mem, actual_node_size(n)); } } @@ -226,15 +226,9 @@ std::size_t free_memory_list::alignment() const noexcept return alignment_for(node_size_); } -std::size_t free_memory_list::fence_size() const noexcept -{ - // fence size is max alignment - return debug_fence_size ? max_alignment : 0u; -} - void free_memory_list::insert_impl(void* mem, std::size_t size) noexcept { - auto actual_size = node_size_ + 2 * fence_size(); + auto actual_size = actual_node_size(node_size_); auto no_nodes = size / actual_size; FOONATHAN_MEMORY_ASSERT(no_nodes > 0); @@ -344,7 +338,7 @@ constexpr std::size_t ordered_free_memory_list::min_element_size; constexpr std::size_t ordered_free_memory_list::min_element_alignment; ordered_free_memory_list::ordered_free_memory_list(std::size_t node_size) noexcept -: node_size_(node_size > min_element_size ? node_size : min_element_size), +: node_size_(adjusted_node_size(node_size)), capacity_(0u), last_dealloc_(end_node()), last_dealloc_prev_(begin_node()) @@ -457,7 +451,7 @@ void* ordered_free_memory_list::allocate() noexcept FOONATHAN_MEMORY_ASSERT(last_dealloc_prev_ == prev); } - return debug_fill_new(node, node_size_, fence_size()); + return debug_fill_new(node, node_size_, fence_size(node_size_)); } void* ordered_free_memory_list::allocate(std::size_t n) noexcept @@ -467,9 +461,9 @@ void* ordered_free_memory_list::allocate(std::size_t n) noexcept if (n <= node_size_) return allocate(); - auto actual_size = node_size_ + 2 * fence_size(); + auto actual_size = actual_node_size(node_size_); - auto i = xor_list_search_array(begin_node(), end_node(), n + 2 * fence_size(), actual_size); + auto i = xor_list_search_array(begin_node(), end_node(), actual_node_size(n), actual_size); if (i.first == nullptr) return nullptr; @@ -485,12 +479,12 @@ void* ordered_free_memory_list::allocate(std::size_t n) noexcept last_dealloc_prev_ = i.prev; } - return debug_fill_new(i.first, n, fence_size()); + return debug_fill_new(i.first, n, fence_size(node_size_)); } void ordered_free_memory_list::deallocate(void* ptr) noexcept { - auto node = static_cast(debug_fill_free(ptr, node_size_, fence_size())); + auto node = static_cast(debug_fill_free(ptr, node_size_, fence_size(node_size_))); auto p = find_pos(allocator_info(FOONATHAN_MEMORY_LOG_PREFIX "::detail::ordered_free_memory_list", @@ -510,8 +504,8 @@ void ordered_free_memory_list::deallocate(void* ptr, std::size_t n) noexcept deallocate(ptr); else { - auto mem = debug_fill_free(ptr, n, fence_size()); - auto prev = insert_impl(mem, n + 2 * fence_size()); + auto mem = debug_fill_free(ptr, n, fence_size(node_size_)); + auto prev = insert_impl(mem, actual_node_size(n)); last_dealloc_ = static_cast(mem); last_dealloc_prev_ = prev; @@ -523,15 +517,9 @@ std::size_t ordered_free_memory_list::alignment() const noexcept return alignment_for(node_size_); } -std::size_t ordered_free_memory_list::fence_size() const noexcept -{ - // node size is fence size - return debug_fence_size ? node_size_ : 0u; -} - char* ordered_free_memory_list::insert_impl(void* mem, std::size_t size) noexcept { - auto actual_size = node_size_ + 2 * fence_size(); + auto actual_size = actual_node_size(node_size_); auto no_nodes = size / actual_size; FOONATHAN_MEMORY_ASSERT(no_nodes > 0); diff --git a/src/detail/small_free_list.cpp b/src/detail/small_free_list.cpp index 944c8eb2..21583958 100644 --- a/src/detail/small_free_list.cpp +++ b/src/detail/small_free_list.cpp @@ -247,7 +247,7 @@ void small_free_memory_list::insert(void* mem, std::size_t size) noexcept FOONATHAN_MEMORY_ASSERT(is_aligned(mem, max_alignment)); debug_fill_internal(mem, size, false); - auto actual_size = node_size_ + 2 * fence_size(); + auto actual_size = actual_node_size(node_size_); auto total_chunk_size = chunk::memory_offset + actual_size * chunk::max_nodes; auto align_buffer = align_offset(total_chunk_size, alignof(chunk)); @@ -294,7 +294,7 @@ void small_free_memory_list::insert(void* mem, std::size_t size) noexcept std::size_t small_free_memory_list::usable_size(std::size_t size) const noexcept { - auto actual_size = node_size_ + 2 * fence_size(); + auto actual_size = actual_node_size(node_size_); auto total_chunk_size = chunk::memory_offset + actual_size * chunk::max_nodes; auto no_chunks = size / total_chunk_size; auto remainder = size % total_chunk_size; @@ -311,9 +311,9 @@ void* small_free_memory_list::allocate() noexcept --capacity_; - auto mem = chunk->allocate(node_size_ + 2 * fence_size()); + auto mem = chunk->allocate(actual_node_size(node_size_)); FOONATHAN_MEMORY_ASSERT(mem); - return detail::debug_fill_new(mem, node_size_, fence_size()); + return detail::debug_fill_new(mem, node_size_, fence_size(node_size_)); } void small_free_memory_list::deallocate(void* mem) noexcept @@ -321,8 +321,8 @@ void small_free_memory_list::deallocate(void* mem) noexcept auto info = allocator_info(FOONATHAN_MEMORY_LOG_PREFIX "::detail::small_free_memory_list", this); - auto actual_size = node_size_ + 2 * fence_size(); - auto node = static_cast(detail::debug_fill_free(mem, node_size_, fence_size())); + auto actual_size = actual_node_size(node_size_); + auto node = static_cast(detail::debug_fill_free(mem, node_size_, fence_size(node_size_))); auto chunk = find_chunk_impl(node); dealloc_chunk_ = chunk; @@ -347,12 +347,6 @@ std::size_t small_free_memory_list::alignment() const noexcept return alignment_for(node_size_); } -std::size_t small_free_memory_list::fence_size() const noexcept -{ - // node size is fence size - return debug_fence_size ? node_size_ : 0u; -} - chunk* small_free_memory_list::find_chunk_impl(std::size_t n) noexcept { if (auto c = make_chunk(alloc_chunk_, n)) @@ -382,7 +376,7 @@ chunk* small_free_memory_list::find_chunk_impl(std::size_t n) noexcept chunk* small_free_memory_list::find_chunk_impl(unsigned char* node, chunk_base* first, chunk_base* last) noexcept { - auto actual_size = node_size_ + 2 * fence_size(); + auto actual_size = actual_node_size(node_size_); do { @@ -399,7 +393,7 @@ chunk* small_free_memory_list::find_chunk_impl(unsigned char* node, chunk_base* chunk* small_free_memory_list::find_chunk_impl(unsigned char* node) noexcept { - auto actual_size = node_size_ + 2 * fence_size(); + auto actual_size = actual_node_size(node_size_); if (auto c = from_chunk(dealloc_chunk_, node, actual_size)) return c; diff --git a/test/memory_pool.cpp b/test/memory_pool.cpp index 4dec5e4e..d6dd4ce3 100644 --- a/test/memory_pool.cpp +++ b/test/memory_pool.cpp @@ -3,6 +3,8 @@ // found in the top-level directory of this distribution. #include "memory_pool.hpp" +#include "container.hpp" +#include "smart_ptr.hpp" #include #include @@ -65,3 +67,33 @@ TEST_CASE("memory_pool", "[pool]") } REQUIRE(alloc.no_allocated() == 0u); } + +TEST_CASE("memory_pool min_block_size", "[pool]") { + + using pool_type = memory_pool<>; + + struct MyObj { + float value; + char data[100]; + }; + CAPTURE(alignof(MyObj)); + + CAPTURE(pool_type::pool_type::type::min_element_size); + CAPTURE(pool_type::pool_type::type::actual_node_size(pool_type::pool_type::type::min_element_size)); + CAPTURE(pool_type::min_node_size); + + auto nodesize = allocate_shared_node_size::value; + CAPTURE(nodesize); + + // Make a pool with the smallest block size possible. + auto minblock = pool_type::min_block_size(nodesize, 1); + CAPTURE(minblock); + + // Create and use the pool + pool_type pool(nodesize, minblock); + auto ptr = allocate_shared(pool); + + // These will grow the pool; should still succeed. + ptr = allocate_shared(pool); + ptr = allocate_shared(pool); +}