/* Copyright (C) 2000, 2001 Stephen Cleary * Copyright (C) 2011 Kwan Ting Chan * * Use, modification and distribution is subject to the * Boost Software License, Version 1.0. (See accompanying * file LICENSE_1_0.txt or http://www.boost.org/LICENSE_1_0.txt) */ #include "random_shuffle.hpp" #include #include #include #include #include #include #include #include #include #include #include // Each "tester" object below checks into and out of the "cdtor_checker", // which will check for any problems related to the construction/destruction of // "tester" objects. class cdtor_checker { private: // Each constructed object registers its "this" pointer into "objs" std::set objs; public: // True iff all objects that have checked in have checked out bool ok() const { return objs.empty(); } ~cdtor_checker() { BOOST_TEST(ok()); } void check_in(void * const This) { BOOST_TEST(objs.find(This) == objs.end()); objs.insert(This); } void check_out(void * const This) { BOOST_TEST(objs.find(This) != objs.end()); objs.erase(This); } }; static cdtor_checker mem; struct tester { tester(bool throw_except = false) { if(throw_except) { throw std::logic_error("Deliberate constructor exception"); } mem.check_in(this); } tester(const tester &) { mem.check_in(this); } ~tester() { mem.check_out(this); } }; // This is a wrapper around a UserAllocator. It just registers alloc/dealloc // to/from the system memory. It's used to make sure pool's are allocating // and deallocating system memory properly. // Do NOT use this class with static or singleton pools. template struct TrackAlloc { typedef typename UserAllocator::size_type size_type; typedef typename UserAllocator::difference_type difference_type; static std::set allocated_blocks; static char * malloc(const size_type bytes) { char * const ret = UserAllocator::malloc(bytes); allocated_blocks.insert(ret); return ret; } static void free(char * const block) { BOOST_TEST(allocated_blocks.find(block) != allocated_blocks.end()); allocated_blocks.erase(block); UserAllocator::free(block); } static bool ok() { return allocated_blocks.empty(); } }; template std::set TrackAlloc::allocated_blocks; typedef TrackAlloc track_alloc; void test() { { // Do nothing pool boost::object_pool pool; } { // Construct several tester objects. Don't delete them (i.e., // test pool's garbage collection). boost::object_pool pool; for(int i=0; i < 10; ++i) { pool.construct(); } } { // Construct several tester objects. Delete some of them. boost::object_pool pool; std::vector v; for(int i=0; i < 10; ++i) { v.push_back(pool.construct()); } pool_test_random_shuffle(v.begin(), v.end()); for(int j=0; j < 5; ++j) { pool.destroy(v[j]); } } { // Test how pool reacts with constructors that throw exceptions. // Shouldn't have any memory leaks. boost::object_pool pool; for(int i=0; i < 5; ++i) { pool.construct(); } for(int j=0; j < 5; ++j) { try { // The following constructions will raise an exception. pool.construct(true); } catch(const std::logic_error &) {} } } } void test_alloc() { { // Allocate several tester objects. Delete one. std::vector > l; for(int i=0; i < 10; ++i) { l.push_back(tester()); } l.pop_back(); } { // Allocate several tester objects. Delete two. std::deque > l; for(int i=0; i < 10; ++i) { l.push_back(tester()); } l.pop_back(); l.pop_front(); } { // Allocate several tester objects. Delete two. std::list > l; // lists rebind their allocators, so dumping is useless for(int i=0; i < 10; ++i) { l.push_back(tester()); } l.pop_back(); l.pop_front(); } tester * tmp; { // Create a memory leak on purpose. (Allocator doesn't have // garbage collection) // (Note: memory leak) boost::pool_allocator a; tmp = a.allocate(1, 0); new (tmp) tester(); } if(mem.ok()) { BOOST_ERROR("Pool allocator cleaned up itself"); } // Remove memory checker entry (to avoid error later) and // clean up memory leak tmp->~tester(); boost::pool_allocator::deallocate(tmp, 1); // test allocating zero elements { boost::pool_allocator alloc; tester* ip = alloc.allocate(0); alloc.deallocate(ip, 0); } } void test_mem_usage() { typedef boost::pool pool_type; { // Constructor should do nothing; no memory allocation pool_type pool(sizeof(int)); BOOST_TEST(track_alloc::ok()); BOOST_TEST(!pool.release_memory()); BOOST_TEST(!pool.purge_memory()); // Should allocate from system pool.free(pool.malloc()); BOOST_TEST(!track_alloc::ok()); // Ask pool to give up memory it's not using; this should succeed BOOST_TEST(pool.release_memory()); BOOST_TEST(track_alloc::ok()); // Should allocate from system again pool.malloc(); // loses the pointer to the returned chunk (*A*) // Ask pool to give up memory it's not using; this should fail BOOST_TEST(!pool.release_memory()); // Force pool to give up memory it's not using; this should succeed // This will clean up the memory leak from (*A*) BOOST_TEST(pool.purge_memory()); BOOST_TEST(track_alloc::ok()); // Should allocate from system again pool.malloc(); // loses the pointer to the returned chunk (*B*) // pool's destructor should purge the memory // This will clean up the memory leak from (*B*) } BOOST_TEST(track_alloc::ok()); } void test_void() { typedef boost::pool_allocator void_allocator; typedef boost::fast_pool_allocator fast_void_allocator; typedef void_allocator::rebind::other int_allocator; typedef fast_void_allocator::rebind::other fast_int_allocator; std::vector v1; std::vector v2; } int main() { std::srand(static_cast(std::time(0))); test(); test_alloc(); test_mem_usage(); test_void(); return boost::report_errors(); }