diff --git a/doc/container.qbk b/doc/container.qbk index 3c2fa43..554bae2 100644 --- a/doc/container.qbk +++ b/doc/container.qbk @@ -2832,6 +2832,12 @@ collect them containers and build [*Boost.Container], a library targeted to a wi [section:release_notes Release Notes] +[section:release_notes_boost_1_93_00 Boost 1.93 Release] + +* Added overaligned support for [classref boost::container::pmr::monotonic_buffer_resource monotonic_buffer_resource]. + +[endsect] + [section:release_notes_boost_1_92_00 Boost 1.92 Release] * Added new [classref boost::container::hub hub] container designed by Joaqu\u00EDn M. L\u00F3pez Mu\u00F1oz. diff --git a/src/monotonic_buffer_resource.cpp b/src/monotonic_buffer_resource.cpp index caf44de..880c1ce 100644 --- a/src/monotonic_buffer_resource.cpp +++ b/src/monotonic_buffer_resource.cpp @@ -145,13 +145,20 @@ void* monotonic_buffer_resource::do_allocate(std::size_t bytes, std::size_t alig //m_current_buffer_size in allocate_from_current(). std::size_t aligner = 0u; if(!m_current_buffer || this->remaining_storage(alignment, aligner) < bytes || m_current_buffer_size < aligner){ - //Update next_buffer_size to at least bytes + //The block obtained from the upstream resource is only guaranteed to be + //max_align-aligned, so for an over-aligned request reserve enough extra + //space to be able to realign the returned block inside the new buffer. const std::size_t extra_for_alignment = (alignment > memory_resource::max_align) ? (alignment - 1u) : 0u; + //Update next_buffer_size to at least bytes plus the realignment slack this->increase_next_buffer_at_least_to(bytes + extra_for_alignment); //Now allocate and update internal data m_current_buffer = (char*)m_memory_blocks.allocate(m_next_buffer_size); m_current_buffer_size = m_next_buffer_size; this->increase_next_buffer(); + //Recompute the alignment padding for the freshly obtained buffer (which is + //only max_align-aligned). This is zero unless the request is over-aligned, + //in which case the reserved slack above guarantees "aligner + bytes" fits. + this->remaining_storage(alignment, aligner); } //Enough internal storage, extract from it. For a zero-sized block this returns //the current aligned position without consuming bytes, so repeated zero-sized diff --git a/test/monotonic_buffer_resource_test.cpp b/test/monotonic_buffer_resource_test.cpp index e11d8db..6e24769 100644 --- a/test/monotonic_buffer_resource_test.cpp +++ b/test/monotonic_buffer_resource_test.cpp @@ -508,6 +508,99 @@ void test_do_allocate_zero() BOOST_TEST(mrl.m_info.size() == 0u); } +void test_do_allocate_overaligned() +{ + //Over-aligned requests (alignment > memory_resource::max_align) must be + //honored. The buffers obtained from upstream are only max_align-aligned + //(memory_resource_logger uses std::malloc), so the resource has to reserve + //extra space and realign the returned block inside the freshly obtained + //buffer. These tests verify the returned pointer honors the requested + //alignment in every code path. + const std::size_t max_align = memory_resource::max_align; + memory_resource_logger mrl; + + //(1) Over-aligned allocation from a fresh resource must trigger an upstream + // buffer and return a correctly over-aligned, fully usable block. + { + const std::size_t over = max_align*4u; + const std::size_t bytes = over*3u + 1u; + monotonic_buffer_resource m(&mrl); + void *const p = m.allocate(bytes, over); + BOOST_TEST(p != 0); + BOOST_TEST((std::size_t(p) % over) == 0u); + //A single upstream buffer was requested for it + BOOST_TEST(mrl.m_info.size() == 1u); + //The whole block must be usable: writing every byte would trip ASAN or + //corrupt the heap if the buffer were too small after realignment. + char *const cp = (char*)p; + for(std::size_t i = 0; i != bytes; ++i){ + cp[i] = (char)0xABu; + } + } + BOOST_TEST(mrl.m_mismatches == 0u); + BOOST_TEST(mrl.m_info.size() == 0u); + + //(2) A sequence of increasing over-alignments must each be honored. + { + monotonic_buffer_resource m(&mrl); + for(std::size_t a = max_align*2u; a <= max_align*32u; a *= 2u){ + void *const p = m.allocate(a/2u, a); + BOOST_TEST(p != 0); + BOOST_TEST((std::size_t(p) % a) == 0u); + } + } + BOOST_TEST(mrl.m_mismatches == 0u); + BOOST_TEST(mrl.m_info.size() == 0u); + + //(3) Over-aligned allocation served from a user-supplied buffer (realigned in + // place, no upstream) and then from a fresh buffer after exhaustion. + { + //256-byte, 64-aligned external buffer + boost::move_detail::aligned_storage<256u, 64u>::type storage; + const std::size_t over = 64u; + monotonic_buffer_resource m(&storage, sizeof(storage), &mrl); + char *const base = (char*)&storage; + //Misalign the current position by 1 byte + void *const one = m.allocate(1u, 1u); + BOOST_TEST(one == base); + //Over-aligned request must be realigned within the same buffer (no upstream) + void *const p = m.allocate(over, over); + BOOST_TEST((std::size_t(p) % over) == 0u); + BOOST_TEST(p == base + over); + BOOST_TEST(mrl.m_info.size() == 0u); + //Exhaust the remaining storage + void *const ex = m.allocate(m.remaining_storage(1u), 1u); + (void)ex; + BOOST_TEST(m.remaining_storage(1u) == 0u); + //Over-aligned request now must go upstream and still be correctly aligned + void *const q = m.allocate(over, over); + BOOST_TEST(q != 0); + BOOST_TEST((std::size_t(q) % over) == 0u); + BOOST_TEST(mrl.m_info.size() == 1u); + } + BOOST_TEST(mrl.m_mismatches == 0u); + BOOST_TEST(mrl.m_info.size() == 0u); + + //(4) A zero-sized over-aligned request is also honored (returns an aligned + // address) following the same rules as any other zero-sized allocation. + { + boost::move_detail::aligned_storage<128u, 64u>::type storage; + const std::size_t over = 64u; + monotonic_buffer_resource m(&storage, sizeof(storage), &mrl); + char *const base = (char*)&storage; + //Misalign, then a zero-sized over-aligned request returns the next aligned + //position inside the buffer without consuming real storage nor upstream. + void *const one = m.allocate(1u, 1u); + BOOST_TEST(one == base); + void *const z = m.allocate(0u, over); + BOOST_TEST(z == base + over); + BOOST_TEST((std::size_t(z) % over) == 0u); + BOOST_TEST(mrl.m_info.size() == 0u); + } + BOOST_TEST(mrl.m_mismatches == 0u); + BOOST_TEST(mrl.m_info.size() == 0u); +} + void test_do_deallocate() { memory_resource_logger mrl; @@ -626,6 +719,7 @@ int main() test_upstream_resource(); test_do_allocate(); test_do_allocate_zero(); + test_do_allocate_overaligned(); test_do_deallocate(); test_do_is_equal(); test_release();