mirror of
https://github.com/boostorg/container.git
synced 2026-08-04 03:34:11 +02:00
Correctly handle zero-sized allocations in polymorphic memory resources.
This commit is contained in:
@@ -145,6 +145,11 @@ void* monotonic_buffer_resource::do_allocate(std::size_t bytes, std::size_t alig
|
||||
#endif
|
||||
}
|
||||
|
||||
//A zero-sized request must still work correctly so treat it as a 1-byte allocation
|
||||
if(!bytes){
|
||||
bytes = 1u;
|
||||
}
|
||||
|
||||
//See if there is room in current buffer
|
||||
std::size_t aligner = 0u;
|
||||
if(this->remaining_storage(alignment, aligner) < bytes){
|
||||
|
||||
@@ -116,6 +116,11 @@ void test_new_delete_resource()
|
||||
#endif
|
||||
mr->deallocate(addr, 16, 1);
|
||||
BOOST_TEST(memcount == allocation_count);
|
||||
|
||||
//A zero-sized allocation must still work
|
||||
void *const zero_addr = mr->allocate(0, 1);
|
||||
mr->deallocate(zero_addr, 0, 1);
|
||||
BOOST_TEST(memcount == allocation_count);
|
||||
}
|
||||
|
||||
#endif //BOOST_CONTAINER_ASAN
|
||||
@@ -143,6 +148,24 @@ void test_null_memory_resource()
|
||||
BOOST_TEST(bad_allocexception_thrown == true);
|
||||
if(p)
|
||||
mr->deallocate(p, 1, 1);
|
||||
|
||||
//A zero-sized allocation must also fail (the standard permits a zero
|
||||
//request to fail, and null_memory_resource always throws).
|
||||
bool zero_bad_alloc_thrown = false;
|
||||
void *pz = 0;
|
||||
BOOST_CONTAINER_TRY{
|
||||
pz = mr->allocate(0, 1);
|
||||
}
|
||||
BOOST_CONTAINER_CATCH(std::bad_alloc&) {
|
||||
zero_bad_alloc_thrown = true;
|
||||
}
|
||||
BOOST_CONTAINER_CATCH(...) {
|
||||
}
|
||||
BOOST_CONTAINER_CATCH_END
|
||||
|
||||
BOOST_TEST(zero_bad_alloc_thrown == true);
|
||||
if(pz)
|
||||
mr->deallocate(pz, 0, 1);
|
||||
#endif //BOOST_NO_EXCEPTIONS
|
||||
}
|
||||
|
||||
|
||||
@@ -360,6 +360,42 @@ void test_do_allocate()
|
||||
BOOST_TEST(mrl.m_info.size() == 0u);
|
||||
}
|
||||
|
||||
void test_do_allocate_zero()
|
||||
{
|
||||
//Regression test: after exhausting an external buffer, a zero-sized
|
||||
//allocation requesting an alignment that needs padding used to underflow
|
||||
//remaining_storage() (m_current_buffer_size wrapped around to SIZE_MAX)
|
||||
//because the padding alone did not fit in the current buffer. In addition,
|
||||
//a zero-sized allocation must return a non-null pointer to distinct storage.
|
||||
memory_resource_logger mrl;
|
||||
//An 8-aligned buffer of 7 bytes reproduces the reported scenario.
|
||||
boost::move_detail::aligned_storage<8u, 8u>::type storage;
|
||||
const std::size_t buffer_size = 7u;
|
||||
{
|
||||
monotonic_buffer_resource m(&storage, buffer_size, &mrl);
|
||||
BOOST_TEST(m.remaining_storage(1u) == buffer_size);
|
||||
|
||||
//Exhaust the buffer with a byte-aligned allocation
|
||||
void *const p_full = m.allocate(buffer_size, 1u);
|
||||
BOOST_TEST(p_full != 0);
|
||||
BOOST_TEST(m.remaining_storage(1u) == 0u);
|
||||
BOOST_TEST(mrl.m_info.size() == 0u);
|
||||
|
||||
//A zero-sized allocation on an exhausted buffer must still work
|
||||
void *const p_zero1 = m.allocate(0u, 1u);
|
||||
BOOST_TEST(p_zero1 != 0);
|
||||
BOOST_TEST(m.remaining_storage(1u) < m.next_buffer_size());
|
||||
|
||||
//A zero-sized allocation with a stricter alignment must honor it
|
||||
void *const p_zero2 = m.allocate(0u, 4u);
|
||||
BOOST_TEST((std::size_t(p_zero2) % 4u) == 0u);
|
||||
BOOST_TEST(m.remaining_storage(1u) < m.next_buffer_size());
|
||||
}
|
||||
//All upstream memory must have been released
|
||||
BOOST_TEST(mrl.m_mismatches == 0u);
|
||||
BOOST_TEST(mrl.m_info.size() == 0u);
|
||||
}
|
||||
|
||||
void test_do_deallocate()
|
||||
{
|
||||
memory_resource_logger mrl;
|
||||
@@ -477,6 +513,7 @@ int main()
|
||||
|
||||
test_upstream_resource();
|
||||
test_do_allocate();
|
||||
test_do_allocate_zero();
|
||||
test_do_deallocate();
|
||||
test_do_is_equal();
|
||||
test_release();
|
||||
|
||||
@@ -390,6 +390,42 @@ void test_do_allocate_deallocate()
|
||||
BOOST_TEST(mrl.m_info.size() == 0u);
|
||||
}
|
||||
|
||||
template<class PoolResource>
|
||||
void test_do_allocate_zero()
|
||||
{
|
||||
//A zero-sized allocation must return a valid, non-null pointer that can be
|
||||
//deallocated, and successive zero-sized allocations must return distinct
|
||||
//blocks (pooled allocations never alias live blocks).
|
||||
memory_resource_logger mrl;
|
||||
{
|
||||
derived_from_pool_resource<PoolResource> dmbr(&mrl);
|
||||
//Zero bytes is served from the smallest pool
|
||||
void *p0 = dmbr.do_allocate(0u, 1u);
|
||||
BOOST_TEST(p0 != 0);
|
||||
void *p1 = dmbr.do_allocate(0u, 1u);
|
||||
BOOST_TEST(p1 != 0);
|
||||
//Distinct live blocks must not alias
|
||||
BOOST_TEST(p0 != p1);
|
||||
//Both come from the smallest pool (index 0)
|
||||
BOOST_TEST(dmbr.pool_index(0u) == 0u);
|
||||
|
||||
//Round-trip deallocation must not report mismatches
|
||||
dmbr.do_deallocate(p1, 0u, 1u);
|
||||
dmbr.do_deallocate(p0, 0u, 1u);
|
||||
//Deallocated zero-sized blocks are cached, not returned to upstream
|
||||
BOOST_TEST(dmbr.pool_cached_blocks(0u) >= 1u);
|
||||
|
||||
//A max-aligned zero-sized allocation must also succeed
|
||||
void *p2 = dmbr.do_allocate(0u, memory_resource::max_align);
|
||||
BOOST_TEST(p2 != 0);
|
||||
BOOST_TEST((std::size_t(p2) % memory_resource::max_align) == 0u);
|
||||
dmbr.do_deallocate(p2, 0u, memory_resource::max_align);
|
||||
}
|
||||
//All memory must have been released back to upstream on destruction
|
||||
BOOST_TEST(mrl.m_mismatches == 0u);
|
||||
BOOST_TEST(mrl.m_info.size() == 0u);
|
||||
}
|
||||
|
||||
template<class PoolResource>
|
||||
void test_do_is_equal()
|
||||
{
|
||||
@@ -487,6 +523,7 @@ void test_pool_resource()
|
||||
test_options_constructor<PoolResource>();
|
||||
test_options<PoolResource>();
|
||||
test_do_allocate_deallocate<PoolResource>();
|
||||
test_do_allocate_zero<PoolResource>();
|
||||
test_do_is_equal<PoolResource>();
|
||||
test_release<PoolResource>();
|
||||
test_destructor<PoolResource>();
|
||||
|
||||
@@ -188,6 +188,15 @@ void test_do_allocate_deallocate()
|
||||
|
||||
//new_allocator, high alignment
|
||||
mr.deallocate(mr.allocate(16, max_alignment_value*4u), 16, max_alignment_value*4u);
|
||||
|
||||
//new_allocator, zero size, low alignment: must return a non-null pointer
|
||||
void *pz = mr.allocate(0, 1);
|
||||
mr.deallocate(pz, 0, 1);
|
||||
|
||||
//new_allocator, zero size, high alignment
|
||||
void *pza = mr.allocate(0, max_alignment_value*4u);
|
||||
BOOST_TEST((std::size_t(pza) % (max_alignment_value*4u)) == 0u);
|
||||
mr.deallocate(pza, 0, max_alignment_value*4u);
|
||||
}
|
||||
{
|
||||
typedef resource_adaptor<std ::allocator<int> > new_resource_alloc_t;
|
||||
@@ -199,6 +208,17 @@ void test_do_allocate_deallocate()
|
||||
|
||||
//std::allocator, high alignment
|
||||
mr.deallocate(mr.allocate(16, max_alignment_value*4u), 16, max_alignment_value*4u);
|
||||
|
||||
//std::allocator, zero size, low alignment: must return a non-null pointer
|
||||
void *pz = mr.allocate(0, 1);
|
||||
mr.deallocate(pz, 0, 1);
|
||||
|
||||
//std::allocator, zero size, high alignment: must return a non-null,
|
||||
//correctly aligned pointer
|
||||
void *pza = mr.allocate(0, max_alignment_value*4u);
|
||||
BOOST_TEST(pza != 0);
|
||||
BOOST_TEST((std::size_t(pza) % (max_alignment_value*4u)) == 0u);
|
||||
mr.deallocate(pza, 0, max_alignment_value*4u);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user