/* * Copyright Andrey Semashev 2007 - 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) */ /*! * \file util_once_block.cpp * \author Andrey Semashev * \date 24.06.2010 * * \brief This header contains tests for once-blocks implementation. * * The test was adopted from test_once.cpp test of Boost.Thread. */ #define BOOST_TEST_MODULE util_once_block #include #include #if !defined(BOOST_LOG_NO_THREADS) #include #include #include #include #include #include namespace logging = boost::log; enum config { THREAD_COUNT = 20, LOOP_COUNT = 100 }; boost::mutex m; typedef boost::lock_guard< boost::mutex > scoped_lock; logging::once_block_flag flag = BOOST_LOG_ONCE_BLOCK_INIT; int var_to_init_once_flag = 0; void initialize_variable() { // ensure that if multiple threads get in here, they are serialized, so we can see the effect scoped_lock lock(m); ++var_to_init_once_flag; } void once_block_flag_thread(boost::barrier& barrier) { int my_once_value = 0; barrier.wait(); for (unsigned int i = 0; i < LOOP_COUNT; ++i) { BOOST_LOG_ONCE_BLOCK_FLAG(flag) { initialize_variable(); } my_once_value = var_to_init_once_flag; if (my_once_value != 1) { break; } } scoped_lock lock(m); BOOST_CHECK_EQUAL(my_once_value, 1); } // The test checks if the BOOST_LOG_ONCE_BLOCK_FLAG macro works BOOST_AUTO_TEST_CASE(once_block_flag) { boost::thread_group group; boost::barrier barrier(static_cast< unsigned int >(THREAD_COUNT)); try { for (unsigned int i = 0; i < THREAD_COUNT; ++i) { group.create_thread(boost::bind(&once_block_flag_thread, boost::ref(barrier))); } group.join_all(); } catch (...) { group.interrupt_all(); group.join_all(); throw; } BOOST_CHECK_EQUAL(var_to_init_once_flag, 1); } int var_to_init_once = 0; void once_block_thread(boost::barrier& barrier) { int my_once_value = 0; barrier.wait(); for (unsigned int i = 0; i < LOOP_COUNT; ++i) { BOOST_LOG_ONCE_BLOCK() { scoped_lock lock(m); ++var_to_init_once; } my_once_value = var_to_init_once; if (my_once_value != 1) { break; } } scoped_lock lock(m); BOOST_CHECK_EQUAL(my_once_value, 1); } // The test checks if the BOOST_LOG_ONCE_BLOCK macro works BOOST_AUTO_TEST_CASE(once_block) { boost::thread_group group; boost::barrier barrier(static_cast< unsigned int >(THREAD_COUNT)); try { for (unsigned int i = 0; i < THREAD_COUNT; ++i) { group.create_thread(boost::bind(&once_block_thread, boost::ref(barrier))); } group.join_all(); } catch(...) { group.interrupt_all(); group.join_all(); throw; } BOOST_CHECK_EQUAL(var_to_init_once, 1); } struct my_exception { }; unsigned int pass_counter = 0; unsigned int exception_counter = 0; void once_block_with_exception_thread(boost::barrier& barrier) { barrier.wait(); try { BOOST_LOG_ONCE_BLOCK() { scoped_lock lock(m); ++pass_counter; if (pass_counter < 3) { throw my_exception(); } } } catch (my_exception&) { scoped_lock lock(m); ++exception_counter; } } // The test verifies that the once_block flag is not set if an exception is thrown from the once-block BOOST_AUTO_TEST_CASE(once_block_retried_on_exception) { boost::thread_group group; boost::barrier barrier(static_cast< unsigned int >(THREAD_COUNT)); try { for (unsigned int i = 0; i < THREAD_COUNT; ++i) { group.create_thread(boost::bind(&once_block_with_exception_thread, boost::ref(barrier))); } group.join_all(); } catch(...) { group.interrupt_all(); group.join_all(); throw; } BOOST_CHECK_EQUAL(pass_counter, 3u); BOOST_CHECK_EQUAL(exception_counter, 2u); } #else // BOOST_LOG_NO_THREADS namespace logging = boost::log; enum config { LOOP_COUNT = 100 }; logging::once_block_flag flag = BOOST_LOG_ONCE_BLOCK_INIT; int var_to_init_once_flag = 0; void initialize_variable() { ++var_to_init_once_flag; } // The test checks if the BOOST_LOG_ONCE_BLOCK_FLAG macro works BOOST_AUTO_TEST_CASE(once_block_flag) { for (unsigned int i = 0; i < LOOP_COUNT; ++i) { BOOST_LOG_ONCE_BLOCK_FLAG(flag) { initialize_variable(); } if (var_to_init_once_flag != 1) { break; } } BOOST_CHECK_EQUAL(var_to_init_once_flag, 1); } int var_to_init_once = 0; // The test checks if the BOOST_LOG_ONCE_BLOCK macro works BOOST_AUTO_TEST_CASE(once_block) { for (unsigned int i = 0; i < LOOP_COUNT; ++i) { BOOST_LOG_ONCE_BLOCK() { ++var_to_init_once; } if (var_to_init_once != 1) { break; } } BOOST_CHECK_EQUAL(var_to_init_once, 1); } struct my_exception { }; unsigned int pass_counter = 0; unsigned int exception_counter = 0; // The test verifies that the once_block flag is not set if an exception is thrown from the once-block BOOST_AUTO_TEST_CASE(once_block_retried_on_exception) { for (unsigned int i = 0; i < LOOP_COUNT; ++i) { try { BOOST_LOG_ONCE_BLOCK() { ++pass_counter; if (pass_counter < 3) { throw my_exception(); } } } catch (my_exception&) { ++exception_counter; } } BOOST_CHECK_EQUAL(pass_counter, 3u); BOOST_CHECK_EQUAL(exception_counter, 2u); } #endif // BOOST_LOG_NO_THREADS