diff --git a/doc/container.qbk b/doc/container.qbk index 3f3812e..5030cae 100644 --- a/doc/container.qbk +++ b/doc/container.qbk @@ -1257,6 +1257,10 @@ use [*Boost.Container]? There are several reasons for that: found by Arthur O'Dowyer in his blog post [@https://quuxplusone.github.io/blog/2018/06/05/libcpp-memory-resource/ for libc++] +* Implemented proposed resolution for + [@https://cplusplus.github.io/LWG/issue3120 ['"LWG 3120 Unclear behavior of monotonic_buffer_resource::release()"]]. + After `release()` the original buffer is recovered for the next allocation. + [endsect] [section:release_notes_boost_1_67_00 Boost 1.67 Release] diff --git a/include/boost/container/pmr/monotonic_buffer_resource.hpp b/include/boost/container/pmr/monotonic_buffer_resource.hpp index dfffe87..5a176a3 100644 --- a/include/boost/container/pmr/monotonic_buffer_resource.hpp +++ b/include/boost/container/pmr/monotonic_buffer_resource.hpp @@ -51,9 +51,11 @@ class BOOST_CONTAINER_DECL monotonic_buffer_resource : public memory_resource { block_slist m_memory_blocks; - void* m_current_buffer; + void * m_current_buffer; std::size_t m_current_buffer_size; std::size_t m_next_buffer_size; + void * const m_initial_buffer; + std::size_t const m_initial_buffer_size; /// @cond void increase_next_buffer(); @@ -131,7 +133,7 @@ class BOOST_CONTAINER_DECL monotonic_buffer_resource std::size_t remaining_storage(std::size_t alignment = 1u) const BOOST_NOEXCEPT; //! Returns: - //! The number of bytes of storage available for the specified alignment. + //! The address pointing to the start of the current free storage. //! //! Note: Non-standard extension. const void *current_buffer() const BOOST_NOEXCEPT; diff --git a/src/monotonic_buffer_resource.cpp b/src/monotonic_buffer_resource.cpp index f9f6f4c..29ffde1 100644 --- a/src/monotonic_buffer_resource.cpp +++ b/src/monotonic_buffer_resource.cpp @@ -63,6 +63,8 @@ monotonic_buffer_resource::monotonic_buffer_resource(memory_resource* upstream) , m_current_buffer(0) , m_current_buffer_size(0u) , m_next_buffer_size(initial_next_buffer_size) + , m_initial_buffer(0) + , m_initial_buffer_size(0u) {} monotonic_buffer_resource::monotonic_buffer_resource(std::size_t initial_size, memory_resource* upstream) BOOST_NOEXCEPT @@ -70,6 +72,8 @@ monotonic_buffer_resource::monotonic_buffer_resource(std::size_t initial_size, m , m_current_buffer(0) , m_current_buffer_size(0u) , m_next_buffer_size(minimum_buffer_size) + , m_initial_buffer(0) + , m_initial_buffer_size(0u) { //In case initial_size is zero this->increase_next_buffer_at_least_to(initial_size + !initial_size); } @@ -81,6 +85,8 @@ monotonic_buffer_resource::monotonic_buffer_resource(void* buffer, std::size_t b , m_next_buffer_size (bi::detail::previous_or_equal_pow2 (boost::container::dtl::max_value(buffer_size, std::size_t(initial_next_buffer_size)))) + , m_initial_buffer(buffer) + , m_initial_buffer_size(buffer_size) { this->increase_next_buffer(); } monotonic_buffer_resource::~monotonic_buffer_resource() @@ -89,8 +95,8 @@ monotonic_buffer_resource::~monotonic_buffer_resource() void monotonic_buffer_resource::release() BOOST_NOEXCEPT { m_memory_blocks.release(); - m_current_buffer = 0u; - m_current_buffer_size = 0u; + m_current_buffer = m_initial_buffer; + m_current_buffer_size = m_initial_buffer_size; m_next_buffer_size = initial_next_buffer_size; } diff --git a/test/monotonic_buffer_resource_test.cpp b/test/monotonic_buffer_resource_test.cpp index 568eb7b..82e7e54 100644 --- a/test/monotonic_buffer_resource_test.cpp +++ b/test/monotonic_buffer_resource_test.cpp @@ -404,21 +404,41 @@ void test_do_is_equal() void test_release() { - memory_resource_logger mrl; - const std::size_t initial_size = 1u; - derived_from_monotonic_buffer_resource dmbr(initial_size, &mrl); - //First test, no buffer - const unsigned iterations = 8; - //Test each iteration allocates memory - for(unsigned i = 0; i != iterations; ++i) { - dmbr.do_allocate(dmbr.remaining_storage()+1, 1); - BOOST_TEST(mrl.m_info.size() == (i+1)); + memory_resource_logger mrl; + const std::size_t initial_size = 1u; + derived_from_monotonic_buffer_resource dmbr(initial_size, &mrl); + //First test, no buffer + const unsigned iterations = 8; + //Test each iteration allocates memory + for(unsigned i = 0; i != iterations; ++i) + { + dmbr.do_allocate(dmbr.remaining_storage()+1, 1); + BOOST_TEST(mrl.m_info.size() == (i+1)); + } + //Release and check memory was released + dmbr.release(); + BOOST_TEST(mrl.m_mismatches == 0u); + BOOST_TEST(mrl.m_info.size() == 0u); + } + //Now use a local buffer + { + boost::move_detail::aligned_storage + ::type buf; + //Supply an external buffer + monotonic_buffer_resource monr(&buf, sizeof(buf)); + memory_resource &mr = monr; + BOOST_TEST(monr.remaining_storage(1u) == sizeof(buf)); + //Allocate all remaining storage + mr.allocate(monr.remaining_storage(1u), 1u); + BOOST_TEST(monr.current_buffer() == ((char*)&buf + sizeof(buf))); + //No new allocation should have ocurred + BOOST_TEST(monr.remaining_storage(1u) == 0u); + //Release and check memory was released and the original buffer is back + monr.release(); + BOOST_TEST(monr.remaining_storage(1u) == sizeof(buf)); + BOOST_TEST(monr.current_buffer() == &buf); } - //Release and check memory was released - dmbr.release(); - BOOST_TEST(mrl.m_mismatches == 0u); - BOOST_TEST(mrl.m_info.size() == 0u); } void test_destructor()