123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493 |
- //////////////////////////////////////////////////////////////////////////////
- //
- // (C) Copyright Ion Gaztanaga 2015-2015. Distributed under the Boost
- // Software License, Version 1.0. (See accompanying file
- // LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
- //
- // See http://www.boost.org/libs/container for documentation.
- //
- //////////////////////////////////////////////////////////////////////////////
- #include <boost/container/pmr/global_resource.hpp>
- #include <boost/core/lightweight_test.hpp>
- #include <boost/intrusive/detail/math.hpp>
- #include "derived_from_memory_resource.hpp"
- #include "memory_resource_logger.hpp"
- using namespace boost::container::pmr;
- template<class PoolResource>
- struct derived_from_pool_resource
- : public PoolResource
- {
- derived_from_pool_resource(const pool_options& opts, memory_resource* upstream)
- : PoolResource(opts, upstream)
- {}
- explicit derived_from_pool_resource(memory_resource *p)
- : PoolResource(p)
- {}
- explicit derived_from_pool_resource(const pool_options &opts)
- : PoolResource(opts)
- {}
- derived_from_pool_resource()
- : PoolResource()
- {}
- using PoolResource::do_allocate;
- using PoolResource::do_deallocate;
- using PoolResource::do_is_equal;
- };
- template<class PoolResource>
- void test_default_constructor()
- {
- //With default options/resource
- {
- derived_from_memory_resource dmr;
- dmr.reset();
- PoolResource m;
- //test postconditions
- BOOST_TEST(m.upstream_resource() == get_default_resource());
- BOOST_TEST(m.options().max_blocks_per_chunk == pool_options_default_max_blocks_per_chunk);
- BOOST_TEST(m.options().largest_required_pool_block == pool_options_default_largest_required_pool_block);
- //test it does not allocate any memory
- BOOST_TEST(dmr.do_allocate_called == false);
- }
- }
- template<class PoolResource>
- void test_upstream_constructor()
- {
- //With a resource, default options
- {
- derived_from_memory_resource dmr;
- dmr.reset();
- PoolResource m(&dmr);
- //test postconditions
- BOOST_TEST(m.upstream_resource() == &dmr);
- BOOST_TEST(m.options().max_blocks_per_chunk == pool_options_default_max_blocks_per_chunk);
- BOOST_TEST(m.options().largest_required_pool_block == pool_options_default_largest_required_pool_block);
- //test it does not allocate any memory
- BOOST_TEST(dmr.do_allocate_called == false);
- }
- }
- template<class PoolResource>
- void test_options_constructor()
- {
- //Default options
- {
- memory_resource_logger mrl;
- BOOST_TEST(mrl.m_info.size() == 0u);
- set_default_resource(&mrl);
- pool_options opts;
- PoolResource m(opts);
- //test postconditions
- BOOST_TEST(m.upstream_resource() == get_default_resource());
- BOOST_TEST(m.options().max_blocks_per_chunk == pool_options_default_max_blocks_per_chunk);
- BOOST_TEST(m.options().largest_required_pool_block == pool_options_default_largest_required_pool_block);
- //test it does not allocate any memory
- BOOST_TEST(mrl.m_info.size() == 0u);
- }
- //Too large option values
- {
- memory_resource_logger mrl;
- BOOST_TEST(mrl.m_info.size() == 0u);
- set_default_resource(&mrl);
- pool_options opts;
- opts.max_blocks_per_chunk = pool_options_default_max_blocks_per_chunk+1;
- opts.largest_required_pool_block = pool_options_default_largest_required_pool_block+1;
- PoolResource m(opts);
- //test postconditions
- BOOST_TEST(m.upstream_resource() == get_default_resource());
- BOOST_TEST(m.options().max_blocks_per_chunk == pool_options_default_max_blocks_per_chunk);
- BOOST_TEST(m.options().largest_required_pool_block == pool_options_default_largest_required_pool_block);
- //test it does not allocate any memory
- BOOST_TEST(mrl.m_info.size() == 0u);
- }
- //Too small option values
- {
- memory_resource_logger mrl;
- BOOST_TEST(mrl.m_info.size() == 0u);
- set_default_resource(&mrl);
- pool_options opts;
- opts.largest_required_pool_block = pool_options_minimum_largest_required_pool_block-1u;
- PoolResource m(opts);
- //test postconditions
- BOOST_TEST(m.upstream_resource() == get_default_resource());
- BOOST_TEST(m.options().max_blocks_per_chunk == pool_options_default_max_blocks_per_chunk);
- BOOST_TEST(m.options().largest_required_pool_block == pool_options_minimum_largest_required_pool_block);
- //test it does not allocate any memory
- BOOST_TEST(mrl.m_info.size() == 0u);
- }
- //In range option values
- {
- memory_resource_logger mrl;
- BOOST_TEST(mrl.m_info.size() == 0u);
- set_default_resource(&mrl);
- pool_options opts;
- opts.max_blocks_per_chunk = pool_options_default_max_blocks_per_chunk;
- opts.largest_required_pool_block = pool_options_minimum_largest_required_pool_block;
- PoolResource m(opts);
- //test postconditions
- BOOST_TEST(m.upstream_resource() == get_default_resource());
- BOOST_TEST(m.options().max_blocks_per_chunk == pool_options_default_max_blocks_per_chunk);
- BOOST_TEST(m.options().largest_required_pool_block == pool_options_minimum_largest_required_pool_block);
- //test it does not allocate any memory
- BOOST_TEST(mrl.m_info.size() == 0u);
- }
- }
- template<class PoolResource>
- void test_options_upstream_constructor()
- {
- //Default options
- {
- derived_from_memory_resource dmr;
- dmr.reset();
- pool_options opts;
- PoolResource m(opts, &dmr);
- //test postconditions
- BOOST_TEST(m.upstream_resource() == &dmr);
- BOOST_TEST(m.options().max_blocks_per_chunk == pool_options_default_max_blocks_per_chunk);
- BOOST_TEST(m.options().largest_required_pool_block == pool_options_default_largest_required_pool_block);
- //test it does not allocate any memory
- BOOST_TEST(dmr.do_allocate_called == false);
- }
- //Too large option values
- {
- derived_from_memory_resource dmr;
- dmr.reset();
- pool_options opts;
- opts.max_blocks_per_chunk = pool_options_default_max_blocks_per_chunk+1;
- opts.largest_required_pool_block = pool_options_default_largest_required_pool_block+1;
- PoolResource m(opts, &dmr);
- //test postconditions
- BOOST_TEST(m.upstream_resource() == &dmr);
- BOOST_TEST(m.options().max_blocks_per_chunk == pool_options_default_max_blocks_per_chunk);
- BOOST_TEST(m.options().largest_required_pool_block == pool_options_default_largest_required_pool_block);
- //test it does not allocate any memory
- BOOST_TEST(dmr.do_allocate_called == false);
- }
- //Too small option values
- {
- derived_from_memory_resource dmr;
- dmr.reset();
- pool_options opts;
- opts.largest_required_pool_block = pool_options_minimum_largest_required_pool_block-1u;
- PoolResource m(opts, &dmr);
- //test postconditions
- BOOST_TEST(m.upstream_resource() == &dmr);
- BOOST_TEST(m.options().max_blocks_per_chunk == pool_options_default_max_blocks_per_chunk);
- BOOST_TEST(m.options().largest_required_pool_block == pool_options_minimum_largest_required_pool_block);
- //test it does not allocate any memory
- BOOST_TEST(dmr.do_allocate_called == false);
- }
- //In range option values
- {
- derived_from_memory_resource dmr;
- dmr.reset();
- pool_options opts;
- opts.max_blocks_per_chunk = pool_options_default_max_blocks_per_chunk;
- opts.largest_required_pool_block = pool_options_minimum_largest_required_pool_block;
- PoolResource m(opts, &dmr);
- //test postconditions
- BOOST_TEST(m.upstream_resource() == &dmr);
- //max blocks is unchanged in this implementation
- BOOST_TEST(m.options().max_blocks_per_chunk == pool_options_default_max_blocks_per_chunk);
- //largest block is rounded to pow2
- BOOST_TEST(m.options().largest_required_pool_block == bi::detail::ceil_pow2(opts.largest_required_pool_block));
- //test it does not allocate any memory
- BOOST_TEST(dmr.do_allocate_called == false);
- }
- }
- template<class PoolResource>
- void test_options()
- {
- //In range option values
- {
- derived_from_memory_resource dmr;
- dmr.reset();
- pool_options opts;
- opts.max_blocks_per_chunk = pool_options_default_max_blocks_per_chunk/2u;
- opts.largest_required_pool_block = (pool_options_default_largest_required_pool_block
- - pool_options_minimum_largest_required_pool_block) | std::size_t(1); //guaranteed to be non power of 2.
- PoolResource m(opts, &dmr);
- //test postconditions
- BOOST_TEST(m.upstream_resource() == &dmr);
- //max blocks is unchanged in this implementation
- BOOST_TEST(m.options().max_blocks_per_chunk == opts.max_blocks_per_chunk);
- //largest block is rounded to pow2
- BOOST_TEST(m.options().largest_required_pool_block == bi::detail::ceil_pow2(opts.largest_required_pool_block));
- //test it does not allocate any memory
- BOOST_TEST(dmr.do_allocate_called == false);
- }
- }
- template<class PoolResource>
- void test_do_allocate_deallocate()
- {
- memory_resource_logger mrl;
- {
- derived_from_pool_resource<PoolResource> dmbr(&mrl);
- {
- //First block from pool 0
- dmbr.do_allocate(1, 1);
- //It should allocate the pool array plus an initial block
- BOOST_TEST(mrl.m_info.size() == 2u);
- //Second block from pool 0
- dmbr.do_allocate(1, 1);
- //It should allocate again (with 2 chunks per block)
- BOOST_TEST(mrl.m_info.size() == 3u);
- //Third block from pool 0
- dmbr.do_allocate(1, 1);
- //It should NOT allocate again (previous was a 2 block chunk)
- BOOST_TEST(mrl.m_info.size() == 3u);
- }
- }
- BOOST_TEST(mrl.m_mismatches == 0u);
- BOOST_TEST(mrl.m_info.size() == 0u);
- //Allocate and deallocate from the same chunk to test block caching
- {
- derived_from_pool_resource<PoolResource> dmbr(&mrl);
- {
- //First block from pool 0
- void *p = dmbr.do_allocate(1, 1);
- //It should allocate the pool array plus an initial block
- BOOST_TEST(mrl.m_info.size() == 2u);
- //No cached, as initial blocks per chunk is 1
- BOOST_TEST(dmbr.pool_cached_blocks(0u) == 0u);
- //Deallocate and allocate again
- dmbr.do_deallocate(p, 1, 1);
- //Cached
- BOOST_TEST(dmbr.pool_cached_blocks(0u) == 1u);
- p = dmbr.do_allocate(1, 1);
- //Reused
- BOOST_TEST(dmbr.pool_cached_blocks(0u) == 0u);
- //It should have NOT allocated (block reuse)
- BOOST_TEST(mrl.m_info.size() == 2u);
- //Allocate again 2 times (a 2 block chunk is exhausted)
- void *p2 = dmbr.do_allocate(1, 1);
- //1 left cached
- BOOST_TEST(dmbr.pool_cached_blocks(0u) == 1u);
- void *p3 = dmbr.do_allocate(1, 1);
- //Cache exhausted
- BOOST_TEST(dmbr.pool_cached_blocks(0u) == 0u);
- //Single chunk allocation happened
- BOOST_TEST(mrl.m_info.size() == 3u);
- //Now deallocate all (no memory is freed, all cached)
- dmbr.do_deallocate(p2, 1, 1);
- dmbr.do_deallocate(p3, 1, 1);
- dmbr.do_deallocate(p, 1, 1);
- BOOST_TEST(dmbr.pool_cached_blocks(0u) == 3u);
- BOOST_TEST(mrl.m_info.size() == 3u);
- }
- }
- BOOST_TEST(mrl.m_mismatches == 0u);
- BOOST_TEST(mrl.m_info.size() == 0u);
- //Now test max block per chunk
- {
- pool_options opts;
- //so after max_blocks_per_chunk*2-1 allocations, all new chunks must hold max_blocks_per_chunk blocks
- opts.max_blocks_per_chunk = 32u;
- derived_from_pool_resource<PoolResource> dmbr(opts, &mrl);
- {
- std::size_t loops = opts.max_blocks_per_chunk*2-1u;
- while(loops--){
- dmbr.do_allocate(1, 1);
- }
- //pool array + log2(max_blocks_per_chunk)+1 chunks (sizes [1, 2, 4, ...])
- const std::size_t num_chunks = bi::detail::floor_log2(opts.max_blocks_per_chunk)+1u;
- BOOST_TEST(mrl.m_info.size() == 1u + num_chunks);
- //Next allocation should allocate max_blocks_per_chunk blocks in a chunk so max_blocks_per_chunk-1 should remain free
- dmbr.do_allocate(1, 1);
- BOOST_TEST(mrl.m_info.size() == 1u + num_chunks + 1u);
- BOOST_TEST(dmbr.pool_cached_blocks(0u) == (opts.max_blocks_per_chunk-1u));
- //Exhaust the chunk and allocate a new one, test max_blocks_per_chunk is not passed again
- loops = opts.max_blocks_per_chunk;
- while(loops--){
- dmbr.do_allocate(1, 1);
- }
- BOOST_TEST(mrl.m_info.size() == 1u + num_chunks + 2u);
- BOOST_TEST(dmbr.pool_cached_blocks(0u) == (opts.max_blocks_per_chunk-1u));
- }
- }
- BOOST_TEST(mrl.m_mismatches == 0u);
- BOOST_TEST(mrl.m_info.size() == 0u);
- //Now test max block per chunk
- {
- pool_options opts;
- //so after max_blocks_per_chunk*2-1 allocations, all new chunks must hold max_blocks_per_chunk blocks
- opts.max_blocks_per_chunk = 32u;
- derived_from_pool_resource<PoolResource> dmbr(opts, &mrl);
- {
- std::size_t loops = opts.max_blocks_per_chunk*2-1u;
- while(loops--){
- dmbr.do_allocate(1, 1);
- }
- //pool array + log2(max_blocks_per_chunk)+1 chunks (sizes [1, 2, 4, ...])
- BOOST_TEST(dmbr.pool_next_blocks_per_chunk(0u) == opts.max_blocks_per_chunk);
- const std::size_t num_chunks = bi::detail::floor_log2(opts.max_blocks_per_chunk)+1u;
- BOOST_TEST(mrl.m_info.size() == 1u + num_chunks);
- //Next allocation should allocate max_blocks_per_chunk blocks in a chunk so max_blocks_per_chunk-1 should remain free
- dmbr.do_allocate(1, 1);
- BOOST_TEST(dmbr.pool_next_blocks_per_chunk(0u) == opts.max_blocks_per_chunk);
- BOOST_TEST(mrl.m_info.size() == 1u + num_chunks + 1u);
- BOOST_TEST(dmbr.pool_cached_blocks(0u) == (opts.max_blocks_per_chunk-1u));
- }
- }
- BOOST_TEST(mrl.m_mismatches == 0u);
- BOOST_TEST(mrl.m_info.size() == 0u);
- //Now test different pool sizes
- {
- pool_options opts;
- //so after max_blocks_per_chunk*2-1 allocations, all new chunks must hold max_blocks_per_chunk blocks
- opts.max_blocks_per_chunk = 1u;
- derived_from_pool_resource<PoolResource> dmbr(opts, &mrl);
- const pool_options &final_opts = dmbr.options();
- //Force pool creation
- dmbr.do_deallocate(dmbr.do_allocate(1, 1), 1, 1);
- //pool array plus first pool's chunk allocation
- BOOST_TEST(mrl.m_info.size() == 2u);
- //pool count must be:
- // log2(the maximum block) - log2(the minimum block) + 1. Example if minimum block is 8, and maximum 32:
- // log(32) - log2(8) + 1u = 3 pools (block sizes: 8, 16, and 32)
- const std::size_t minimum_size = dmbr.pool_block(0u);
- const std::size_t maximum_size = final_opts.largest_required_pool_block;
- BOOST_TEST(dmbr.pool_count() == (1u + bi::detail::floor_log2(maximum_size) - bi::detail::floor_log2(minimum_size)));
- for(std::size_t i = 0, s = minimum_size, max = dmbr.pool_count(); i != max; ++i, s*=2){
- //Except in the first pool, each cache should be empty
- BOOST_TEST(dmbr.pool_cached_blocks(i) == std::size_t(i == 0));
- dmbr.do_deallocate(dmbr.do_allocate(s/2+1, 1), s/2+1, 1);
- dmbr.do_deallocate(dmbr.do_allocate(s-1, 1), s-1, 1);
- dmbr.do_deallocate(dmbr.do_allocate(s, 1), s, 1);
- //pool array plus each previous chunk allocation
- BOOST_TEST(mrl.m_info.size() == (1u + i + 1u));
- //as we limited max_blocks_per_chunk to 1, no cached blocks should be available except one
- BOOST_TEST(dmbr.pool_cached_blocks(i) == 1u);
- }
- //Now test out of maximum values, which should go directly to upstream
- //it should be directly deallocated.
- void *p = dmbr.do_allocate(maximum_size+1, 1);
- BOOST_TEST(mrl.m_info.size() == (1u + dmbr.pool_count() + 1u));
- dmbr.do_deallocate(p, maximum_size+1, 1);
- BOOST_TEST(mrl.m_info.size() == (1u + dmbr.pool_count()));
- }
- BOOST_TEST(mrl.m_mismatches == 0u);
- BOOST_TEST(mrl.m_info.size() == 0u);
- }
- template<class PoolResource>
- void test_do_is_equal()
- {
- //`this == dynamic_cast<const PoolResource*>(&other)`.
- memory_resource_logger mrl;
- derived_from_pool_resource<PoolResource> dmbr(&mrl);
- derived_from_pool_resource<PoolResource> dmbr2(&mrl);
- BOOST_TEST(true == dmbr.do_is_equal(dmbr));
- BOOST_TEST(false == dmbr.do_is_equal(dmbr2));
- //A different type should be always different
- derived_from_memory_resource dmr;
- BOOST_TEST(false == dmbr.do_is_equal(dmr));
- }
- template<class PoolResource>
- void test_release()
- {
- memory_resource_logger mrl;
- {
- pool_options opts;
- //so after max_blocks_per_chunk*2-1 allocations, all new chunks must hold max_blocks_per_chunk blocks
- opts.max_blocks_per_chunk = 4u;
- derived_from_pool_resource<PoolResource> dmbr(opts, &mrl);
- const pool_options &final_opts = dmbr.options();
- const std::size_t minimum_size = dmbr.pool_block(0u);
- const std::size_t maximum_size = final_opts.largest_required_pool_block;
- const std::size_t pool_count = 1u + bi::detail::floor_log2(maximum_size) - bi::detail::floor_log2(minimum_size);
- std::size_t expected_memory_allocs = 0;
- for(std::size_t i = 0, imax = pool_count, s = minimum_size; i != imax; s*=2, ++i){
- for(std::size_t j = 0, j_max = opts.max_blocks_per_chunk*2u-1u; j != j_max; ++j){
- dmbr.do_allocate(s, 1);
- }
- //One due to the pool array, and for each pool, log2(max_blocks_per_chunk)+1 allocations
- expected_memory_allocs = 1 + (bid::floor_log2(opts.max_blocks_per_chunk) + 1u)*(i+1);
- //pool array plus each previous chunk allocation
- BOOST_TEST(mrl.m_info.size() == expected_memory_allocs);
- }
- //Now with out-of-pool sizes
- for(std::size_t j = 0, j_max = opts.max_blocks_per_chunk*2u-1u; j != j_max; ++j){
- dmbr.do_allocate(maximum_size+1, 1);
- BOOST_TEST(mrl.m_info.size() == ++expected_memory_allocs);
- }
- //Now release memory and check all memory allocated through do_allocate was deallocated to upstream
- dmbr.release();
- BOOST_TEST(mrl.m_info.size() == 1u);
- }
- BOOST_TEST(mrl.m_mismatches == 0u);
- BOOST_TEST(mrl.m_info.size() == 0u);
- }
- template<class PoolResource>
- void test_destructor()
- {
- memory_resource_logger mrl;
- {
- pool_options opts;
- //so after max_blocks_per_chunk*2-1 allocations, all new chunks must hold max_blocks_per_chunk blocks
- opts.max_blocks_per_chunk = 4u;
- derived_from_pool_resource<PoolResource> dmbr(opts, &mrl);
- const pool_options &final_opts = dmbr.options();
- const std::size_t minimum_size = dmbr.pool_block(0u);
- const std::size_t maximum_size = final_opts.largest_required_pool_block;
- const std::size_t pool_count = 1u + bi::detail::floor_log2(maximum_size) - bi::detail::floor_log2(minimum_size);
- std::size_t expected_memory_allocs = 0;
- for(std::size_t i = 0, imax = pool_count, s = minimum_size; i != imax; s*=2, ++i){
- for(std::size_t j = 0, j_max = opts.max_blocks_per_chunk*2u-1u; j != j_max; ++j){
- dmbr.do_allocate(s, 1);
- }
- //One due to the pool array, and for each pool, log2(max_blocks_per_chunk)+1 allocations
- expected_memory_allocs = 1 + (bid::floor_log2(opts.max_blocks_per_chunk) + 1u)*(i+1);
- //pool array plus each previous chunk allocation
- BOOST_TEST(mrl.m_info.size() == expected_memory_allocs);
- }
- //Now with out-of-pool sizes
- for(std::size_t j = 0, j_max = opts.max_blocks_per_chunk*2u-1u; j != j_max; ++j){
- dmbr.do_allocate(maximum_size+1, 1);
- BOOST_TEST(mrl.m_info.size() == ++expected_memory_allocs);
- }
- //Don't release, all memory, including internal allocations, should be automatically
- //released after the destructor is run
- }
- BOOST_TEST(mrl.m_mismatches == 0u);
- BOOST_TEST(mrl.m_info.size() == 0u);
- }
- template<class PoolResource>
- void test_pool_resource()
- {
- test_options_upstream_constructor<PoolResource>();
- test_default_constructor<PoolResource>();
- test_upstream_constructor<PoolResource>();
- test_options_constructor<PoolResource>();
- test_options<PoolResource>();
- test_do_allocate_deallocate<PoolResource>();
- test_do_is_equal<PoolResource>();
- test_release<PoolResource>();
- test_destructor<PoolResource>();
- }
|