// Copyright (c) 2011 Helge Bahmann // // 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) #ifndef BOOST_ATOMIC_API_TEST_HELPERS_HPP #define BOOST_ATOMIC_API_TEST_HELPERS_HPP #include #include #include #include #include #include #include #include #include #include #include namespace boost { namespace detail { // Make sure characters are printed as numbers if tests fail inline int test_output_impl(char v) { return v; } inline int test_output_impl(signed char v) { return v; } inline unsigned int test_output_impl(unsigned char v) { return v; } inline int test_output_impl(short v) { return v; } inline unsigned int test_output_impl(unsigned short v) { return v; } } // namespace detail } // namespace boost /* provide helpers that exercise whether the API functions of "boost::atomic" provide the correct operational semantic in the case of sequential execution */ static void test_flag_api(void) { #ifndef BOOST_ATOMIC_NO_ATOMIC_FLAG_INIT boost::atomic_flag f = BOOST_ATOMIC_FLAG_INIT; #else boost::atomic_flag f; #endif BOOST_TEST( !f.test_and_set() ); BOOST_TEST( f.test_and_set() ); f.clear(); BOOST_TEST( !f.test_and_set() ); } template void test_base_operators(T value1, T value2, T value3) { /* explicit load/store */ { boost::atomic a(value1); BOOST_TEST_EQ( a.load(), value1 ); } { boost::atomic a(value1); a.store(value2); BOOST_TEST_EQ( a.load(), value2 ); } /* overloaded assignment/conversion */ { boost::atomic a(value1); BOOST_TEST( value1 == a ); } { boost::atomic a; a = value2; BOOST_TEST( value2 == a ); } /* exchange-type operators */ { boost::atomic a(value1); T n = a.exchange(value2); BOOST_TEST_EQ( a.load(), value2 ); BOOST_TEST_EQ( n, value1 ); } { boost::atomic a(value1); T expected = value1; bool success = a.compare_exchange_strong(expected, value3); BOOST_TEST( success ); BOOST_TEST_EQ( a.load(), value3 ); BOOST_TEST_EQ( expected, value1 ); } { boost::atomic a(value1); T expected = value2; bool success = a.compare_exchange_strong(expected, value3); BOOST_TEST( !success ); BOOST_TEST_EQ( a.load(), value1 ); BOOST_TEST_EQ( expected, value1 ); } { boost::atomic a(value1); T expected; bool success; do { expected = value1; success = a.compare_exchange_weak(expected, value3); } while(!success); BOOST_TEST( success ); BOOST_TEST_EQ( a.load(), value3 ); BOOST_TEST_EQ( expected, value1 ); } { boost::atomic a(value1); T expected; bool success; do { expected = value2; success = a.compare_exchange_weak(expected, value3); if (expected != value2) break; } while(!success); BOOST_TEST( !success ); BOOST_TEST_EQ( a.load(), value1 ); BOOST_TEST_EQ( expected, value1 ); } } // T requires an int constructor template void test_constexpr_ctor() { #ifndef BOOST_NO_CXX11_CONSTEXPR const T value(0); const boost::atomic tester(value); BOOST_TEST( tester == value ); #endif } //! The type traits provides max and min values of type D that can be added/subtracted to T(0) without signed overflow template< typename T, typename D, bool IsSigned = boost::is_signed< D >::value > struct distance_limits { static D min BOOST_PREVENT_MACRO_SUBSTITUTION () BOOST_NOEXCEPT { return (std::numeric_limits< D >::min)(); } static D max BOOST_PREVENT_MACRO_SUBSTITUTION () BOOST_NOEXCEPT { return (std::numeric_limits< D >::max)(); } }; #if defined(BOOST_MSVC) #pragma warning(push) // 'static_cast': truncation of constant value. There is no actual truncation happening because // the cast is only performed if the value fits in the range of the result. #pragma warning(disable: 4309) #endif template< typename T, typename D > struct distance_limits< T*, D, true > { static D min BOOST_PREVENT_MACRO_SUBSTITUTION () BOOST_NOEXCEPT { const std::ptrdiff_t ptrdiff = (std::numeric_limits< std::ptrdiff_t >::min)() / static_cast< std::ptrdiff_t >(sizeof(T)); const D diff = (std::numeric_limits< D >::min)(); // Both values are negative. Return the closest value to zero. return diff < ptrdiff ? static_cast< D >(ptrdiff) : diff; } static D max BOOST_PREVENT_MACRO_SUBSTITUTION () BOOST_NOEXCEPT { const std::ptrdiff_t ptrdiff = (std::numeric_limits< std::ptrdiff_t >::max)() / static_cast< std::ptrdiff_t >(sizeof(T)); const D diff = (std::numeric_limits< D >::max)(); // Both values are positive. Return the closest value to zero. return diff > ptrdiff ? static_cast< D >(ptrdiff) : diff; } }; template< typename T, typename D > struct distance_limits< T*, D, false > { static D min BOOST_PREVENT_MACRO_SUBSTITUTION () BOOST_NOEXCEPT { return (std::numeric_limits< D >::min)(); } static D max BOOST_PREVENT_MACRO_SUBSTITUTION () BOOST_NOEXCEPT { const std::size_t ptrdiff = static_cast< std::size_t >((std::numeric_limits< std::ptrdiff_t >::max)()) / sizeof(T); const D diff = (std::numeric_limits< D >::max)(); return diff > ptrdiff ? static_cast< D >(ptrdiff) : diff; } }; #if defined(BOOST_MSVC) #pragma warning(pop) #endif template void test_additive_operators_with_type_and_test() { // Note: This set of tests is extracted to a separate function because otherwise MSVC-10 for x64 generates broken code const T zero_value = 0; const D zero_diff = 0; const D one_diff = 1; const AddType zero_add = 0; { boost::atomic a(zero_value); bool f = a.add_and_test(zero_diff); BOOST_TEST_EQ( f, true ); BOOST_TEST_EQ( a.load(), zero_value ); f = a.add_and_test(one_diff); BOOST_TEST_EQ( f, false ); BOOST_TEST_EQ( a.load(), T(zero_add + one_diff) ); } { boost::atomic a(zero_value); bool f = a.add_and_test((distance_limits< T, D >::max)()); BOOST_TEST_EQ( f, false ); BOOST_TEST_EQ( a.load(), T(zero_add + (distance_limits< T, D >::max)()) ); } { boost::atomic a(zero_value); bool f = a.add_and_test((distance_limits< T, D >::min)()); BOOST_TEST_EQ( f, ((distance_limits< T, D >::min)() == 0) ); BOOST_TEST_EQ( a.load(), T(zero_add + (distance_limits< T, D >::min)()) ); } { boost::atomic a(zero_value); bool f = a.sub_and_test(zero_diff); BOOST_TEST_EQ( f, true ); BOOST_TEST_EQ( a.load(), zero_value ); f = a.sub_and_test(one_diff); BOOST_TEST_EQ( f, false ); BOOST_TEST_EQ( a.load(), T(zero_add - one_diff) ); } { boost::atomic a(zero_value); bool f = a.sub_and_test((distance_limits< T, D >::max)()); BOOST_TEST_EQ( f, false ); BOOST_TEST_EQ( a.load(), T(zero_add - (distance_limits< T, D >::max)()) ); } { boost::atomic a(zero_value); bool f = a.sub_and_test((distance_limits< T, D >::min)()); BOOST_TEST_EQ( f, ((distance_limits< T, D >::min)() == 0) ); BOOST_TEST_EQ( a.load(), T(zero_add - (distance_limits< T, D >::min)()) ); } } template void test_additive_operators_with_type(T value, D delta) { /* note: the tests explicitly cast the result of any addition to the type to be tested to force truncation of the result to the correct range in case of overflow */ /* explicit add/sub */ { boost::atomic a(value); T n = a.fetch_add(delta); BOOST_TEST_EQ( a.load(), T((AddType)value + delta) ); BOOST_TEST_EQ( n, value ); } { boost::atomic a(value); T n = a.fetch_sub(delta); BOOST_TEST_EQ( a.load(), T((AddType)value - delta) ); BOOST_TEST_EQ( n, value ); } /* overloaded modify/assign*/ { boost::atomic a(value); T n = (a += delta); BOOST_TEST_EQ( a.load(), T((AddType)value + delta) ); BOOST_TEST_EQ( n, T((AddType)value + delta) ); } { boost::atomic a(value); T n = (a -= delta); BOOST_TEST_EQ( a.load(), T((AddType)value - delta) ); BOOST_TEST_EQ( n, T((AddType)value - delta) ); } /* overloaded increment/decrement */ { boost::atomic a(value); T n = a++; BOOST_TEST_EQ( a.load(), T((AddType)value + 1) ); BOOST_TEST_EQ( n, value ); } { boost::atomic a(value); T n = ++a; BOOST_TEST_EQ( a.load(), T((AddType)value + 1) ); BOOST_TEST_EQ( n, T((AddType)value + 1) ); } { boost::atomic a(value); T n = a--; BOOST_TEST_EQ( a.load(), T((AddType)value - 1) ); BOOST_TEST_EQ( n, value ); } { boost::atomic a(value); T n = --a; BOOST_TEST_EQ( a.load(), T((AddType)value - 1) ); BOOST_TEST_EQ( n, T((AddType)value - 1) ); } // Opaque operations { boost::atomic a(value); a.opaque_add(delta); BOOST_TEST_EQ( a.load(), T((AddType)value + delta) ); } { boost::atomic a(value); a.opaque_sub(delta); BOOST_TEST_EQ( a.load(), T((AddType)value - delta) ); } // Modify and test operations test_additive_operators_with_type_and_test< T, D, AddType >(); } template void test_additive_operators(T value, D delta) { test_additive_operators_with_type(value, delta); } template< typename T > void test_negation() { { boost::atomic a((T)1); T n = a.fetch_negate(); BOOST_TEST_EQ( a.load(), (T)-1 ); BOOST_TEST_EQ( n, (T)1 ); n = a.fetch_negate(); BOOST_TEST_EQ( a.load(), (T)1 ); BOOST_TEST_EQ( n, (T)-1 ); } { boost::atomic a((T)1); a.opaque_negate(); BOOST_TEST_EQ( a.load(), (T)-1 ); a.opaque_negate(); BOOST_TEST_EQ( a.load(), (T)1 ); } } template void test_additive_wrap(T value) { { boost::atomic a(value); T n = a.fetch_add(1) + (T)1; BOOST_TEST_EQ( a.load(), n ); } { boost::atomic a(value); T n = a.fetch_sub(1) - (T)1; BOOST_TEST_EQ( a.load(), n ); } } template void test_bit_operators(T value, T delta) { /* explicit and/or/xor */ { boost::atomic a(value); T n = a.fetch_and(delta); BOOST_TEST_EQ( a.load(), T(value & delta) ); BOOST_TEST_EQ( n, value ); } { boost::atomic a(value); T n = a.fetch_or(delta); BOOST_TEST_EQ( a.load(), T(value | delta) ); BOOST_TEST_EQ( n, value ); } { boost::atomic a(value); T n = a.fetch_xor(delta); BOOST_TEST_EQ( a.load(), T(value ^ delta) ); BOOST_TEST_EQ( n, value ); } { boost::atomic a(value); T n = a.fetch_complement(); BOOST_TEST_EQ( a.load(), T(~value) ); BOOST_TEST_EQ( n, value ); } /* overloaded modify/assign */ { boost::atomic a(value); T n = (a &= delta); BOOST_TEST_EQ( a.load(), T(value & delta) ); BOOST_TEST_EQ( n, T(value & delta) ); } { boost::atomic a(value); T n = (a |= delta); BOOST_TEST_EQ( a.load(), T(value | delta) ); BOOST_TEST_EQ( n, T(value | delta) ); } { boost::atomic a(value); T n = (a ^= delta); BOOST_TEST_EQ( a.load(), T(value ^ delta) ); BOOST_TEST_EQ( n, T(value ^ delta) ); } // Opaque operations { boost::atomic a(value); a.opaque_and(delta); BOOST_TEST_EQ( a.load(), T(value & delta) ); } { boost::atomic a(value); a.opaque_or(delta); BOOST_TEST_EQ( a.load(), T(value | delta) ); } { boost::atomic a(value); a.opaque_xor(delta); BOOST_TEST_EQ( a.load(), T(value ^ delta) ); } { boost::atomic a(value); a.opaque_complement(); BOOST_TEST_EQ( a.load(), T(~value) ); } // Modify and test operations { boost::atomic a((T)1); bool f = a.and_and_test((T)1); BOOST_TEST_EQ( f, false ); BOOST_TEST_EQ( a.load(), T(1) ); f = a.and_and_test((T)0); BOOST_TEST_EQ( f, true ); BOOST_TEST_EQ( a.load(), T(0) ); f = a.and_and_test((T)0); BOOST_TEST_EQ( f, true ); BOOST_TEST_EQ( a.load(), T(0) ); } { boost::atomic a((T)0); bool f = a.or_and_test((T)0); BOOST_TEST_EQ( f, true ); BOOST_TEST_EQ( a.load(), T(0) ); f = a.or_and_test((T)1); BOOST_TEST_EQ( f, false ); BOOST_TEST_EQ( a.load(), T(1) ); f = a.or_and_test((T)1); BOOST_TEST_EQ( f, false ); BOOST_TEST_EQ( a.load(), T(1) ); } { boost::atomic a((T)0); bool f = a.xor_and_test((T)0); BOOST_TEST_EQ( f, true ); BOOST_TEST_EQ( a.load(), T(0) ); f = a.xor_and_test((T)1); BOOST_TEST_EQ( f, false ); BOOST_TEST_EQ( a.load(), T(1) ); f = a.xor_and_test((T)1); BOOST_TEST_EQ( f, true ); BOOST_TEST_EQ( a.load(), T(0) ); } // Bit test and modify operations { boost::atomic a((T)42); bool f = a.bit_test_and_set(0); BOOST_TEST_EQ( f, false ); BOOST_TEST_EQ( a.load(), T(43) ); f = a.bit_test_and_set(1); BOOST_TEST_EQ( f, true ); BOOST_TEST_EQ( a.load(), T(43) ); f = a.bit_test_and_set(2); BOOST_TEST_EQ( f, false ); BOOST_TEST_EQ( a.load(), T(47) ); } { boost::atomic a((T)42); bool f = a.bit_test_and_reset(0); BOOST_TEST_EQ( f, false ); BOOST_TEST_EQ( a.load(), T(42) ); f = a.bit_test_and_reset(1); BOOST_TEST_EQ( f, true ); BOOST_TEST_EQ( a.load(), T(40) ); f = a.bit_test_and_set(2); BOOST_TEST_EQ( f, false ); BOOST_TEST_EQ( a.load(), T(44) ); } { boost::atomic a((T)42); bool f = a.bit_test_and_complement(0); BOOST_TEST_EQ( f, false ); BOOST_TEST_EQ( a.load(), T(43) ); f = a.bit_test_and_complement(1); BOOST_TEST_EQ( f, true ); BOOST_TEST_EQ( a.load(), T(41) ); f = a.bit_test_and_complement(2); BOOST_TEST_EQ( f, false ); BOOST_TEST_EQ( a.load(), T(45) ); } } template void do_test_integral_api(boost::false_type) { BOOST_TEST( sizeof(boost::atomic) >= sizeof(T)); test_base_operators(42, 43, 44); test_additive_operators(42, 17); test_bit_operators((T)0x5f5f5f5f5f5f5f5fULL, (T)0xf5f5f5f5f5f5f5f5ULL); /* test for unsigned overflow/underflow */ test_additive_operators((T)-1, 1); test_additive_operators(0, 1); /* test for signed overflow/underflow */ test_additive_operators(((T)-1) >> (sizeof(T) * 8 - 1), 1); test_additive_operators(1 + (((T)-1) >> (sizeof(T) * 8 - 1)), 1); } template void do_test_integral_api(boost::true_type) { do_test_integral_api(boost::false_type()); test_additive_wrap(0u); BOOST_CONSTEXPR_OR_CONST T all_ones = ~(T)0u; test_additive_wrap(all_ones); BOOST_CONSTEXPR_OR_CONST T max_signed_twos_compl = all_ones >> 1; test_additive_wrap(all_ones ^ max_signed_twos_compl); test_additive_wrap(max_signed_twos_compl); } template inline void test_integral_api(void) { do_test_integral_api(boost::is_unsigned()); if (boost::is_signed::value) test_negation(); } template void test_pointer_api(void) { BOOST_TEST_GE( sizeof(boost::atomic), sizeof(T *)); BOOST_TEST_GE( sizeof(boost::atomic), sizeof(T *)); T values[3]; test_base_operators(&values[0], &values[1], &values[2]); test_additive_operators(&values[1], 1); test_base_operators(&values[0], &values[1], &values[2]); #if defined(BOOST_HAS_INTPTR_T) boost::atomic ptr; boost::atomic integral; BOOST_TEST_EQ( ptr.is_lock_free(), integral.is_lock_free() ); #endif } enum test_enum { foo, bar, baz }; static void test_enum_api(void) { test_base_operators(foo, bar, baz); } template struct test_struct { typedef T value_type; value_type i; inline bool operator==(const test_struct & c) const {return i == c.i;} inline bool operator!=(const test_struct & c) const {return i != c.i;} }; template< typename Char, typename Traits, typename T > inline std::basic_ostream< Char, Traits >& operator<< (std::basic_ostream< Char, Traits >& strm, test_struct< T > const& s) { using boost::detail::test_output_impl; strm << "{" << test_output_impl(s.i) << "}"; return strm; } template void test_struct_api(void) { T a = {1}, b = {2}, c = {3}; test_base_operators(a, b, c); { boost::atomic sa; boost::atomic si; BOOST_TEST_EQ( sa.is_lock_free(), si.is_lock_free() ); } } template struct test_struct_x2 { typedef T value_type; value_type i, j; inline bool operator==(const test_struct_x2 & c) const {return i == c.i && j == c.j;} inline bool operator!=(const test_struct_x2 & c) const {return i != c.i && j != c.j;} }; template< typename Char, typename Traits, typename T > inline std::basic_ostream< Char, Traits >& operator<< (std::basic_ostream< Char, Traits >& strm, test_struct_x2< T > const& s) { using boost::detail::test_output_impl; strm << "{" << test_output_impl(s.i) << ", " << test_output_impl(s.j) << "}"; return strm; } template void test_struct_x2_api(void) { T a = {1, 1}, b = {2, 2}, c = {3, 3}; test_base_operators(a, b, c); } struct large_struct { long data[64]; inline bool operator==(const large_struct & c) const { return std::memcmp(data, &c.data, sizeof(data)) == 0; } inline bool operator!=(const large_struct & c) const { return std::memcmp(data, &c.data, sizeof(data)) != 0; } }; template< typename Char, typename Traits > inline std::basic_ostream< Char, Traits >& operator<< (std::basic_ostream< Char, Traits >& strm, large_struct const&) { strm << "[large_struct]"; return strm; } static void test_large_struct_api(void) { large_struct a = {{1}}, b = {{2}}, c = {{3}}; test_base_operators(a, b, c); } struct test_struct_with_ctor { typedef unsigned int value_type; value_type i; test_struct_with_ctor() : i(0x01234567) {} inline bool operator==(const test_struct_with_ctor & c) const {return i == c.i;} inline bool operator!=(const test_struct_with_ctor & c) const {return i != c.i;} }; template< typename Char, typename Traits > inline std::basic_ostream< Char, Traits >& operator<< (std::basic_ostream< Char, Traits >& strm, test_struct_with_ctor const&) { strm << "[test_struct_with_ctor]"; return strm; } static void test_struct_with_ctor_api(void) { { test_struct_with_ctor s; boost::atomic sa; // Check that the default constructor was called BOOST_TEST( sa.load() == s ); } test_struct_with_ctor a, b, c; a.i = 1; b.i = 2; c.i = 3; test_base_operators(a, b, c); } #endif