| // Copyright (C) 2011 Tim Blechmann |
| // |
| // 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) |
| |
| // enables error checks via dummy::~dtor |
| #define BOOST_LOCKFREE_FREELIST_INIT_RUNS_DTOR |
| |
| #include <boost/lockfree/detail/freelist.hpp> |
| #include <boost/lockfree/queue.hpp> |
| |
| #include <boost/foreach.hpp> |
| #include <boost/thread.hpp> |
| #include <boost/scoped_ptr.hpp> |
| |
| #define BOOST_TEST_MAIN |
| #ifdef BOOST_LOCKFREE_INCLUDE_TESTS |
| #include <boost/test/included/unit_test.hpp> |
| #else |
| #include <boost/test/unit_test.hpp> |
| #endif |
| |
| #include <set> |
| |
| #include "test_helpers.hpp" |
| |
| using boost::lockfree::detail::atomic; |
| |
| atomic<bool> test_running(false); |
| |
| struct dummy |
| { |
| dummy(void) |
| { |
| if (test_running.load(boost::lockfree::detail::memory_order_relaxed)) |
| assert(allocated == 0); |
| allocated = 1; |
| } |
| |
| ~dummy(void) |
| { |
| if (test_running.load(boost::lockfree::detail::memory_order_relaxed)) |
| assert(allocated == 1); |
| allocated = 0; |
| } |
| |
| size_t padding[2]; // for used for the freelist node |
| int allocated; |
| }; |
| |
| template <typename freelist_type, |
| bool threadsafe, |
| bool bounded> |
| void run_test(void) |
| { |
| freelist_type fl(std::allocator<int>(), 8); |
| |
| std::set<dummy*> nodes; |
| |
| dummy d; |
| if (bounded) |
| test_running.store(true); |
| |
| for (int i = 0; i != 4; ++i) { |
| dummy * allocated = fl.template construct<threadsafe, bounded>(); |
| BOOST_REQUIRE(nodes.find(allocated) == nodes.end()); |
| nodes.insert(allocated); |
| } |
| |
| BOOST_FOREACH(dummy * d, nodes) |
| fl.template destruct<threadsafe>(d); |
| |
| nodes.clear(); |
| for (int i = 0; i != 4; ++i) |
| nodes.insert(fl.template construct<threadsafe, bounded>()); |
| |
| BOOST_FOREACH(dummy * d, nodes) |
| fl.template destruct<threadsafe>(d); |
| |
| for (int i = 0; i != 4; ++i) |
| nodes.insert(fl.template construct<threadsafe, bounded>()); |
| |
| if (bounded) |
| test_running.store(false); |
| } |
| |
| template <bool bounded> |
| void run_tests(void) |
| { |
| run_test<boost::lockfree::detail::freelist_stack<dummy>, true, bounded>(); |
| run_test<boost::lockfree::detail::freelist_stack<dummy>, false, bounded>(); |
| run_test<boost::lockfree::detail::fixed_size_freelist<dummy>, true, bounded>(); |
| } |
| |
| BOOST_AUTO_TEST_CASE( freelist_tests ) |
| { |
| run_tests<false>(); |
| run_tests<true>(); |
| } |
| |
| template <typename freelist_type, bool threadsafe> |
| void oom_test(void) |
| { |
| const bool bounded = true; |
| freelist_type fl(std::allocator<int>(), 8); |
| |
| for (int i = 0; i != 8; ++i) |
| fl.template construct<threadsafe, bounded>(); |
| |
| dummy * allocated = fl.template construct<threadsafe, bounded>(); |
| BOOST_REQUIRE(allocated == NULL); |
| } |
| |
| BOOST_AUTO_TEST_CASE( oom_tests ) |
| { |
| oom_test<boost::lockfree::detail::freelist_stack<dummy>, true >(); |
| oom_test<boost::lockfree::detail::freelist_stack<dummy>, false >(); |
| oom_test<boost::lockfree::detail::fixed_size_freelist<dummy>, true >(); |
| oom_test<boost::lockfree::detail::fixed_size_freelist<dummy>, false >(); |
| } |
| |
| |
| template <typename freelist_type, bool bounded> |
| struct freelist_tester |
| { |
| static const int size = 128; |
| static const int thread_count = 4; |
| #ifndef BOOST_LOCKFREE_STRESS_TEST |
| static const int operations_per_thread = 1000; |
| #else |
| static const int operations_per_thread = 100000; |
| #endif |
| |
| freelist_type fl; |
| boost::lockfree::queue<dummy*> allocated_nodes; |
| |
| atomic<bool> running; |
| static_hashed_set<dummy*, 1<<16 > working_set; |
| |
| |
| freelist_tester(void): |
| fl(std::allocator<int>(), size), allocated_nodes(256) |
| {} |
| |
| void run() |
| { |
| running = true; |
| |
| if (bounded) |
| test_running.store(true); |
| boost::thread_group alloc_threads; |
| boost::thread_group dealloc_threads; |
| |
| for (int i = 0; i != thread_count; ++i) |
| dealloc_threads.create_thread(boost::bind(&freelist_tester::deallocate, this)); |
| |
| for (int i = 0; i != thread_count; ++i) |
| alloc_threads.create_thread(boost::bind(&freelist_tester::allocate, this)); |
| alloc_threads.join_all(); |
| test_running.store(false); |
| running = false; |
| dealloc_threads.join_all(); |
| } |
| |
| void allocate(void) |
| { |
| for (long i = 0; i != operations_per_thread; ++i) { |
| for (;;) { |
| dummy * node = fl.template construct<true, bounded>(); |
| if (node) { |
| bool success = working_set.insert(node); |
| assert(success); |
| allocated_nodes.push(node); |
| break; |
| } |
| } |
| } |
| } |
| |
| void deallocate(void) |
| { |
| for (;;) { |
| dummy * node; |
| if (allocated_nodes.pop(node)) { |
| bool success = working_set.erase(node); |
| assert(success); |
| fl.template destruct<true>(node); |
| } |
| |
| if (running.load() == false) |
| break; |
| } |
| |
| dummy * node; |
| while (allocated_nodes.pop(node)) { |
| bool success = working_set.erase(node); |
| assert(success); |
| fl.template destruct<true>(node); |
| } |
| } |
| }; |
| |
| template <typename Tester> |
| void run_tester() |
| { |
| boost::scoped_ptr<Tester> tester (new Tester); |
| tester->run(); |
| } |
| |
| |
| BOOST_AUTO_TEST_CASE( unbounded_freelist_test ) |
| { |
| typedef freelist_tester<boost::lockfree::detail::freelist_stack<dummy>, false > test_type; |
| run_tester<test_type>(); |
| } |
| |
| |
| BOOST_AUTO_TEST_CASE( bounded_freelist_test ) |
| { |
| typedef freelist_tester<boost::lockfree::detail::freelist_stack<dummy>, true > test_type; |
| run_tester<test_type>(); |
| } |
| |
| BOOST_AUTO_TEST_CASE( fixed_size_freelist_test ) |
| { |
| typedef freelist_tester<boost::lockfree::detail::fixed_size_freelist<dummy>, true > test_type; |
| run_tester<test_type>(); |
| } |