diff options
Diffstat (limited to 'src/boost/libs/atomic/test')
19 files changed, 3216 insertions, 0 deletions
diff --git a/src/boost/libs/atomic/test/Jamfile.v2 b/src/boost/libs/atomic/test/Jamfile.v2 new file mode 100644 index 000000000..42bc16d24 --- /dev/null +++ b/src/boost/libs/atomic/test/Jamfile.v2 @@ -0,0 +1,37 @@ +# Boost.Atomic Library test Jamfile +# +# Copyright (c) 2011 Helge Bahmann +# Copyright (c) 2012 Tim Blechmann +# Copyright (c) 2020 Andrey Semashev +# +# 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) + +import testing ; + +project boost/atomic/test + : requirements + <threading>multi + <library>/boost/thread//boost_thread + <library>/boost/atomic//boost_atomic + <target-os>windows:<define>BOOST_USE_WINDOWS_H + <target-os>windows:<define>_WIN32_WINNT=0x0500 + <toolset>gcc,<target-os>windows:<linkflags>"-lkernel32" + ; + +test-suite atomic + : [ run native_api.cpp ] + [ run native_ref_api.cpp ] + [ run fallback_api.cpp ] + [ run fallback_ref_api.cpp ] + [ run atomicity.cpp ] + [ run atomicity_ref.cpp ] + [ run ordering.cpp ] + [ run ordering_ref.cpp ] + [ run lockfree.cpp ] + [ compile-fail cf_arith_void_ptr.cpp ] + [ compile-fail cf_arith_func_ptr.cpp ] + [ compile-fail cf_arith_mem_ptr.cpp ] + [ compile c_implicit_ctor.cpp ] + ; diff --git a/src/boost/libs/atomic/test/aligned_object.hpp b/src/boost/libs/atomic/test/aligned_object.hpp new file mode 100644 index 000000000..9a8098782 --- /dev/null +++ b/src/boost/libs/atomic/test/aligned_object.hpp @@ -0,0 +1,61 @@ +// Copyright (c) 2020 Andrey Semashev +// +// 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_TESTS_ALIGNED_OBJECT_HPP_INCLUDED_ +#define BOOST_ATOMIC_TESTS_ALIGNED_OBJECT_HPP_INCLUDED_ + +#include <cstddef> +#include <new> +#include <boost/config.hpp> +#include <boost/cstdint.hpp> + +//! A wrapper that creates an object that has at least the specified alignment +template< typename T, std::size_t Alignment > +class aligned_object +{ +private: + T* m_p; + unsigned char m_storage[Alignment + sizeof(T)]; + +public: + aligned_object() + { + m_p = new (get_aligned_storage()) T; + } + + explicit aligned_object(T const& value) + { + m_p = new (get_aligned_storage()) T(value); + } + + ~aligned_object() BOOST_NOEXCEPT + { + m_p->~T(); + } + + T& get() const BOOST_NOEXCEPT + { + return *m_p; + } + + BOOST_DELETED_FUNCTION(aligned_object(aligned_object const&)) + BOOST_DELETED_FUNCTION(aligned_object& operator= (aligned_object const&)) + +private: + unsigned char* get_aligned_storage() + { +#if defined(BOOST_HAS_INTPTR_T) + typedef boost::uintptr_t uintptr_type; +#else + typedef std::size_t uintptr_type; +#endif + uintptr_type p = (uintptr_type)(unsigned char*)m_storage; + p = (p + (uintptr_type)(Alignment - 1u)) & ~(uintptr_type)(Alignment - 1u); + return (unsigned char*)p; + } +}; + +#endif // BOOST_ATOMIC_TESTS_ALIGNED_OBJECT_HPP_INCLUDED_ diff --git a/src/boost/libs/atomic/test/api_test_helpers.hpp b/src/boost/libs/atomic/test/api_test_helpers.hpp new file mode 100644 index 000000000..3f09700a3 --- /dev/null +++ b/src/boost/libs/atomic/test/api_test_helpers.hpp @@ -0,0 +1,1238 @@ +// Copyright (c) 2011 Helge Bahmann +// Copyright (c) 2017 - 2020 Andrey Semashev +// +// 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 <boost/atomic.hpp> +#include <cstddef> +#include <cstring> +#include <cstdlib> +#include <limits> +#include <iostream> +#include <boost/config.hpp> +#include <boost/cstdint.hpp> +#include <boost/type.hpp> +#include <boost/type_traits/integral_constant.hpp> +#include <boost/type_traits/alignment_of.hpp> +#include <boost/type_traits/is_pointer.hpp> +#include <boost/type_traits/is_signed.hpp> +#include <boost/type_traits/is_unsigned.hpp> +#include <boost/type_traits/make_signed.hpp> +#include <boost/type_traits/make_unsigned.hpp> +#include <boost/type_traits/conditional.hpp> +#include "aligned_object.hpp" + +struct test_stream_type +{ + typedef std::ios_base& (*ios_base_manip)(std::ios_base&); + typedef std::basic_ios< char, std::char_traits< char > >& (*basic_ios_manip)(std::basic_ios< char, std::char_traits< char > >&); + typedef std::ostream& (*stream_manip)(std::ostream&); + + template< typename T > + test_stream_type const& operator<< (T const& value) const + { + std::cerr << value; + return *this; + } + + test_stream_type const& operator<< (ios_base_manip manip) const + { + std::cerr << manip; + return *this; + } + test_stream_type const& operator<< (basic_ios_manip manip) const + { + std::cerr << manip; + return *this; + } + test_stream_type const& operator<< (stream_manip manip) const + { + std::cerr << manip; + return *this; + } + + // Make sure characters are printed as numbers if tests fail + test_stream_type const& operator<< (char value) const + { + std::cerr << static_cast< int >(value); + return *this; + } + test_stream_type const& operator<< (signed char value) const + { + std::cerr << static_cast< int >(value); + return *this; + } + test_stream_type const& operator<< (unsigned char value) const + { + std::cerr << static_cast< unsigned int >(value); + return *this; + } + test_stream_type const& operator<< (short value) const + { + std::cerr << static_cast< int >(value); + return *this; + } + test_stream_type const& operator<< (unsigned short value) const + { + std::cerr << static_cast< unsigned int >(value); + return *this; + } + +#if defined(BOOST_HAS_INT128) + // Some GCC versions don't provide output operators for __int128 + test_stream_type const& operator<< (boost::int128_type const& v) const + { + std::cerr << static_cast< long long >(v); + return *this; + } + test_stream_type const& operator<< (boost::uint128_type const& v) const + { + std::cerr << static_cast< unsigned long long >(v); + return *this; + } +#endif // defined(BOOST_HAS_INT128) +#if defined(BOOST_HAS_FLOAT128) + // libstdc++ does not provide output operators for __float128 + test_stream_type const& operator<< (boost::float128_type const& v) const + { + std::cerr << static_cast< double >(v); + return *this; + } +#endif // defined(BOOST_HAS_FLOAT128) +}; + +const test_stream_type test_stream = {}; + +#define BOOST_LIGHTWEIGHT_TEST_OSTREAM test_stream + +#include <boost/core/lightweight_test.hpp> + +#include "value_with_epsilon.hpp" + +const unsigned int max_weak_cas_loops = 1000; + +//! Wrapper type for atomic template +template< typename T > +struct atomic_wrapper +{ + typedef boost::atomic< T > atomic_type; + + atomic_type a; + + BOOST_DEFAULTED_FUNCTION(atomic_wrapper(), {}) + explicit atomic_wrapper(T const& value) : a(value) {} +}; + +//! Wrapper type for atomic_ref template +template< typename T > +struct atomic_ref_wrapper +{ + typedef boost::atomic_ref< T > atomic_type; + + aligned_object< T, atomic_type::required_alignment > object; + atomic_type a; + + atomic_ref_wrapper() : a(object.get()) {} + explicit atomic_ref_wrapper(T const& value) : object(value), a(object.get()) {} +}; + +/* provide helpers that exercise whether the API +functions of "boost::atomic" provide the correct +operational semantic in the case of sequential +execution */ + +inline 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() ); + BOOST_TEST( !f.test_and_set() ); + BOOST_TEST( f.test() ); + BOOST_TEST( f.test_and_set() ); + BOOST_TEST( f.test() ); + f.clear(); + BOOST_TEST( !f.test() ); + BOOST_TEST( !f.test_and_set() ); +} + +template< typename T > +inline void test_atomic_type_traits(boost::type< boost::atomic< T > >) +{ + BOOST_TEST_GE(sizeof(boost::atomic< T >), sizeof(T)); +} + +template< typename T > +inline void test_atomic_type_traits(boost::type< boost::atomic_ref< T > >) +{ + if (boost::atomic_ref< T >::is_always_lock_free) + { + BOOST_TEST_GE(boost::atomic_ref< T >::required_alignment, boost::alignment_of< T >::value); + } + else + { + // Lock-based implementation should not require alignment higher than alignof(T) + BOOST_TEST_EQ(boost::atomic_ref< T >::required_alignment, boost::alignment_of< T >::value); + } +} + +template< template< typename > class Wrapper, typename T > +void test_base_operators(T value1, T value2, T value3) +{ + test_atomic_type_traits(boost::type< typename Wrapper<T>::atomic_type >()); + + // explicit load/store + { + Wrapper<T> wrapper(value1); + typename Wrapper<T>::atomic_type& a = wrapper.a; + BOOST_TEST_EQ( a.load(), value1 ); + } + + { + Wrapper<T> wrapper(value1); + typename Wrapper<T>::atomic_type& a = wrapper.a; + a.store(value2); + BOOST_TEST_EQ( a.load(), value2 ); + } + + // overloaded assignment/conversion + { + Wrapper<T> wrapper(value1); + typename Wrapper<T>::atomic_type& a = wrapper.a; + BOOST_TEST( value1 == a ); + } + + { + Wrapper<T> wrapper(value1); + typename Wrapper<T>::atomic_type& a = wrapper.a; + a = value2; + BOOST_TEST( value2 == a ); + } + + // exchange-type operators + { + Wrapper<T> wrapper(value1); + typename Wrapper<T>::atomic_type& a = wrapper.a; + T n = a.exchange(value2); + BOOST_TEST_EQ( a.load(), value2 ); + BOOST_TEST_EQ( n, value1 ); + } + + { + Wrapper<T> wrapper(value1); + typename Wrapper<T>::atomic_type& a = wrapper.a; + 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 ); + } + + { + Wrapper<T> wrapper(value1); + typename Wrapper<T>::atomic_type& a = wrapper.a; + 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 ); + } + + { + Wrapper<T> wrapper(value1); + typename Wrapper<T>::atomic_type& a = wrapper.a; + T expected; + unsigned int loops = 0; + bool success = false; + do + { + expected = value1; + success = a.compare_exchange_weak(expected, value3); + ++loops; + } + while (!success && loops < max_weak_cas_loops); + BOOST_TEST( success ); + BOOST_TEST_EQ( a.load(), value3 ); + BOOST_TEST_EQ( expected, value1 ); + } + + { + Wrapper<T> wrapper(value1); + typename Wrapper<T>::atomic_type& a = wrapper.a; + T expected; + unsigned int loops = 0; + bool success = false; + do + { + expected = value2; + success = a.compare_exchange_weak(expected, value3); + if (expected != value2) + break; + ++loops; + } + while (!success && loops < max_weak_cas_loops); + BOOST_TEST( !success ); + BOOST_TEST_EQ( a.load(), value1 ); + BOOST_TEST_EQ( expected, value1 ); + } +} + +//! Tests whether boost::atomic supports constexpr constructor. Note that boost::atomic_ref (as std::atomic_ref) does not support constexpr constructor. +template< typename T > +void test_constexpr_ctor() +{ +#ifndef BOOST_ATOMIC_DETAIL_NO_CXX11_CONSTEXPR_UNION_INIT + constexpr T value(0); + constexpr boost::atomic<T> 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 +{ + //! Difference type D promoted to the width of type T + typedef typename boost::conditional< + IsSigned, + boost::make_signed< T >, + boost::make_unsigned< T > + >::type::type promoted_difference_type; + + 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 > +{ + //! Difference type D promoted to the width of type T + typedef std::ptrdiff_t promoted_difference_type; + + 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 > +{ + //! Difference type D promoted to the width of type T + typedef std::size_t promoted_difference_type; + + 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_HAS_INT128) + +// At least libstdc++ does not specialize std::numeric_limits for __int128 in strict mode (i.e. with GNU extensions disabled). +// So we have to specialize the limits ourself. We assume two's complement signed representation. +template< typename T, bool IsSigned > +struct distance_limits< T, boost::int128_type, IsSigned > +{ + //! Difference type D promoted to the width of type T + typedef boost::int128_type promoted_difference_type; + + static boost::int128_type min BOOST_PREVENT_MACRO_SUBSTITUTION () BOOST_NOEXCEPT + { + return -(max)() - 1; + } + static boost::int128_type max BOOST_PREVENT_MACRO_SUBSTITUTION () BOOST_NOEXCEPT + { + return static_cast< boost::int128_type >((~static_cast< boost::uint128_type >(0u)) >> 1); + } +}; + +template< typename T, bool IsSigned > +struct distance_limits< T, boost::uint128_type, IsSigned > +{ + //! Difference type D promoted to the width of type T + typedef boost::uint128_type promoted_difference_type; + + static boost::uint128_type min BOOST_PREVENT_MACRO_SUBSTITUTION () BOOST_NOEXCEPT + { + return 0u; + } + static boost::uint128_type max BOOST_PREVENT_MACRO_SUBSTITUTION () BOOST_NOEXCEPT + { + return ~static_cast< boost::uint128_type >(0u); + } +}; + +#endif // defined(BOOST_HAS_INT128) + +#if defined(BOOST_MSVC) +#pragma warning(pop) +#endif + +#if defined(BOOST_MSVC) +#pragma warning(push) +// unary minus operator applied to unsigned type, result still unsigned +#pragma warning(disable: 4146) +#endif + +template< template< typename > class Wrapper, typename T, typename D, typename AddType > +void test_additive_operators_with_type_and_test() +{ +#if defined(UBSAN) + // clang UBSAN flags this test when AddType is a pointer as it considers subtracting from a null pointer (zero_add) an UB + if (boost::is_pointer< AddType >::value) + return; +#endif + + // Note: This set of tests is extracted to a separate function because otherwise MSVC-10 for x64 generates broken code + typedef typename distance_limits< T, D >::promoted_difference_type promoted_difference_type; + typedef typename boost::make_unsigned< promoted_difference_type >::type unsigned_promoted_difference_type; + const T zero_value = 0; + const D zero_diff = 0; + const D one_diff = 1; + const AddType zero_add = 0; + { + Wrapper<T> wrapper(zero_value); + typename Wrapper<T>::atomic_type& a = wrapper.a; + bool f = a.add_and_test(zero_diff); + BOOST_TEST_EQ( f, false ); + BOOST_TEST_EQ( a.load(), zero_value ); + + f = a.add_and_test(one_diff); + BOOST_TEST_EQ( f, true ); + BOOST_TEST_EQ( a.load(), T(zero_add + one_diff) ); + } + { + Wrapper<T> wrapper(zero_value); + typename Wrapper<T>::atomic_type& a = wrapper.a; + bool f = a.add_and_test((distance_limits< T, D >::max)()); + BOOST_TEST_EQ( f, true ); + BOOST_TEST_EQ( a.load(), T(zero_add + (distance_limits< T, D >::max)()) ); + } + { + Wrapper<T> wrapper(zero_value); + typename Wrapper<T>::atomic_type& a = wrapper.a; + 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)()) ); + } + + { + Wrapper<T> wrapper(zero_value); + typename Wrapper<T>::atomic_type& a = wrapper.a; + bool f = a.sub_and_test(zero_diff); + BOOST_TEST_EQ( f, false ); + BOOST_TEST_EQ( a.load(), zero_value ); + + f = a.sub_and_test(one_diff); + BOOST_TEST_EQ( f, true ); + BOOST_TEST_EQ( a.load(), T(zero_add - one_diff) ); + } + { + Wrapper<T> wrapper(zero_value); + typename Wrapper<T>::atomic_type& a = wrapper.a; + bool f = a.sub_and_test((distance_limits< T, D >::max)()); + BOOST_TEST_EQ( f, true ); + BOOST_TEST_EQ( a.load(), T(zero_add - (distance_limits< T, D >::max)()) ); + } + { + Wrapper<T> wrapper(zero_value); + typename Wrapper<T>::atomic_type& a = wrapper.a; + bool f = a.sub_and_test((distance_limits< T, D >::min)()); + BOOST_TEST_EQ( f, ((distance_limits< T, D >::min)() != 0) ); + // Be very careful as to not cause signed overflow on negation + unsigned_promoted_difference_type umin = static_cast< unsigned_promoted_difference_type >( + static_cast< promoted_difference_type >((distance_limits< T, D >::min)())); + umin = -umin; + promoted_difference_type neg_min; + std::memcpy(&neg_min, &umin, sizeof(neg_min)); + BOOST_TEST_EQ( a.load(), T(zero_add + neg_min) ); + } +} + +#if defined(BOOST_MSVC) +#pragma warning(pop) +#endif + +template< template< typename > class Wrapper, typename T, typename D, typename AddType > +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 + { + Wrapper<T> wrapper(value); + typename Wrapper<T>::atomic_type& a = wrapper.a; + T n = a.fetch_add(delta); + BOOST_TEST_EQ( a.load(), T((AddType)value + delta) ); + BOOST_TEST_EQ( n, value ); + } + + { + Wrapper<T> wrapper(value); + typename Wrapper<T>::atomic_type& a = wrapper.a; + T n = a.fetch_sub(delta); + BOOST_TEST_EQ( a.load(), T((AddType)value - delta) ); + BOOST_TEST_EQ( n, value ); + } + + // overloaded modify/assign + { + Wrapper<T> wrapper(value); + typename Wrapper<T>::atomic_type& a = wrapper.a; + T n = (a += delta); + BOOST_TEST_EQ( a.load(), T((AddType)value + delta) ); + BOOST_TEST_EQ( n, T((AddType)value + delta) ); + } + + { + Wrapper<T> wrapper(value); + typename Wrapper<T>::atomic_type& a = wrapper.a; + T n = (a -= delta); + BOOST_TEST_EQ( a.load(), T((AddType)value - delta) ); + BOOST_TEST_EQ( n, T((AddType)value - delta) ); + } + + // overloaded increment/decrement + { + Wrapper<T> wrapper(value); + typename Wrapper<T>::atomic_type& a = wrapper.a; + T n = a++; + BOOST_TEST_EQ( a.load(), T((AddType)value + 1) ); + BOOST_TEST_EQ( n, value ); + } + + { + Wrapper<T> wrapper(value); + typename Wrapper<T>::atomic_type& a = wrapper.a; + T n = ++a; + BOOST_TEST_EQ( a.load(), T((AddType)value + 1) ); + BOOST_TEST_EQ( n, T((AddType)value + 1) ); + } + + { + Wrapper<T> wrapper(value); + typename Wrapper<T>::atomic_type& a = wrapper.a; + T n = a--; + BOOST_TEST_EQ( a.load(), T((AddType)value - 1) ); + BOOST_TEST_EQ( n, value ); + } + + { + Wrapper<T> wrapper(value); + typename Wrapper<T>::atomic_type& a = wrapper.a; + T n = --a; + BOOST_TEST_EQ( a.load(), T((AddType)value - 1) ); + BOOST_TEST_EQ( n, T((AddType)value - 1) ); + } + + // Operations returning the actual resulting value + { + Wrapper<T> wrapper(value); + typename Wrapper<T>::atomic_type& a = wrapper.a; + T n = a.add(delta); + BOOST_TEST_EQ( a.load(), T((AddType)value + delta) ); + BOOST_TEST_EQ( n, T((AddType)value + delta) ); + } + + { + Wrapper<T> wrapper(value); + typename Wrapper<T>::atomic_type& a = wrapper.a; + T n = a.sub(delta); + BOOST_TEST_EQ( a.load(), T((AddType)value - delta) ); + BOOST_TEST_EQ( n, T((AddType)value - delta) ); + } + + // Opaque operations + { + Wrapper<T> wrapper(value); + typename Wrapper<T>::atomic_type& a = wrapper.a; + a.opaque_add(delta); + BOOST_TEST_EQ( a.load(), T((AddType)value + delta) ); + } + + { + Wrapper<T> wrapper(value); + typename Wrapper<T>::atomic_type& a = wrapper.a; + a.opaque_sub(delta); + BOOST_TEST_EQ( a.load(), T((AddType)value - delta) ); + } + + // Modify and test operations + test_additive_operators_with_type_and_test< Wrapper, T, D, AddType >(); +} + +template< template< typename > class Wrapper, typename T, typename D > +void test_additive_operators(T value, D delta) +{ + test_additive_operators_with_type< Wrapper, T, D, T >(value, delta); +} + +template< template< typename > class Wrapper, typename T > +void test_negation() +{ + { + Wrapper<T> wrapper((T)1); + typename Wrapper<T>::atomic_type& a = wrapper.a; + 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 ); + } + { + Wrapper<T> wrapper((T)1); + typename Wrapper<T>::atomic_type& a = wrapper.a; + T n = a.negate(); + BOOST_TEST_EQ( a.load(), (T)-1 ); + BOOST_TEST_EQ( n, (T)-1 ); + + n = a.negate(); + BOOST_TEST_EQ( a.load(), (T)1 ); + BOOST_TEST_EQ( n, (T)1 ); + } + { + Wrapper<T> wrapper((T)1); + typename Wrapper<T>::atomic_type& a = wrapper.a; + a.opaque_negate(); + BOOST_TEST_EQ( a.load(), (T)-1 ); + + a.opaque_negate(); + BOOST_TEST_EQ( a.load(), (T)1 ); + } + { + Wrapper<T> wrapper((T)1); + typename Wrapper<T>::atomic_type& a = wrapper.a; + bool f = a.negate_and_test(); + BOOST_TEST_EQ( f, true ); + BOOST_TEST_EQ( a.load(), (T)-1 ); + + f = a.negate_and_test(); + BOOST_TEST_EQ( f, true ); + BOOST_TEST_EQ( a.load(), (T)1 ); + } + { + Wrapper<T> wrapper((T)0); + typename Wrapper<T>::atomic_type& a = wrapper.a; + bool f = a.negate_and_test(); + BOOST_TEST_EQ( f, false ); + BOOST_TEST_EQ( a.load(), (T)0 ); + } +} + +template< template< typename > class Wrapper, typename T > +void test_additive_wrap(T value) +{ + { + Wrapper<T> wrapper(value); + typename Wrapper<T>::atomic_type& a = wrapper.a; + T n = a.fetch_add(1) + (T)1; + BOOST_TEST_EQ( a.load(), n ); + } + { + Wrapper<T> wrapper(value); + typename Wrapper<T>::atomic_type& a = wrapper.a; + T n = a.fetch_sub(1) - (T)1; + BOOST_TEST_EQ( a.load(), n ); + } +} + +template< template< typename > class Wrapper, typename T > +void test_bit_operators(T value, T delta) +{ + // explicit and/or/xor + { + Wrapper<T> wrapper(value); + typename Wrapper<T>::atomic_type& a = wrapper.a; + T n = a.fetch_and(delta); + BOOST_TEST_EQ( a.load(), T(value & delta) ); + BOOST_TEST_EQ( n, value ); + } + + { + Wrapper<T> wrapper(value); + typename Wrapper<T>::atomic_type& a = wrapper.a; + T n = a.fetch_or(delta); + BOOST_TEST_EQ( a.load(), T(value | delta) ); + BOOST_TEST_EQ( n, value ); + } + + { + Wrapper<T> wrapper(value); + typename Wrapper<T>::atomic_type& a = wrapper.a; + T n = a.fetch_xor(delta); + BOOST_TEST_EQ( a.load(), T(value ^ delta) ); + BOOST_TEST_EQ( n, value ); + } + + { + Wrapper<T> wrapper(value); + typename Wrapper<T>::atomic_type& a = wrapper.a; + T n = a.fetch_complement(); + BOOST_TEST_EQ( a.load(), T(~value) ); + BOOST_TEST_EQ( n, value ); + } + + // overloaded modify/assign + { + Wrapper<T> wrapper(value); + typename Wrapper<T>::atomic_type& a = wrapper.a; + T n = (a &= delta); + BOOST_TEST_EQ( a.load(), T(value & delta) ); + BOOST_TEST_EQ( n, T(value & delta) ); + } + + { + Wrapper<T> wrapper(value); + typename Wrapper<T>::atomic_type& a = wrapper.a; + T n = (a |= delta); + BOOST_TEST_EQ( a.load(), T(value | delta) ); + BOOST_TEST_EQ( n, T(value | delta) ); + } + + { + Wrapper<T> wrapper(value); + typename Wrapper<T>::atomic_type& a = wrapper.a; + T n = (a ^= delta); + BOOST_TEST_EQ( a.load(), T(value ^ delta) ); + BOOST_TEST_EQ( n, T(value ^ delta) ); + } + + // Operations returning the actual resulting value + { + Wrapper<T> wrapper(value); + typename Wrapper<T>::atomic_type& a = wrapper.a; + T n = a.bitwise_and(delta); + BOOST_TEST_EQ( a.load(), T(value & delta) ); + BOOST_TEST_EQ( n, T(value & delta) ); + } + + { + Wrapper<T> wrapper(value); + typename Wrapper<T>::atomic_type& a = wrapper.a; + T n = a.bitwise_or(delta); + BOOST_TEST_EQ( a.load(), T(value | delta) ); + BOOST_TEST_EQ( n, T(value | delta) ); + } + + { + Wrapper<T> wrapper(value); + typename Wrapper<T>::atomic_type& a = wrapper.a; + T n = a.bitwise_xor(delta); + BOOST_TEST_EQ( a.load(), T(value ^ delta) ); + BOOST_TEST_EQ( n, T(value ^ delta) ); + } + + { + Wrapper<T> wrapper(value); + typename Wrapper<T>::atomic_type& a = wrapper.a; + T n = a.bitwise_complement(); + BOOST_TEST_EQ( a.load(), T(~value) ); + BOOST_TEST_EQ( n, T(~value) ); + } + + // Opaque operations + { + Wrapper<T> wrapper(value); + typename Wrapper<T>::atomic_type& a = wrapper.a; + a.opaque_and(delta); + BOOST_TEST_EQ( a.load(), T(value & delta) ); + } + + { + Wrapper<T> wrapper(value); + typename Wrapper<T>::atomic_type& a = wrapper.a; + a.opaque_or(delta); + BOOST_TEST_EQ( a.load(), T(value | delta) ); + } + + { + Wrapper<T> wrapper(value); + typename Wrapper<T>::atomic_type& a = wrapper.a; + a.opaque_xor(delta); + BOOST_TEST_EQ( a.load(), T(value ^ delta) ); + } + + { + Wrapper<T> wrapper(value); + typename Wrapper<T>::atomic_type& a = wrapper.a; + a.opaque_complement(); + BOOST_TEST_EQ( a.load(), T(~value) ); + } + + // Modify and test operations + { + Wrapper<T> wrapper((T)1); + typename Wrapper<T>::atomic_type& a = wrapper.a; + bool f = a.and_and_test((T)1); + BOOST_TEST_EQ( f, true ); + BOOST_TEST_EQ( a.load(), T(1) ); + + f = a.and_and_test((T)0); + BOOST_TEST_EQ( f, false ); + BOOST_TEST_EQ( a.load(), T(0) ); + + f = a.and_and_test((T)0); + BOOST_TEST_EQ( f, false ); + BOOST_TEST_EQ( a.load(), T(0) ); + } + + { + Wrapper<T> wrapper((T)0); + typename Wrapper<T>::atomic_type& a = wrapper.a; + bool f = a.or_and_test((T)0); + BOOST_TEST_EQ( f, false ); + BOOST_TEST_EQ( a.load(), T(0) ); + + f = a.or_and_test((T)1); + BOOST_TEST_EQ( f, true ); + BOOST_TEST_EQ( a.load(), T(1) ); + + f = a.or_and_test((T)1); + BOOST_TEST_EQ( f, true ); + BOOST_TEST_EQ( a.load(), T(1) ); + } + + { + Wrapper<T> wrapper((T)0); + typename Wrapper<T>::atomic_type& a = wrapper.a; + bool f = a.xor_and_test((T)0); + BOOST_TEST_EQ( f, false ); + BOOST_TEST_EQ( a.load(), T(0) ); + + f = a.xor_and_test((T)1); + BOOST_TEST_EQ( f, true ); + BOOST_TEST_EQ( a.load(), T(1) ); + + f = a.xor_and_test((T)1); + BOOST_TEST_EQ( f, false ); + BOOST_TEST_EQ( a.load(), T(0) ); + } + + { + Wrapper<T> wrapper((T)0); + typename Wrapper<T>::atomic_type& a = wrapper.a; + bool f = a.complement_and_test(); + BOOST_TEST_EQ( f, true ); + BOOST_TEST_EQ( a.load(), static_cast< T >(~static_cast< T >(0)) ); + + f = a.complement_and_test(); + BOOST_TEST_EQ( f, false ); + BOOST_TEST_EQ( a.load(), T(0) ); + } + + // Bit test and modify operations + { + Wrapper<T> wrapper((T)42); + typename Wrapper<T>::atomic_type& a = wrapper.a; + 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) ); + } + + { + Wrapper<T> wrapper((T)42); + typename Wrapper<T>::atomic_type& a = wrapper.a; + 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) ); + } + + { + Wrapper<T> wrapper((T)42); + typename Wrapper<T>::atomic_type& a = wrapper.a; + 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) ); + } + + // Test that a runtime value works for the bit index. This is important for asm block constraints. + { + unsigned int runtime_bit_index = std::rand() & 7u; + Wrapper<T> wrapper((T)42); + typename Wrapper<T>::atomic_type& a = wrapper.a; + + a.bit_test_and_set(runtime_bit_index); + a.bit_test_and_reset(runtime_bit_index); + a.bit_test_and_complement(runtime_bit_index); + } +} + +template< template< typename > class Wrapper, typename T > +void do_test_integral_api(boost::false_type) +{ + test_base_operators< Wrapper, T >(42, 43, 44); + test_additive_operators< Wrapper, T, T >(42, 17); + test_bit_operators< Wrapper, T >((T)0x5f5f5f5f5f5f5f5fULL, (T)0xf5f5f5f5f5f5f5f5ULL); + + /* test for unsigned overflow/underflow */ + test_additive_operators< Wrapper, T, T >((T)-1, 1); + test_additive_operators< Wrapper, T, T >(0, 1); + /* test for signed overflow/underflow */ + test_additive_operators< Wrapper, T, T >(((T)-1) >> (sizeof(T) * 8 - 1), 1); + test_additive_operators< Wrapper, T, T >(1 + (((T)-1) >> (sizeof(T) * 8 - 1)), 1); +} + +template< template< typename > class Wrapper, typename T > +void do_test_integral_api(boost::true_type) +{ + do_test_integral_api< Wrapper, T >(boost::false_type()); + + test_additive_wrap< Wrapper, T >(0u); + BOOST_CONSTEXPR_OR_CONST T all_ones = ~(T)0u; + test_additive_wrap< Wrapper, T >(all_ones); + BOOST_CONSTEXPR_OR_CONST T max_signed_twos_compl = all_ones >> 1; + test_additive_wrap< Wrapper, T >(all_ones ^ max_signed_twos_compl); + test_additive_wrap< Wrapper, T >(max_signed_twos_compl); +} + +template< template< typename > class Wrapper, typename T > +inline void test_integral_api(void) +{ + do_test_integral_api< Wrapper, T >(boost::is_unsigned<T>()); + + if (boost::is_signed<T>::value) + test_negation< Wrapper, T >(); +} + +#if !defined(BOOST_ATOMIC_NO_FLOATING_POINT) + +template< template< typename > class Wrapper, typename T, typename D > +void test_fp_additive_operators(T value, D delta) +{ + // explicit add/sub + { + Wrapper<T> wrapper(value); + typename Wrapper<T>::atomic_type& a = wrapper.a; + T n = a.fetch_add(delta); + BOOST_TEST_EQ( a.load(), approx(T(value + delta)) ); + BOOST_TEST_EQ( n, approx(value) ); + } + + { + Wrapper<T> wrapper(value); + typename Wrapper<T>::atomic_type& a = wrapper.a; + T n = a.fetch_sub(delta); + BOOST_TEST_EQ( a.load(), approx(T(value - delta)) ); + BOOST_TEST_EQ( n, approx(value) ); + } + + // overloaded modify/assign + { + Wrapper<T> wrapper(value); + typename Wrapper<T>::atomic_type& a = wrapper.a; + T n = (a += delta); + BOOST_TEST_EQ( a.load(), approx(T(value + delta)) ); + BOOST_TEST_EQ( n, approx(T(value + delta)) ); + } + + { + Wrapper<T> wrapper(value); + typename Wrapper<T>::atomic_type& a = wrapper.a; + T n = (a -= delta); + BOOST_TEST_EQ( a.load(), approx(T(value - delta)) ); + BOOST_TEST_EQ( n, approx(T(value - delta)) ); + } + + // Operations returning the actual resulting value + { + Wrapper<T> wrapper(value); + typename Wrapper<T>::atomic_type& a = wrapper.a; + T n = a.add(delta); + BOOST_TEST_EQ( a.load(), approx(T(value + delta)) ); + BOOST_TEST_EQ( n, approx(T(value + delta)) ); + } + + { + Wrapper<T> wrapper(value); + typename Wrapper<T>::atomic_type& a = wrapper.a; + T n = a.sub(delta); + BOOST_TEST_EQ( a.load(), approx(T(value - delta)) ); + BOOST_TEST_EQ( n, approx(T(value - delta)) ); + } + + // Opaque operations + { + Wrapper<T> wrapper(value); + typename Wrapper<T>::atomic_type& a = wrapper.a; + a.opaque_add(delta); + BOOST_TEST_EQ( a.load(), approx(T(value + delta)) ); + } + + { + Wrapper<T> wrapper(value); + typename Wrapper<T>::atomic_type& a = wrapper.a; + a.opaque_sub(delta); + BOOST_TEST_EQ( a.load(), approx(T(value - delta)) ); + } +} + +template< template< typename > class Wrapper, typename T > +void test_fp_negation() +{ + { + Wrapper<T> wrapper((T)1); + typename Wrapper<T>::atomic_type& a = wrapper.a; + T n = a.fetch_negate(); + BOOST_TEST_EQ( a.load(), approx((T)-1) ); + BOOST_TEST_EQ( n, approx((T)1) ); + + n = a.fetch_negate(); + BOOST_TEST_EQ( a.load(), approx((T)1) ); + BOOST_TEST_EQ( n, approx((T)-1) ); + } + { + Wrapper<T> wrapper((T)1); + typename Wrapper<T>::atomic_type& a = wrapper.a; + T n = a.negate(); + BOOST_TEST_EQ( a.load(), approx((T)-1) ); + BOOST_TEST_EQ( n, approx((T)-1) ); + + n = a.negate(); + BOOST_TEST_EQ( a.load(), approx((T)1) ); + BOOST_TEST_EQ( n, approx((T)1) ); + } + { + Wrapper<T> wrapper((T)1); + typename Wrapper<T>::atomic_type& a = wrapper.a; + a.opaque_negate(); + BOOST_TEST_EQ( a.load(), approx((T)-1) ); + + a.opaque_negate(); + BOOST_TEST_EQ( a.load(), approx((T)1) ); + } +} + +#endif // !defined(BOOST_ATOMIC_NO_FLOATING_POINT) + +template< template< typename > class Wrapper, typename T > +void test_floating_point_api(void) +{ + // Note: When support for floating point is disabled, even the base operation tests may fail because + // the generic template specialization does not account for garbage in padding bits that are present in some FP types. +#if !defined(BOOST_ATOMIC_NO_FLOATING_POINT) + test_base_operators< Wrapper, T >(static_cast<T>(42.1), static_cast<T>(43.2), static_cast<T>(44.3)); + + test_fp_additive_operators< Wrapper, T, T >(static_cast<T>(42.5), static_cast<T>(17.7)); + test_fp_additive_operators< Wrapper, T, T >(static_cast<T>(-42.5), static_cast<T>(-17.7)); + + test_fp_negation< Wrapper, T >(); +#endif +} + + +template< template< typename > class Wrapper, typename T > +void test_pointer_api(void) +{ + T values[3]; + + test_base_operators< Wrapper, T* >(&values[0], &values[1], &values[2]); + test_additive_operators< Wrapper, T*>(&values[1], 1); + + test_base_operators< Wrapper, void*>(&values[0], &values[1], &values[2]); + +#if defined(BOOST_HAS_INTPTR_T) + Wrapper<void*> wrapper_ptr; + typename Wrapper<void*>::atomic_type& ptr = wrapper_ptr.a; + Wrapper<boost::intptr_t> wrapper_integral; + typename Wrapper<boost::intptr_t>::atomic_type& integral = wrapper_integral.a; + BOOST_TEST_EQ( ptr.is_lock_free(), integral.is_lock_free() ); +#endif +} + +enum test_enum +{ + foo, bar, baz +}; + +template< template< typename > class Wrapper > +void test_enum_api(void) +{ + test_base_operators< Wrapper >(foo, bar, baz); +} + +template< typename T > +struct test_struct +{ + typedef T value_type; + value_type i; + inline bool operator==(test_struct const& c) const { return i == c.i; } + inline bool operator!=(test_struct const& c) const { return !operator==(c); } +}; + +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) +{ + test_stream << "{" << s.i << "}"; + return strm; +} + +template< template< typename > class Wrapper, typename T > +void test_struct_api(void) +{ + T a = {1}, b = {2}, c = {3}; + + test_base_operators< Wrapper >(a, b, c); + + { + Wrapper<T> wrapper_sa; + typename Wrapper<T>::atomic_type& sa = wrapper_sa.a; + Wrapper<typename T::value_type> wrapper_si; + typename Wrapper<typename T::value_type>::atomic_type& si = wrapper_si.a; + BOOST_TEST_EQ( sa.is_lock_free(), si.is_lock_free() ); + } +} + +template< typename T > +struct test_struct_x2 +{ + typedef T value_type; + value_type i, j; + inline bool operator==(test_struct_x2 const& c) const { return i == c.i && j == c.j; } + inline bool operator!=(test_struct_x2 const& c) const { return !operator==(c); } +}; + +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) +{ + test_stream << "{" << s.i << ", " << s.j << "}"; + return strm; +} + +template< template< typename > class Wrapper, typename T > +void test_struct_x2_api(void) +{ + T a = {1, 1}, b = {2, 2}, c = {3, 3}; + + test_base_operators< Wrapper >(a, b, c); +} + +struct large_struct +{ + unsigned char data[256u]; + + inline bool operator==(large_struct const& c) const + { + return std::memcmp(data, &c.data, sizeof(data)) == 0; + } + inline bool operator!=(large_struct const& 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; +} + +template< template< typename > class Wrapper > +void test_large_struct_api(void) +{ + large_struct a = {{1}}, b = {{2}}, c = {{3}}; + test_base_operators< Wrapper >(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==(test_struct_with_ctor const& c) const { return i == c.i; } + inline bool operator!=(test_struct_with_ctor const& c) const { return !operator==(c); } +}; + +template< typename Char, typename Traits > +inline std::basic_ostream< Char, Traits >& operator<< (std::basic_ostream< Char, Traits >& strm, test_struct_with_ctor const& s) +{ + strm << "{" << s.i << "}"; + return strm; +} + +template< template< typename > class Wrapper > +void test_struct_with_ctor_api(void) +{ + { + test_struct_with_ctor s; + Wrapper<test_struct_with_ctor> wrapper_sa; + typename Wrapper<test_struct_with_ctor>::atomic_type& sa = wrapper_sa.a; + // 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< Wrapper >(a, b, c); +} + +#endif diff --git a/src/boost/libs/atomic/test/atomicity.cpp b/src/boost/libs/atomic/test/atomicity.cpp new file mode 100644 index 000000000..457c97a29 --- /dev/null +++ b/src/boost/libs/atomic/test/atomicity.cpp @@ -0,0 +1,285 @@ +// 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) + +// Attempt to determine whether the operations on atomic variables +// do in fact behave atomically: Let multiple threads race modifying +// a shared atomic variable and verify that it behaves as expected. +// +// We assume that "observable race condition" events are exponentially +// distributed, with unknown "average time between observable races" +// (which is just the reciprocal of exp distribution parameter lambda). +// Use a non-atomic implementation that intentionally exhibits a +// (hopefully tight) race to compute the maximum-likelihood estimate +// for this time. From this, compute an estimate that covers the +// unknown value with 0.995 confidence (using chi square quantile). +// +// Use this estimate to pick a timeout for the race tests of the +// atomic implementations such that under the assumed distribution +// we get 0.995 probability to detect a race (if there is one). +// +// Overall this yields 0.995 * 0.995 > 0.99 confidence that the +// operations truly behave atomic if this test program does not +// report an error. + +#include <boost/atomic.hpp> + +#include <cstddef> +#include <algorithm> +#include <boost/config.hpp> +#include <boost/ref.hpp> +#include <boost/function.hpp> +#include <boost/bind/bind.hpp> +#include <boost/date_time/posix_time/posix_time_types.hpp> +#include <boost/thread/thread.hpp> +#include <boost/thread/thread_time.hpp> +#include <boost/thread/lock_guard.hpp> +#include <boost/thread/lock_types.hpp> +#include <boost/thread/mutex.hpp> +#include <boost/thread/condition_variable.hpp> +#include <boost/core/lightweight_test.hpp> + +/* helper class to let two instances of a function race against each +other, with configurable timeout and early abort on detection of error */ +class concurrent_runner +{ +public: + /* concurrently run the function in two threads, until either timeout + or one of the functions returns "false"; returns true if timeout + was reached, or false if early abort and updates timeout accordingly */ + static bool execute(const boost::function<bool(std::size_t)> & fn, boost::posix_time::time_duration & timeout) + { + concurrent_runner runner(fn); + runner.wait_finish(timeout); + return !runner.failure(); + } + + concurrent_runner(const boost::function<bool(std::size_t)> & fn) : + finished_(false), failure_(false) + { + boost::thread(boost::bind(&concurrent_runner::thread_function, this, fn, 0)).swap(first_thread_); + boost::thread(boost::bind(&concurrent_runner::thread_function, this, fn, 1)).swap(second_thread_); + } + + void wait_finish(boost::posix_time::time_duration & timeout) + { + boost::system_time start = boost::get_system_time(); + boost::system_time end = start + timeout; + + { + boost::unique_lock< boost::mutex > guard(m_); + while (boost::get_system_time() < end && !finished()) + c_.timed_wait(guard, end); + } + + finished_.store(true, boost::memory_order_relaxed); + + first_thread_.join(); + second_thread_.join(); + + boost::posix_time::time_duration duration = boost::get_system_time() - start; + if (duration < timeout) + timeout = duration; + } + + bool finished(void) const BOOST_NOEXCEPT_OR_NOTHROW + { + return finished_.load(boost::memory_order_relaxed); + } + + bool failure(void) const BOOST_NOEXCEPT_OR_NOTHROW + { + return failure_; + } + +private: + void thread_function(boost::function<bool(std::size_t)> function, std::size_t instance) + { + while (!finished()) + { + if (!function(instance)) + { + boost::lock_guard< boost::mutex > guard(m_); + failure_ = true; + finished_.store(true, boost::memory_order_relaxed); + c_.notify_all(); + break; + } + } + } + +private: + boost::mutex m_; + boost::condition_variable c_; + + boost::atomic<bool> finished_; + bool failure_; + + boost::thread first_thread_; + boost::thread second_thread_; +}; + +bool racy_add(volatile unsigned int & value, std::size_t instance) +{ + std::size_t shift = instance * 8; + unsigned int mask = 0xff << shift; + for (std::size_t n = 0; n < 255; ++n) + { + unsigned int tmp = value; + value = tmp + (1 << shift); + + if ((tmp & mask) != (n << shift)) + return false; + } + + unsigned int tmp = value; + value = tmp & ~mask; + if ((tmp & mask) != mask) + return false; + + return true; +} + +/* compute estimate for average time between races being observable, in usecs */ +double estimate_avg_race_time(void) +{ + double sum = 0.0; + + /* take 10 samples */ + for (std::size_t n = 0; n < 10; ++n) + { + boost::posix_time::time_duration timeout(0, 0, 10); + + volatile unsigned int value(0); + bool success = concurrent_runner::execute( + boost::bind(racy_add, boost::ref(value), boost::placeholders::_1), + timeout + ); + + if (success) + { + BOOST_ERROR("Failed to establish baseline time for reproducing race condition"); + } + + sum = sum + timeout.total_microseconds(); + } + + /* determine maximum likelihood estimate for average time between + race observations */ + double avg_race_time_mle = (sum / 10); + + /* pick 0.995 confidence (7.44 = chi square 0.995 confidence) */ + double avg_race_time_995 = avg_race_time_mle * 2 * 10 / 7.44; + + return avg_race_time_995; +} + +template<typename value_type, std::size_t shift_> +bool test_arithmetic(boost::atomic<value_type> & shared_value, std::size_t instance) +{ + std::size_t shift = instance * 8; + value_type mask = 0xff << shift; + value_type increment = 1 << shift; + + value_type expected = 0; + + for (std::size_t n = 0; n < 255; ++n) + { + value_type tmp = shared_value.fetch_add(increment, boost::memory_order_relaxed); + if ( (tmp & mask) != (expected << shift) ) + return false; + ++expected; + } + for (std::size_t n = 0; n < 255; ++n) + { + value_type tmp = shared_value.fetch_sub(increment, boost::memory_order_relaxed); + if ( (tmp & mask) != (expected << shift) ) + return false; + --expected; + } + + return true; +} + +template<typename value_type, std::size_t shift_> +bool test_bitops(boost::atomic<value_type> & shared_value, std::size_t instance) +{ + std::size_t shift = instance * 8; + value_type mask = 0xff << shift; + + value_type expected = 0; + + for (std::size_t k = 0; k < 8; ++k) + { + value_type mod = 1u << k; + value_type tmp = shared_value.fetch_or(mod << shift, boost::memory_order_relaxed); + if ( (tmp & mask) != (expected << shift)) + return false; + expected = expected | mod; + } + for (std::size_t k = 0; k < 8; ++k) + { + value_type tmp = shared_value.fetch_and(~(1u << (shift + k)), boost::memory_order_relaxed); + if ( (tmp & mask) != (expected << shift)) + return false; + expected = expected & ~(1u << k); + } + for (std::size_t k = 0; k < 8; ++k) + { + value_type mod = 255u ^ (1u << k); + value_type tmp = shared_value.fetch_xor(mod << shift, boost::memory_order_relaxed); + if ( (tmp & mask) != (expected << shift)) + return false; + expected = expected ^ mod; + } + + value_type tmp = shared_value.fetch_and(~mask, boost::memory_order_relaxed); + if ( (tmp & mask) != (expected << shift) ) + return false; + + return true; +} + +int main(int, char *[]) +{ + boost::posix_time::time_duration reciprocal_lambda; + + double avg_race_time = estimate_avg_race_time(); + + /* 5.298 = 0.995 quantile of exponential distribution */ + const boost::posix_time::time_duration timeout = boost::posix_time::microseconds((long)(5.298 * avg_race_time)); + + { + boost::atomic<unsigned int> value(0); + + /* testing two different operations in this loop, therefore + enlarge timeout */ + boost::posix_time::time_duration tmp(timeout * 2); + + bool success = concurrent_runner::execute( + boost::bind(test_arithmetic<unsigned int, 0>, boost::ref(value), boost::placeholders::_1), + tmp + ); + + BOOST_TEST(success); // concurrent arithmetic error + } + + { + boost::atomic<unsigned int> value(0); + + /* testing three different operations in this loop, therefore + enlarge timeout */ + boost::posix_time::time_duration tmp(timeout * 3); + + bool success = concurrent_runner::execute( + boost::bind(test_bitops<unsigned int, 0>, boost::ref(value), boost::placeholders::_1), + tmp + ); + + BOOST_TEST(success); // concurrent bit operations error + } + + return boost::report_errors(); +} diff --git a/src/boost/libs/atomic/test/atomicity_ref.cpp b/src/boost/libs/atomic/test/atomicity_ref.cpp new file mode 100644 index 000000000..5a7b013a9 --- /dev/null +++ b/src/boost/libs/atomic/test/atomicity_ref.cpp @@ -0,0 +1,293 @@ +// Copyright (c) 2020 Andrey Semashev +// +// 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) + +// This test is based on atomicity.cpp by Helge Bahmann. The test +// Was modified to use atomic_ref template instead of atomic. + +// Attempt to determine whether the operations on atomic variables +// do in fact behave atomically: Let multiple threads race modifying +// a shared atomic variable and verify that it behaves as expected. +// +// We assume that "observable race condition" events are exponentially +// distributed, with unknown "average time between observable races" +// (which is just the reciprocal of exp distribution parameter lambda). +// Use a non-atomic implementation that intentionally exhibits a +// (hopefully tight) race to compute the maximum-likelihood estimate +// for this time. From this, compute an estimate that covers the +// unknown value with 0.995 confidence (using chi square quantile). +// +// Use this estimate to pick a timeout for the race tests of the +// atomic implementations such that under the assumed distribution +// we get 0.995 probability to detect a race (if there is one). +// +// Overall this yields 0.995 * 0.995 > 0.99 confidence that the +// operations truly behave atomic if this test program does not +// report an error. + +#include <boost/memory_order.hpp> +#include <boost/atomic/atomic.hpp> +#include <boost/atomic/atomic_ref.hpp> + +#include <cstddef> +#include <algorithm> +#include <boost/config.hpp> +#include <boost/ref.hpp> +#include <boost/function.hpp> +#include <boost/bind/bind.hpp> +#include <boost/date_time/posix_time/posix_time_types.hpp> +#include <boost/thread/thread.hpp> +#include <boost/thread/thread_time.hpp> +#include <boost/thread/lock_guard.hpp> +#include <boost/thread/lock_types.hpp> +#include <boost/thread/mutex.hpp> +#include <boost/thread/condition_variable.hpp> +#include <boost/core/lightweight_test.hpp> + +/* helper class to let two instances of a function race against each +other, with configurable timeout and early abort on detection of error */ +class concurrent_runner +{ +public: + /* concurrently run the function in two threads, until either timeout + or one of the functions returns "false"; returns true if timeout + was reached, or false if early abort and updates timeout accordingly */ + static bool execute(const boost::function<bool(std::size_t)> & fn, boost::posix_time::time_duration & timeout) + { + concurrent_runner runner(fn); + runner.wait_finish(timeout); + return !runner.failure(); + } + + + concurrent_runner(const boost::function<bool(std::size_t)> & fn) : + finished_(false), failure_(false) + { + boost::thread(boost::bind(&concurrent_runner::thread_function, this, fn, 0)).swap(first_thread_); + boost::thread(boost::bind(&concurrent_runner::thread_function, this, fn, 1)).swap(second_thread_); + } + + void wait_finish(boost::posix_time::time_duration & timeout) + { + boost::system_time start = boost::get_system_time(); + boost::system_time end = start + timeout; + + { + boost::unique_lock< boost::mutex > guard(m_); + while (boost::get_system_time() < end && !finished()) + c_.timed_wait(guard, end); + } + + finished_.store(true, boost::memory_order_relaxed); + + first_thread_.join(); + second_thread_.join(); + + boost::posix_time::time_duration duration = boost::get_system_time() - start; + if (duration < timeout) + timeout = duration; + } + + bool finished(void) const BOOST_NOEXCEPT_OR_NOTHROW + { + return finished_.load(boost::memory_order_relaxed); + } + + bool failure(void) const BOOST_NOEXCEPT_OR_NOTHROW + { + return failure_; + } + +private: + void thread_function(boost::function<bool(std::size_t)> function, std::size_t instance) + { + while (!finished()) + { + if (!function(instance)) + { + boost::lock_guard< boost::mutex > guard(m_); + failure_ = true; + finished_.store(true, boost::memory_order_relaxed); + c_.notify_all(); + break; + } + } + } + +private: + boost::mutex m_; + boost::condition_variable c_; + + boost::atomic<bool> finished_; + bool failure_; + + boost::thread first_thread_; + boost::thread second_thread_; +}; + +bool racy_add(volatile unsigned int & value, std::size_t instance) +{ + std::size_t shift = instance * 8; + unsigned int mask = 0xff << shift; + for (std::size_t n = 0; n < 255; ++n) + { + unsigned int tmp = value; + value = tmp + (1 << shift); + + if ((tmp & mask) != (n << shift)) + return false; + } + + unsigned int tmp = value; + value = tmp & ~mask; + if ((tmp & mask) != mask) + return false; + + return true; +} + +/* compute estimate for average time between races being observable, in usecs */ +double estimate_avg_race_time(void) +{ + double sum = 0.0; + + /* take 10 samples */ + for (std::size_t n = 0; n < 10; n++) + { + boost::posix_time::time_duration timeout(0, 0, 10); + + volatile unsigned int value(0); + bool success = concurrent_runner::execute( + boost::bind(racy_add, boost::ref(value), boost::placeholders::_1), + timeout + ); + + if (success) + { + BOOST_ERROR("Failed to establish baseline time for reproducing race condition"); + } + + sum = sum + timeout.total_microseconds(); + } + + /* determine maximum likelihood estimate for average time between + race observations */ + double avg_race_time_mle = (sum / 10); + + /* pick 0.995 confidence (7.44 = chi square 0.995 confidence) */ + double avg_race_time_995 = avg_race_time_mle * 2 * 10 / 7.44; + + return avg_race_time_995; +} + +template<typename value_type, std::size_t shift_> +bool test_arithmetic(value_type& shared_value, std::size_t instance) +{ + std::size_t shift = instance * 8; + value_type mask = 0xff << shift; + value_type increment = 1 << shift; + + value_type expected = 0; + boost::atomic_ref<value_type> shared_value_ref(shared_value); + + for (std::size_t n = 0; n < 255; ++n) + { + value_type tmp = shared_value_ref.fetch_add(increment, boost::memory_order_relaxed); + if ( (tmp & mask) != (expected << shift) ) + return false; + ++expected; + } + for (std::size_t n = 0; n < 255; ++n) + { + value_type tmp = shared_value_ref.fetch_sub(increment, boost::memory_order_relaxed); + if ( (tmp & mask) != (expected << shift) ) + return false; + --expected; + } + + return true; +} + +template<typename value_type, std::size_t shift_> +bool test_bitops(value_type& shared_value, std::size_t instance) +{ + std::size_t shift = instance * 8; + value_type mask = 0xff << shift; + + value_type expected = 0; + boost::atomic_ref<value_type> shared_value_ref(shared_value); + + for (std::size_t k = 0; k < 8; ++k) + { + value_type mod = 1u << k; + value_type tmp = shared_value_ref.fetch_or(mod << shift, boost::memory_order_relaxed); + if ( (tmp & mask) != (expected << shift)) + return false; + expected = expected | mod; + } + for (std::size_t k = 0; k < 8; ++k) + { + value_type tmp = shared_value_ref.fetch_and(~(1u << (shift + k)), boost::memory_order_relaxed); + if ( (tmp & mask) != (expected << shift)) + return false; + expected = expected & ~(1u << k); + } + for (std::size_t k = 0; k < 8; ++k) + { + value_type mod = 255u ^ (1u << k); + value_type tmp = shared_value_ref.fetch_xor(mod << shift, boost::memory_order_relaxed); + if ( (tmp & mask) != (expected << shift)) + return false; + expected = expected ^ mod; + } + + value_type tmp = shared_value_ref.fetch_and(~mask, boost::memory_order_relaxed); + if ( (tmp & mask) != (expected << shift) ) + return false; + + return true; +} + +int main(int, char *[]) +{ + boost::posix_time::time_duration reciprocal_lambda; + + double avg_race_time = estimate_avg_race_time(); + + /* 5.298 = 0.995 quantile of exponential distribution */ + const boost::posix_time::time_duration timeout = boost::posix_time::microseconds((long)(5.298 * avg_race_time)); + + { + unsigned int value = 0; + + /* testing two different operations in this loop, therefore + enlarge timeout */ + boost::posix_time::time_duration tmp(timeout * 2); + + bool success = concurrent_runner::execute( + boost::bind(test_arithmetic<unsigned int, 0>, boost::ref(value), boost::placeholders::_1), + tmp + ); + + BOOST_TEST(success); // concurrent arithmetic error + } + + { + unsigned int value = 0; + + /* testing three different operations in this loop, therefore + enlarge timeout */ + boost::posix_time::time_duration tmp(timeout * 3); + + bool success = concurrent_runner::execute( + boost::bind(test_bitops<unsigned int, 0>, boost::ref(value), boost::placeholders::_1), + tmp + ); + + BOOST_TEST(success); // concurrent bit operations error + } + + return boost::report_errors(); +} diff --git a/src/boost/libs/atomic/test/c_implicit_ctor.cpp b/src/boost/libs/atomic/test/c_implicit_ctor.cpp new file mode 100644 index 000000000..1a68a7078 --- /dev/null +++ b/src/boost/libs/atomic/test/c_implicit_ctor.cpp @@ -0,0 +1,34 @@ +// Copyright (c) 2018 Andrey Semashev +// +// 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) + +// The test verifies that atomic<T> has an implicit conversion constructor from T. +// This can only be tested in C++17 because it has mandated copy elision. Previous C++ versions +// also require atomic<> to have a copy or move constructor, which it does not. +#if __cplusplus >= 201703L + +#include <boost/atomic.hpp> +#include <boost/static_assert.hpp> +#include <boost/config.hpp> +#include <type_traits> + +int main(int, char *[]) +{ + static_assert(std::is_convertible< int, boost::atomic< int > >::value, "boost::atomic<T> does not have an implicit constructor from T"); + + boost::atomic< short > a = 10; + (void)a; + + return 0; +} + +#else // __cplusplus >= 201703L + +int main(int, char *[]) +{ + return 0; +} + +#endif // __cplusplus >= 201703L diff --git a/src/boost/libs/atomic/test/cf_arith_func_ptr.cpp b/src/boost/libs/atomic/test/cf_arith_func_ptr.cpp new file mode 100644 index 000000000..dfb42f619 --- /dev/null +++ b/src/boost/libs/atomic/test/cf_arith_func_ptr.cpp @@ -0,0 +1,17 @@ +// Copyright (c) 2017 Andrey Semashev +// +// 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) + +#include <boost/atomic.hpp> + +int main(int, char *[]) +{ + // The test verifies that atomic<> does not provide arithmetic operations on function pointers + typedef void (*func_ptr)(int); + boost::atomic< func_ptr > a; + a.fetch_add(1); + + return 1; +} diff --git a/src/boost/libs/atomic/test/cf_arith_mem_ptr.cpp b/src/boost/libs/atomic/test/cf_arith_mem_ptr.cpp new file mode 100644 index 000000000..6ccf44aad --- /dev/null +++ b/src/boost/libs/atomic/test/cf_arith_mem_ptr.cpp @@ -0,0 +1,22 @@ +// Copyright (c) 2017 Andrey Semashev +// +// 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) + +#include <boost/atomic.hpp> + +struct foo +{ + int n; +}; + +int main(int, char *[]) +{ + // The test verifies that atomic<> does not provide arithmetic operations on member pointers + typedef int (foo::*mem_ptr); + boost::atomic< mem_ptr > a; + a.fetch_add(1); + + return 1; +} diff --git a/src/boost/libs/atomic/test/cf_arith_void_ptr.cpp b/src/boost/libs/atomic/test/cf_arith_void_ptr.cpp new file mode 100644 index 000000000..7c2d8fc25 --- /dev/null +++ b/src/boost/libs/atomic/test/cf_arith_void_ptr.cpp @@ -0,0 +1,16 @@ +// Copyright (c) 2017 Andrey Semashev +// +// 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) + +#include <boost/atomic.hpp> + +int main(int, char *[]) +{ + // The test verifies that atomic<> does not provide arithmetic operations on void pointers + boost::atomic< void* > a; + a.fetch_add(1); + + return 1; +} diff --git a/src/boost/libs/atomic/test/fallback_api.cpp b/src/boost/libs/atomic/test/fallback_api.cpp new file mode 100644 index 000000000..815f14ac7 --- /dev/null +++ b/src/boost/libs/atomic/test/fallback_api.cpp @@ -0,0 +1,77 @@ +// Copyright (c) 2011 Helge Bahmann +// Copyright (c) 2020 Andrey Semashev +// +// 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) + +/* force fallback implementation using locks */ +#define BOOST_ATOMIC_FORCE_FALLBACK 1 + +#include <boost/atomic.hpp> + +#include <boost/config.hpp> +#include <boost/cstdint.hpp> + +#include "api_test_helpers.hpp" + +int main(int, char *[]) +{ + test_flag_api(); + + test_integral_api< atomic_wrapper, char >(); + test_integral_api< atomic_wrapper, signed char >(); + test_integral_api< atomic_wrapper, unsigned char >(); + test_integral_api< atomic_wrapper, boost::uint8_t >(); + test_integral_api< atomic_wrapper, boost::int8_t >(); + test_integral_api< atomic_wrapper, short >(); + test_integral_api< atomic_wrapper, unsigned short >(); + test_integral_api< atomic_wrapper, boost::uint16_t >(); + test_integral_api< atomic_wrapper, boost::int16_t >(); + test_integral_api< atomic_wrapper, int >(); + test_integral_api< atomic_wrapper, unsigned int >(); + test_integral_api< atomic_wrapper, boost::uint32_t >(); + test_integral_api< atomic_wrapper, boost::int32_t >(); + test_integral_api< atomic_wrapper, long >(); + test_integral_api< atomic_wrapper, unsigned long >(); + test_integral_api< atomic_wrapper, boost::uint64_t >(); + test_integral_api< atomic_wrapper, boost::int64_t >(); + test_integral_api< atomic_wrapper, long long >(); + test_integral_api< atomic_wrapper, unsigned long long >(); +#if defined(BOOST_HAS_INT128) && !defined(BOOST_ATOMIC_TESTS_NO_INT128) + test_integral_api< atomic_wrapper, boost::int128_type >(); + test_integral_api< atomic_wrapper, boost::uint128_type >(); +#endif + +#if !defined(BOOST_ATOMIC_NO_FLOATING_POINT) + test_floating_point_api< atomic_wrapper, float >(); + test_floating_point_api< atomic_wrapper, double >(); + test_floating_point_api< atomic_wrapper, long double >(); +#if defined(BOOST_HAS_FLOAT128) && !defined(BOOST_ATOMIC_TESTS_NO_FLOAT128) + test_floating_point_api< atomic_wrapper, boost::float128_type >(); +#endif +#endif + + test_pointer_api< atomic_wrapper, int >(); + + test_enum_api< atomic_wrapper >(); + + test_struct_api< atomic_wrapper, test_struct< boost::uint8_t > >(); + test_struct_api< atomic_wrapper, test_struct< boost::uint16_t > >(); + test_struct_api< atomic_wrapper, test_struct< boost::uint32_t > >(); + test_struct_api< atomic_wrapper, test_struct< boost::uint64_t > >(); + + // https://svn.boost.org/trac/boost/ticket/10994 + test_struct_x2_api< atomic_wrapper, test_struct_x2< boost::uint64_t > >(); + + // https://svn.boost.org/trac/boost/ticket/9985 + test_struct_api< atomic_wrapper, test_struct< double > >(); + + test_large_struct_api< atomic_wrapper >(); + + // Test that boost::atomic<T> only requires T to be trivially copyable. + // Other non-trivial constructors are allowed. + test_struct_with_ctor_api< atomic_wrapper >(); + + return boost::report_errors(); +} diff --git a/src/boost/libs/atomic/test/fallback_ref_api.cpp b/src/boost/libs/atomic/test/fallback_ref_api.cpp new file mode 100644 index 000000000..b000eba55 --- /dev/null +++ b/src/boost/libs/atomic/test/fallback_ref_api.cpp @@ -0,0 +1,74 @@ +// Copyright (c) 2020 Andrey Semashev +// +// 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) + +/* force fallback implementation using locks */ +#define BOOST_ATOMIC_FORCE_FALLBACK 1 + +#include <boost/atomic/atomic_ref.hpp> + +#include <boost/config.hpp> +#include <boost/cstdint.hpp> + +#include "api_test_helpers.hpp" + +int main(int, char *[]) +{ + test_integral_api< atomic_ref_wrapper, char >(); + test_integral_api< atomic_ref_wrapper, signed char >(); + test_integral_api< atomic_ref_wrapper, unsigned char >(); + test_integral_api< atomic_ref_wrapper, boost::uint8_t >(); + test_integral_api< atomic_ref_wrapper, boost::int8_t >(); + test_integral_api< atomic_ref_wrapper, short >(); + test_integral_api< atomic_ref_wrapper, unsigned short >(); + test_integral_api< atomic_ref_wrapper, boost::uint16_t >(); + test_integral_api< atomic_ref_wrapper, boost::int16_t >(); + test_integral_api< atomic_ref_wrapper, int >(); + test_integral_api< atomic_ref_wrapper, unsigned int >(); + test_integral_api< atomic_ref_wrapper, boost::uint32_t >(); + test_integral_api< atomic_ref_wrapper, boost::int32_t >(); + test_integral_api< atomic_ref_wrapper, long >(); + test_integral_api< atomic_ref_wrapper, unsigned long >(); + test_integral_api< atomic_ref_wrapper, boost::uint64_t >(); + test_integral_api< atomic_ref_wrapper, boost::int64_t >(); + test_integral_api< atomic_ref_wrapper, long long >(); + test_integral_api< atomic_ref_wrapper, unsigned long long >(); +#if defined(BOOST_HAS_INT128) && !defined(BOOST_ATOMIC_TESTS_NO_INT128) + test_integral_api< atomic_ref_wrapper, boost::int128_type >(); + test_integral_api< atomic_ref_wrapper, boost::uint128_type >(); +#endif + +#if !defined(BOOST_ATOMIC_NO_FLOATING_POINT) + test_floating_point_api< atomic_ref_wrapper, float >(); + test_floating_point_api< atomic_ref_wrapper, double >(); + test_floating_point_api< atomic_ref_wrapper, long double >(); +#if defined(BOOST_HAS_FLOAT128) && !defined(BOOST_ATOMIC_TESTS_NO_FLOAT128) + test_floating_point_api< atomic_ref_wrapper, boost::float128_type >(); +#endif +#endif + + test_pointer_api< atomic_ref_wrapper, int >(); + + test_enum_api< atomic_ref_wrapper >(); + + test_struct_api< atomic_ref_wrapper, test_struct< boost::uint8_t > >(); + test_struct_api< atomic_ref_wrapper, test_struct< boost::uint16_t > >(); + test_struct_api< atomic_ref_wrapper, test_struct< boost::uint32_t > >(); + test_struct_api< atomic_ref_wrapper, test_struct< boost::uint64_t > >(); + + // https://svn.boost.org/trac/boost/ticket/10994 + test_struct_x2_api< atomic_ref_wrapper, test_struct_x2< boost::uint64_t > >(); + + // https://svn.boost.org/trac/boost/ticket/9985 + test_struct_api< atomic_ref_wrapper, test_struct< double > >(); + + test_large_struct_api< atomic_ref_wrapper >(); + + // Test that boost::atomic_ref<T> only requires T to be trivially copyable. + // Other non-trivial constructors are allowed. + test_struct_with_ctor_api< atomic_ref_wrapper >(); + + return boost::report_errors(); +} diff --git a/src/boost/libs/atomic/test/lockfree.cpp b/src/boost/libs/atomic/test/lockfree.cpp new file mode 100644 index 000000000..23557d71e --- /dev/null +++ b/src/boost/libs/atomic/test/lockfree.cpp @@ -0,0 +1,237 @@ +// Copyright (c) 2011 Helge Bahmann +// Copyright (c) 2020 Andrey Semashev +// +// 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) + +// Verify that definition of the "LOCK_FREE" macros and the +// "is_lock_free" members is consistent and matches expectations. +// Also, if any operation is lock-free, then the platform +// implementation must provide overridden fence implementations. + +#include <boost/atomic.hpp> + +#include <iostream> +#include <boost/config.hpp> +#include <boost/core/lightweight_test.hpp> +#include "aligned_object.hpp" + +static const char* const lock_free_level[] = +{ + "never", + "sometimes", + "always" +}; + +template< typename T > +void verify_lock_free(const char* type_name, int lock_free_macro_val, int lock_free_expect) +{ + BOOST_TEST(lock_free_macro_val >= 0 && lock_free_macro_val <= 2); + BOOST_TEST(lock_free_macro_val == lock_free_expect); + + boost::atomic<T> value; + + if (lock_free_macro_val == 0) + BOOST_TEST(!value.is_lock_free()); + if (lock_free_macro_val == 2) + BOOST_TEST(value.is_lock_free()); + + BOOST_TEST_EQ(boost::atomic<T>::is_always_lock_free, (lock_free_expect == 2)); + + std::cout << "atomic<" << type_name << "> is " << lock_free_level[lock_free_macro_val] << " lock free\n"; + + // atomic<T> may use larger storage than sizeof(T) to achieve lock-free property. In this case atomic_ref<T> may not be lock-free. + if (sizeof(boost::atomic<T>) == sizeof(T)) + { + aligned_object<T, boost::atomic_ref<T>::required_alignment> object; + boost::atomic_ref<T> ref(object.get()); + + BOOST_TEST_EQ(ref.is_lock_free(), value.is_lock_free()); + BOOST_TEST_EQ(boost::atomic_ref<T>::is_always_lock_free, boost::atomic<T>::is_always_lock_free); + } +} + +#if (defined(__GNUC__) || defined(__SUNPRO_CC)) && defined(__i386__) + +#define EXPECT_CHAR_LOCK_FREE 2 +#define EXPECT_SHORT_LOCK_FREE 2 +#define EXPECT_INT_LOCK_FREE 2 +#define EXPECT_LONG_LOCK_FREE 2 +#if defined(BOOST_ATOMIC_DETAIL_X86_HAS_CMPXCHG8B) +#define EXPECT_LLONG_LOCK_FREE 2 +#else +#define EXPECT_LLONG_LOCK_FREE 0 +#endif +#define EXPECT_INT128_LOCK_FREE 0 +#define EXPECT_POINTER_LOCK_FREE 2 +#define EXPECT_BOOL_LOCK_FREE 2 + +#elif (defined(__GNUC__) || defined(__SUNPRO_CC)) && defined(__x86_64__) + +#define EXPECT_CHAR_LOCK_FREE 2 +#define EXPECT_SHORT_LOCK_FREE 2 +#define EXPECT_INT_LOCK_FREE 2 +#define EXPECT_LONG_LOCK_FREE 2 +#define EXPECT_LLONG_LOCK_FREE 2 +#if defined(BOOST_ATOMIC_DETAIL_X86_HAS_CMPXCHG16B) +#define EXPECT_INT128_LOCK_FREE 2 +#else +#define EXPECT_INT128_LOCK_FREE 0 +#endif +#define EXPECT_POINTER_LOCK_FREE 2 +#define EXPECT_BOOL_LOCK_FREE 2 + +#elif defined(__GNUC__) && (defined(__POWERPC__) || defined(__PPC__)) + +#define EXPECT_CHAR_LOCK_FREE 2 +#define EXPECT_CHAR16_T_LOCK_FREE 2 +#define EXPECT_CHAR32_T_LOCK_FREE 2 +#define EXPECT_WCHAR_T_LOCK_FREE 2 +#define EXPECT_SHORT_LOCK_FREE 2 +#define EXPECT_INT_LOCK_FREE 2 +#define EXPECT_LONG_LOCK_FREE 2 +#if defined(__powerpc64__) +#define EXPECT_LLONG_LOCK_FREE 2 +#else +#define EXPECT_LLONG_LOCK_FREE 0 +#endif +#define EXPECT_INT128_LOCK_FREE 0 +#define EXPECT_POINTER_LOCK_FREE 2 +#define EXPECT_BOOL_LOCK_FREE 2 + +#elif defined(__GNUC__) && defined(__alpha__) + +#define EXPECT_CHAR_LOCK_FREE 2 +#define EXPECT_CHAR16_T_LOCK_FREE 2 +#define EXPECT_CHAR32_T_LOCK_FREE 2 +#define EXPECT_WCHAR_T_LOCK_FREE 2 +#define EXPECT_SHORT_LOCK_FREE 2 +#define EXPECT_INT_LOCK_FREE 2 +#define EXPECT_LONG_LOCK_FREE 2 +#define EXPECT_LLONG_LOCK_FREE 2 +#define EXPECT_INT128_LOCK_FREE 0 +#define EXPECT_POINTER_LOCK_FREE 2 +#define EXPECT_BOOL_LOCK_FREE 2 + +#elif defined(__GNUC__) &&\ + (\ + defined(__ARM_ARCH_6__) || defined(__ARM_ARCH_6J__) ||\ + defined(__ARM_ARCH_6K__) || defined(__ARM_ARCH_6Z__) ||\ + defined(__ARM_ARCH_6ZK__) ||\ + defined(__ARM_ARCH_7__) || defined(__ARM_ARCH_7A__) ||\ + defined(__ARM_ARCH_7R__) || defined(__ARM_ARCH_7M__) ||\ + defined(__ARM_ARCH_7EM__) || defined(__ARM_ARCH_7S__)\ + ) + +#define EXPECT_CHAR_LOCK_FREE 2 +#define EXPECT_SHORT_LOCK_FREE 2 +#define EXPECT_INT_LOCK_FREE 2 +#define EXPECT_LONG_LOCK_FREE 2 +#if !(defined(__ARM_ARCH_6__) || defined(__ARM_ARCH_6J__) || defined(__ARM_ARCH_6Z__)\ + || ((defined(__ARM_ARCH_6K__) || defined(__ARM_ARCH_6ZK__)) && defined(__thumb__)) || defined(__ARM_ARCH_7M__)) +#define EXPECT_LLONG_LOCK_FREE 2 +#else +#define EXPECT_LLONG_LOCK_FREE 0 +#endif +#define EXPECT_INT128_LOCK_FREE 0 +#define EXPECT_POINTER_LOCK_FREE 2 +#define EXPECT_BOOL_LOCK_FREE 2 + +#elif defined(__linux__) && defined(__arm__) + +#define EXPECT_CHAR_LOCK_FREE 2 +#define EXPECT_SHORT_LOCK_FREE 2 +#define EXPECT_INT_LOCK_FREE 2 +#define EXPECT_LONG_LOCK_FREE 2 +#define EXPECT_LLONG_LOCK_FREE 0 +#define EXPECT_INT128_LOCK_FREE 0 +#define EXPECT_POINTER_LOCK_FREE 2 +#define EXPECT_BOOL_LOCK_FREE 2 + +#elif (defined(__GNUC__) || defined(__SUNPRO_CC)) && (defined(__sparcv8plus) || defined(__sparc_v9__)) + +#define EXPECT_CHAR_LOCK_FREE 2 +#define EXPECT_SHORT_LOCK_FREE 2 +#define EXPECT_INT_LOCK_FREE 2 +#define EXPECT_LONG_LOCK_FREE 2 +#define EXPECT_LLONG_LOCK_FREE 2 +#define EXPECT_INT128_LOCK_FREE 0 +#define EXPECT_POINTER_LOCK_FREE 2 +#define EXPECT_BOOL_LOCK_FREE 2 + +#elif defined(BOOST_USE_WINDOWS_H) || defined(_WIN32_CE) || defined(BOOST_MSVC) || defined(BOOST_INTEL_WIN) || defined(WIN32) || defined(_WIN32) || defined(__WIN32__) || defined(__CYGWIN__) + +#define EXPECT_CHAR_LOCK_FREE 2 +#define EXPECT_SHORT_LOCK_FREE 2 +#define EXPECT_INT_LOCK_FREE 2 +#define EXPECT_LONG_LOCK_FREE 2 +#if defined(_WIN64) || defined(BOOST_ATOMIC_DETAIL_X86_HAS_CMPXCHG8B) || defined(_M_AMD64) || defined(_M_IA64) || (_MSC_VER >= 1700 && (defined(_M_ARM) || defined(_M_ARM64))) +#define EXPECT_LLONG_LOCK_FREE 2 +#else +#define EXPECT_LLONG_LOCK_FREE 0 +#endif +#define EXPECT_INT128_LOCK_FREE 0 +#define EXPECT_POINTER_LOCK_FREE 2 +#define EXPECT_BOOL_LOCK_FREE 2 + +#else + +#define EXPECT_CHAR_LOCK_FREE 0 +#define EXPECT_SHORT_LOCK_FREE 0 +#define EXPECT_INT_LOCK_FREE 0 +#define EXPECT_LONG_LOCK_FREE 0 +#define EXPECT_LLONG_LOCK_FREE 0 +#define EXPECT_INT128_LOCK_FREE 0 +#define EXPECT_POINTER_LOCK_FREE 0 +#define EXPECT_BOOL_LOCK_FREE 0 + +#endif + +int main(int, char *[]) +{ + verify_lock_free<char>("char", BOOST_ATOMIC_CHAR_LOCK_FREE, EXPECT_CHAR_LOCK_FREE); + verify_lock_free<short>("short", BOOST_ATOMIC_SHORT_LOCK_FREE, EXPECT_SHORT_LOCK_FREE); + verify_lock_free<int>("int", BOOST_ATOMIC_INT_LOCK_FREE, EXPECT_INT_LOCK_FREE); + verify_lock_free<long>("long", BOOST_ATOMIC_LONG_LOCK_FREE, EXPECT_LONG_LOCK_FREE); +#ifdef BOOST_HAS_LONG_LONG + verify_lock_free<long long>("long long", BOOST_ATOMIC_LLONG_LOCK_FREE, EXPECT_LLONG_LOCK_FREE); +#endif +#ifdef BOOST_HAS_INT128 + verify_lock_free<boost::int128_type>("int128", BOOST_ATOMIC_INT128_LOCK_FREE, EXPECT_INT128_LOCK_FREE); +#endif + verify_lock_free<void *>("void *", BOOST_ATOMIC_POINTER_LOCK_FREE, EXPECT_SHORT_LOCK_FREE); + verify_lock_free<bool>("bool", BOOST_ATOMIC_BOOL_LOCK_FREE, EXPECT_BOOL_LOCK_FREE); + +#ifndef BOOST_ATOMIC_NO_FLOATING_POINT + + verify_lock_free<float>("float", BOOST_ATOMIC_FLOAT_LOCK_FREE, + sizeof(float) == 1 ? EXPECT_CHAR_LOCK_FREE : (sizeof(float) == 2 ? EXPECT_SHORT_LOCK_FREE : + (sizeof(float) <= 4 ? EXPECT_INT_LOCK_FREE : (sizeof(float) <= 8 ? EXPECT_LLONG_LOCK_FREE : (sizeof(float) <= 16 ? EXPECT_INT128_LOCK_FREE : 0))))); + + verify_lock_free<double>("double", BOOST_ATOMIC_DOUBLE_LOCK_FREE, + sizeof(double) == 1 ? EXPECT_CHAR_LOCK_FREE : (sizeof(double) == 2 ? EXPECT_SHORT_LOCK_FREE : + (sizeof(double) <= 4 ? EXPECT_INT_LOCK_FREE : (sizeof(double) <= 8 ? EXPECT_LLONG_LOCK_FREE : (sizeof(double) <= 16 ? EXPECT_INT128_LOCK_FREE : 0))))); + + verify_lock_free<long double>("long double", BOOST_ATOMIC_LONG_DOUBLE_LOCK_FREE, + sizeof(long double) == 1 ? EXPECT_CHAR_LOCK_FREE : (sizeof(long double) == 2 ? EXPECT_SHORT_LOCK_FREE : + (sizeof(long double) <= 4 ? EXPECT_INT_LOCK_FREE : (sizeof(long double) <= 8 ? EXPECT_LLONG_LOCK_FREE : (sizeof(long double) <= 16 ? EXPECT_INT128_LOCK_FREE : 0))))); + +#if defined(BOOST_HAS_INT128) && defined(BOOST_HAS_FLOAT128) + verify_lock_free<boost::float128_type>("float128", BOOST_ATOMIC_INT128_LOCK_FREE, EXPECT_INT128_LOCK_FREE); +#endif + +#endif // BOOST_ATOMIC_NO_FLOATING_POINT + + bool any_lock_free = + BOOST_ATOMIC_CHAR_LOCK_FREE > 0 || + BOOST_ATOMIC_SHORT_LOCK_FREE > 0 || + BOOST_ATOMIC_INT_LOCK_FREE > 0 || + BOOST_ATOMIC_LONG_LOCK_FREE > 0 || + BOOST_ATOMIC_LLONG_LOCK_FREE > 0 || + BOOST_ATOMIC_BOOL_LOCK_FREE > 0; + + BOOST_TEST(!any_lock_free || BOOST_ATOMIC_THREAD_FENCE > 0); + + return boost::report_errors(); +} diff --git a/src/boost/libs/atomic/test/native_api.cpp b/src/boost/libs/atomic/test/native_api.cpp new file mode 100644 index 000000000..4704e55c9 --- /dev/null +++ b/src/boost/libs/atomic/test/native_api.cpp @@ -0,0 +1,83 @@ +// Copyright (c) 2011 Helge Bahmann +// Copyright (c) 2020 Andrey Semashev +// +// 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) + +#include <boost/atomic.hpp> + +#include <boost/config.hpp> +#include <boost/cstdint.hpp> + +#include "api_test_helpers.hpp" + +int main(int, char *[]) +{ + test_flag_api(); + + test_integral_api< atomic_wrapper, char >(); + test_integral_api< atomic_wrapper, signed char >(); + test_integral_api< atomic_wrapper, unsigned char >(); + test_integral_api< atomic_wrapper, boost::uint8_t >(); + test_integral_api< atomic_wrapper, boost::int8_t >(); + test_integral_api< atomic_wrapper, short >(); + test_integral_api< atomic_wrapper, unsigned short >(); + test_integral_api< atomic_wrapper, boost::uint16_t >(); + test_integral_api< atomic_wrapper, boost::int16_t >(); + test_integral_api< atomic_wrapper, int >(); + test_integral_api< atomic_wrapper, unsigned int >(); + test_integral_api< atomic_wrapper, boost::uint32_t >(); + test_integral_api< atomic_wrapper, boost::int32_t >(); + test_integral_api< atomic_wrapper, long >(); + test_integral_api< atomic_wrapper, unsigned long >(); + test_integral_api< atomic_wrapper, boost::uint64_t >(); + test_integral_api< atomic_wrapper, boost::int64_t >(); + test_integral_api< atomic_wrapper, long long >(); + test_integral_api< atomic_wrapper, unsigned long long >(); +#if defined(BOOST_HAS_INT128) && !defined(BOOST_ATOMIC_TESTS_NO_INT128) + test_integral_api< atomic_wrapper, boost::int128_type >(); + test_integral_api< atomic_wrapper, boost::uint128_type >(); +#endif + + test_constexpr_ctor< char >(); + test_constexpr_ctor< short >(); + test_constexpr_ctor< int >(); + test_constexpr_ctor< long >(); + // test_constexpr_ctor< int* >(); // for pointers we're not offering a constexpr constructor because of bitwise_cast + +#if !defined(BOOST_ATOMIC_NO_FLOATING_POINT) + test_floating_point_api< atomic_wrapper, float >(); + test_floating_point_api< atomic_wrapper, double >(); + test_floating_point_api< atomic_wrapper, long double >(); +#if defined(BOOST_HAS_FLOAT128) && !defined(BOOST_ATOMIC_TESTS_NO_FLOAT128) + test_floating_point_api< atomic_wrapper, boost::float128_type >(); +#endif +#endif + + test_pointer_api< atomic_wrapper, int >(); + + test_enum_api< atomic_wrapper >(); + + test_struct_api< atomic_wrapper, test_struct< boost::uint8_t > >(); + test_struct_api< atomic_wrapper, test_struct< boost::uint16_t > >(); + test_struct_api< atomic_wrapper, test_struct< boost::uint32_t > >(); + test_struct_api< atomic_wrapper, test_struct< boost::uint64_t > >(); +#if defined(BOOST_HAS_INT128) + test_struct_api< atomic_wrapper, test_struct< boost::uint128_type > >(); +#endif + + // https://svn.boost.org/trac/boost/ticket/10994 + test_struct_x2_api< atomic_wrapper, test_struct_x2< boost::uint64_t > >(); + + // https://svn.boost.org/trac/boost/ticket/9985 + test_struct_api< atomic_wrapper, test_struct< double > >(); + + test_large_struct_api< atomic_wrapper >(); + + // Test that boost::atomic<T> only requires T to be trivially copyable. + // Other non-trivial constructors are allowed. + test_struct_with_ctor_api< atomic_wrapper >(); + + return boost::report_errors(); +} diff --git a/src/boost/libs/atomic/test/native_ref_api.cpp b/src/boost/libs/atomic/test/native_ref_api.cpp new file mode 100644 index 000000000..e29ace670 --- /dev/null +++ b/src/boost/libs/atomic/test/native_ref_api.cpp @@ -0,0 +1,74 @@ +// Copyright (c) 2020 Andrey Semashev +// +// 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) + +#include <boost/atomic/atomic_ref.hpp> + +#include <boost/config.hpp> +#include <boost/cstdint.hpp> + +#include "api_test_helpers.hpp" + +int main(int, char *[]) +{ + test_integral_api< atomic_ref_wrapper, char >(); + test_integral_api< atomic_ref_wrapper, signed char >(); + test_integral_api< atomic_ref_wrapper, unsigned char >(); + test_integral_api< atomic_ref_wrapper, boost::uint8_t >(); + test_integral_api< atomic_ref_wrapper, boost::int8_t >(); + test_integral_api< atomic_ref_wrapper, short >(); + test_integral_api< atomic_ref_wrapper, unsigned short >(); + test_integral_api< atomic_ref_wrapper, boost::uint16_t >(); + test_integral_api< atomic_ref_wrapper, boost::int16_t >(); + test_integral_api< atomic_ref_wrapper, int >(); + test_integral_api< atomic_ref_wrapper, unsigned int >(); + test_integral_api< atomic_ref_wrapper, boost::uint32_t >(); + test_integral_api< atomic_ref_wrapper, boost::int32_t >(); + test_integral_api< atomic_ref_wrapper, long >(); + test_integral_api< atomic_ref_wrapper, unsigned long >(); + test_integral_api< atomic_ref_wrapper, boost::uint64_t >(); + test_integral_api< atomic_ref_wrapper, boost::int64_t >(); + test_integral_api< atomic_ref_wrapper, long long >(); + test_integral_api< atomic_ref_wrapper, unsigned long long >(); +#if defined(BOOST_HAS_INT128) && !defined(BOOST_ATOMIC_TESTS_NO_INT128) + test_integral_api< atomic_ref_wrapper, boost::int128_type >(); + test_integral_api< atomic_ref_wrapper, boost::uint128_type >(); +#endif + +#if !defined(BOOST_ATOMIC_NO_FLOATING_POINT) + test_floating_point_api< atomic_ref_wrapper, float >(); + test_floating_point_api< atomic_ref_wrapper, double >(); + test_floating_point_api< atomic_ref_wrapper, long double >(); +#if defined(BOOST_HAS_FLOAT128) && !defined(BOOST_ATOMIC_TESTS_NO_FLOAT128) + test_floating_point_api< atomic_ref_wrapper, boost::float128_type >(); +#endif +#endif + + test_pointer_api< atomic_ref_wrapper, int >(); + + test_enum_api< atomic_ref_wrapper >(); + + test_struct_api< atomic_ref_wrapper, test_struct< boost::uint8_t > >(); + test_struct_api< atomic_ref_wrapper, test_struct< boost::uint16_t > >(); + test_struct_api< atomic_ref_wrapper, test_struct< boost::uint32_t > >(); + test_struct_api< atomic_ref_wrapper, test_struct< boost::uint64_t > >(); +#if defined(BOOST_HAS_INT128) + test_struct_api< atomic_ref_wrapper, test_struct< boost::uint128_type > >(); +#endif + + // https://svn.boost.org/trac/boost/ticket/10994 + test_struct_x2_api< atomic_ref_wrapper, test_struct_x2< boost::uint64_t > >(); + + // https://svn.boost.org/trac/boost/ticket/9985 + test_struct_api< atomic_ref_wrapper, test_struct< double > >(); + + test_large_struct_api< atomic_ref_wrapper >(); + + // Test that boost::atomic_ref<T> only requires T to be trivially copyable. + // Other non-trivial constructors are allowed. + test_struct_with_ctor_api< atomic_ref_wrapper >(); + + return boost::report_errors(); +} diff --git a/src/boost/libs/atomic/test/ordering.cpp b/src/boost/libs/atomic/test/ordering.cpp new file mode 100644 index 000000000..a7416cdeb --- /dev/null +++ b/src/boost/libs/atomic/test/ordering.cpp @@ -0,0 +1,270 @@ +// Copyright (c) 2011 Helge Bahmann +// Copyright (c) 2012 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) + +// Attempt to determine whether the memory ordering/ fence operations +// work as expected: +// Let two threads race accessing multiple shared variables and +// verify that "observable" order of operations matches with the +// ordering constraints specified. +// +// We assume that "memory ordering violation" events are exponentially +// distributed, with unknown "average time between violations" +// (which is just the reciprocal of exp distribution parameter lambda). +// Use a "relaxed ordering" implementation that intentionally exhibits +// a (hopefully observable) such violation to compute the maximum-likelihood +// estimate for this time. From this, compute an estimate that covers the +// unknown value with 0.995 confidence (using chi square quantile). +// +// Use this estimate to pick a timeout for the race tests of the +// atomic implementations such that under the assumed distribution +// we get 0.995 probability to detect a race (if there is one). +// +// Overall this yields 0.995 * 0.995 > 0.99 confidence that the +// fences work as expected if this test program does not +// report an error. + +#include <boost/atomic.hpp> + +#include <cstddef> +#include <boost/bind/bind.hpp> +#include <boost/date_time/posix_time/posix_time_types.hpp> +#include <boost/thread/thread.hpp> +#include <boost/thread/thread_time.hpp> +#include <boost/thread/lock_guard.hpp> +#include <boost/thread/lock_types.hpp> +#include <boost/thread/mutex.hpp> +#include <boost/thread/condition_variable.hpp> +#include <boost/thread/barrier.hpp> +#include <boost/core/lightweight_test.hpp> + +// Two threads perform the following operations: +// +// thread # 1 thread # 2 +// store(a, 1) store(b, 1) +// x = read(b) y = read(a) +// +// Under relaxed memory ordering, the case (x, y) == (0, 0) is +// possible. Under sequential consistency, this case is impossible. +// +// This "problem" is reproducible on all platforms, even x86. +template<boost::memory_order store_order, boost::memory_order load_order> +class total_store_order_test +{ +public: + total_store_order_test(void); + + void run(boost::posix_time::time_duration & timeout); + bool detected_conflict(void) const { return detected_conflict_; } + +private: + void thread1fn(void); + void thread2fn(void); + void check_conflict(void); + +private: + boost::atomic<int> a_; + /* insert a bit of padding to push the two variables into + different cache lines and increase the likelihood of detecting + a conflict */ + char pad1_[512]; + boost::atomic<int> b_; + + char pad2_[512]; + boost::barrier barrier_; + + int vrfyb1_, vrfya2_; + + boost::atomic<bool> terminate_threads_; + boost::atomic<int> termination_consensus_; + + bool detected_conflict_; + boost::mutex m_; + boost::condition_variable c_; +}; + +template<boost::memory_order store_order, boost::memory_order load_order> +total_store_order_test<store_order, load_order>::total_store_order_test(void) : + a_(0), b_(0), barrier_(2), + vrfyb1_(0), vrfya2_(0), + terminate_threads_(false), termination_consensus_(0), + detected_conflict_(false) +{ +} + +template<boost::memory_order store_order, boost::memory_order load_order> +void total_store_order_test<store_order, load_order>::run(boost::posix_time::time_duration & timeout) +{ + boost::system_time start = boost::get_system_time(); + boost::system_time end = start + timeout; + + boost::thread t1(boost::bind(&total_store_order_test::thread1fn, this)); + boost::thread t2(boost::bind(&total_store_order_test::thread2fn, this)); + + { + boost::unique_lock< boost::mutex > guard(m_); + while (boost::get_system_time() < end && !detected_conflict_) + c_.timed_wait(guard, end); + } + + terminate_threads_.store(true, boost::memory_order_relaxed); + + t2.join(); + t1.join(); + + boost::posix_time::time_duration duration = boost::get_system_time() - start; + if (duration < timeout) + timeout = duration; +} + +volatile int backoff_dummy; + +template<boost::memory_order store_order, boost::memory_order load_order> +void total_store_order_test<store_order, load_order>::thread1fn(void) +{ + while (true) + { + a_.store(1, store_order); + int b = b_.load(load_order); + + barrier_.wait(); + + vrfyb1_ = b; + + barrier_.wait(); + + check_conflict(); + + /* both threads synchronize via barriers, so either + both threads must exit here, or they must both do + another round, otherwise one of them will wait forever */ + if (terminate_threads_.load(boost::memory_order_relaxed)) + { + while (true) + { + int tmp = termination_consensus_.fetch_or(1, boost::memory_order_relaxed); + + if (tmp == 3) + return; + if (tmp & 4) + break; + } + } + + termination_consensus_.fetch_xor(4, boost::memory_order_relaxed); + + unsigned int delay = rand() % 10000; + a_.store(0, boost::memory_order_relaxed); + + barrier_.wait(); + + while (delay--) + backoff_dummy = delay; + } +} + +template<boost::memory_order store_order, boost::memory_order load_order> +void total_store_order_test<store_order, load_order>::thread2fn(void) +{ + while (true) + { + b_.store(1, store_order); + int a = a_.load(load_order); + + barrier_.wait(); + + vrfya2_ = a; + + barrier_.wait(); + + check_conflict(); + + /* both threads synchronize via barriers, so either + both threads must exit here, or they must both do + another round, otherwise one of them will wait forever */ + if (terminate_threads_.load(boost::memory_order_relaxed)) + { + while (true) + { + int tmp = termination_consensus_.fetch_or(2, boost::memory_order_relaxed); + + if (tmp == 3) + return; + if (tmp & 4) + break; + } + } + + termination_consensus_.fetch_xor(4, boost::memory_order_relaxed); + + unsigned int delay = rand() % 10000; + b_.store(0, boost::memory_order_relaxed); + + barrier_.wait(); + + while (delay--) + backoff_dummy = delay; + } +} + +template<boost::memory_order store_order, boost::memory_order load_order> +void total_store_order_test<store_order, load_order>::check_conflict(void) +{ + if (vrfyb1_ == 0 && vrfya2_ == 0) + { + boost::lock_guard< boost::mutex > guard(m_); + detected_conflict_ = true; + terminate_threads_.store(true, boost::memory_order_relaxed); + c_.notify_all(); + } +} + +void test_seq_cst(void) +{ + double sum = 0.0; + + /* take 10 samples */ + for (std::size_t n = 0; n < 10; n++) + { + boost::posix_time::time_duration timeout(0, 0, 10); + + total_store_order_test<boost::memory_order_relaxed, boost::memory_order_relaxed> test; + test.run(timeout); + if (!test.detected_conflict()) + { + std::cout << "Failed to detect order=seq_cst violation while ith order=relaxed -- intrinsic ordering too strong for this test\n"; + return; + } + + std::cout << "seq_cst violation with order=relaxed after " << timeout.total_microseconds() << " us\n"; + + sum = sum + timeout.total_microseconds(); + } + + /* determine maximum likelihood estimate for average time between + race observations */ + double avg_race_time_mle = (sum / 10); + + /* pick 0.995 confidence (7.44 = chi square 0.995 confidence) */ + double avg_race_time_995 = avg_race_time_mle * 2 * 10 / 7.44; + + /* 5.298 = 0.995 quantile of exponential distribution */ + boost::posix_time::time_duration timeout = boost::posix_time::microseconds((long)(5.298 * avg_race_time_995)); + + std::cout << "run seq_cst for " << timeout.total_microseconds() << " us\n"; + + total_store_order_test<boost::memory_order_seq_cst, boost::memory_order_seq_cst> test; + test.run(timeout); + + BOOST_TEST(!test.detected_conflict()); // sequential consistency error +} + +int main(int, char *[]) +{ + test_seq_cst(); + + return boost::report_errors(); +} diff --git a/src/boost/libs/atomic/test/ordering_ref.cpp b/src/boost/libs/atomic/test/ordering_ref.cpp new file mode 100644 index 000000000..8a48277e0 --- /dev/null +++ b/src/boost/libs/atomic/test/ordering_ref.cpp @@ -0,0 +1,276 @@ +// Copyright (c) 2020 Andrey Semashev +// +// 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) + +// This test is based on ordering.cpp by Helge Bahmann and Tim Blechmann. +// The test Was modified to use atomic_ref template instead of atomic. + +// Attempt to determine whether the memory ordering/ fence operations +// work as expected: +// Let two threads race accessing multiple shared variables and +// verify that "observable" order of operations matches with the +// ordering constraints specified. +// +// We assume that "memory ordering violation" events are exponentially +// distributed, with unknown "average time between violations" +// (which is just the reciprocal of exp distribution parameter lambda). +// Use a "relaxed ordering" implementation that intentionally exhibits +// a (hopefully observable) such violation to compute the maximum-likelihood +// estimate for this time. From this, compute an estimate that covers the +// unknown value with 0.995 confidence (using chi square quantile). +// +// Use this estimate to pick a timeout for the race tests of the +// atomic implementations such that under the assumed distribution +// we get 0.995 probability to detect a race (if there is one). +// +// Overall this yields 0.995 * 0.995 > 0.99 confidence that the +// fences work as expected if this test program does not +// report an error. + +#include <boost/memory_order.hpp> +#include <boost/atomic/atomic.hpp> +#include <boost/atomic/atomic_ref.hpp> + +#include <cstddef> +#include <boost/bind/bind.hpp> +#include <boost/date_time/posix_time/posix_time_types.hpp> +#include <boost/thread/thread.hpp> +#include <boost/thread/thread_time.hpp> +#include <boost/thread/lock_guard.hpp> +#include <boost/thread/lock_types.hpp> +#include <boost/thread/mutex.hpp> +#include <boost/thread/condition_variable.hpp> +#include <boost/thread/barrier.hpp> +#include <boost/core/lightweight_test.hpp> + +// Two threads perform the following operations: +// +// thread # 1 thread # 2 +// store(a, 1) store(b, 1) +// x = read(b) y = read(a) +// +// Under relaxed memory ordering, the case (x, y) == (0, 0) is +// possible. Under sequential consistency, this case is impossible. +// +// This "problem" is reproducible on all platforms, even x86. +template<boost::memory_order store_order, boost::memory_order load_order> +class total_store_order_test +{ +public: + total_store_order_test(void); + + void run(boost::posix_time::time_duration & timeout); + bool detected_conflict(void) const { return detected_conflict_; } + +private: + void thread1fn(void); + void thread2fn(void); + void check_conflict(void); + +private: + int a_value_; + boost::atomic_ref<int> a_; + /* insert a bit of padding to push the two variables into + different cache lines and increase the likelihood of detecting + a conflict */ + char pad1_[512]; + int b_value_; + boost::atomic_ref<int> b_; + + char pad2_[512]; + boost::barrier barrier_; + + int vrfyb1_, vrfya2_; + + boost::atomic<bool> terminate_threads_; + boost::atomic<int> termination_consensus_; + + bool detected_conflict_; + boost::mutex m_; + boost::condition_variable c_; +}; + +template<boost::memory_order store_order, boost::memory_order load_order> +total_store_order_test<store_order, load_order>::total_store_order_test(void) : + a_value_(0), a_(a_value_), b_value_(0), b_(b_value_), barrier_(2), + vrfyb1_(0), vrfya2_(0), + terminate_threads_(false), termination_consensus_(0), + detected_conflict_(false) +{ +} + +template<boost::memory_order store_order, boost::memory_order load_order> +void total_store_order_test<store_order, load_order>::run(boost::posix_time::time_duration & timeout) +{ + boost::system_time start = boost::get_system_time(); + boost::system_time end = start + timeout; + + boost::thread t1(boost::bind(&total_store_order_test::thread1fn, this)); + boost::thread t2(boost::bind(&total_store_order_test::thread2fn, this)); + + { + boost::unique_lock< boost::mutex > guard(m_); + while (boost::get_system_time() < end && !detected_conflict_) + c_.timed_wait(guard, end); + } + + terminate_threads_.store(true, boost::memory_order_relaxed); + + t2.join(); + t1.join(); + + boost::posix_time::time_duration duration = boost::get_system_time() - start; + if (duration < timeout) + timeout = duration; +} + +volatile int backoff_dummy; + +template<boost::memory_order store_order, boost::memory_order load_order> +void total_store_order_test<store_order, load_order>::thread1fn(void) +{ + while (true) + { + a_.store(1, store_order); + int b = b_.load(load_order); + + barrier_.wait(); + + vrfyb1_ = b; + + barrier_.wait(); + + check_conflict(); + + /* both threads synchronize via barriers, so either + both threads must exit here, or they must both do + another round, otherwise one of them will wait forever */ + if (terminate_threads_.load(boost::memory_order_relaxed)) + { + while (true) + { + int tmp = termination_consensus_.fetch_or(1, boost::memory_order_relaxed); + + if (tmp == 3) + return; + if (tmp & 4) + break; + } + } + + termination_consensus_.fetch_xor(4, boost::memory_order_relaxed); + + unsigned int delay = rand() % 10000; + a_.store(0, boost::memory_order_relaxed); + + barrier_.wait(); + + while (delay--) + backoff_dummy = delay; + } +} + +template<boost::memory_order store_order, boost::memory_order load_order> +void total_store_order_test<store_order, load_order>::thread2fn(void) +{ + while (true) + { + b_.store(1, store_order); + int a = a_.load(load_order); + + barrier_.wait(); + + vrfya2_ = a; + + barrier_.wait(); + + check_conflict(); + + /* both threads synchronize via barriers, so either + both threads must exit here, or they must both do + another round, otherwise one of them will wait forever */ + if (terminate_threads_.load(boost::memory_order_relaxed)) + { + while (true) + { + int tmp = termination_consensus_.fetch_or(2, boost::memory_order_relaxed); + + if (tmp == 3) + return; + if (tmp & 4) + break; + } + } + + termination_consensus_.fetch_xor(4, boost::memory_order_relaxed); + + unsigned int delay = rand() % 10000; + b_.store(0, boost::memory_order_relaxed); + + barrier_.wait(); + + while (delay--) + backoff_dummy = delay; + } +} + +template<boost::memory_order store_order, boost::memory_order load_order> +void total_store_order_test<store_order, load_order>::check_conflict(void) +{ + if (vrfyb1_ == 0 && vrfya2_ == 0) + { + boost::lock_guard< boost::mutex > guard(m_); + detected_conflict_ = true; + terminate_threads_.store(true, boost::memory_order_relaxed); + c_.notify_all(); + } +} + +void test_seq_cst(void) +{ + double sum = 0.0; + + /* take 10 samples */ + for (std::size_t n = 0; n < 10; n++) + { + boost::posix_time::time_duration timeout(0, 0, 10); + + total_store_order_test<boost::memory_order_relaxed, boost::memory_order_relaxed> test; + test.run(timeout); + if (!test.detected_conflict()) + { + std::cout << "Failed to detect order=seq_cst violation while ith order=relaxed -- intrinsic ordering too strong for this test\n"; + return; + } + + std::cout << "seq_cst violation with order=relaxed after " << timeout.total_microseconds() << " us\n"; + + sum = sum + timeout.total_microseconds(); + } + + /* determine maximum likelihood estimate for average time between + race observations */ + double avg_race_time_mle = (sum / 10); + + /* pick 0.995 confidence (7.44 = chi square 0.995 confidence) */ + double avg_race_time_995 = avg_race_time_mle * 2 * 10 / 7.44; + + /* 5.298 = 0.995 quantile of exponential distribution */ + boost::posix_time::time_duration timeout = boost::posix_time::microseconds((long)(5.298 * avg_race_time_995)); + + std::cout << "run seq_cst for " << timeout.total_microseconds() << " us\n"; + + total_store_order_test<boost::memory_order_seq_cst, boost::memory_order_seq_cst> test; + test.run(timeout); + + BOOST_TEST(!test.detected_conflict()); // sequential consistency error +} + +int main(int, char *[]) +{ + test_seq_cst(); + + return boost::report_errors(); +} diff --git a/src/boost/libs/atomic/test/test_cmake/CMakeLists.txt b/src/boost/libs/atomic/test/test_cmake/CMakeLists.txt new file mode 100644 index 000000000..31170bdee --- /dev/null +++ b/src/boost/libs/atomic/test/test_cmake/CMakeLists.txt @@ -0,0 +1,22 @@ +# Copyright 2018 Mike Dev +# Distributed under the Boost Software License, Version 1.0. +# See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt +# +# NOTE: This does NOT run the unit tests for Boost.Atomic. +# It only tests, if the CMakeLists.txt file in it's root works as expected + +cmake_minimum_required( VERSION 3.5 ) + +project( BoostAtomicCMakeSelfTest ) + +add_definitions( -DBOOST_ALL_NO_LIB ) + +add_subdirectory( ../../../assert ${CMAKE_CURRENT_BINARY_DIR}/libs/assert ) +add_subdirectory( ../../../config ${CMAKE_CURRENT_BINARY_DIR}/libs/config ) +add_subdirectory( ../../../static_assert ${CMAKE_CURRENT_BINARY_DIR}/libs/static_assert ) +add_subdirectory( ../../../type_traits ${CMAKE_CURRENT_BINARY_DIR}/libs/type_traits ) + +add_subdirectory( ../.. ${CMAKE_CURRENT_BINARY_DIR}/libs/boost_atomic ) + +add_executable( boost_atomic_cmake_self_test main.cpp ) +target_link_libraries( boost_atomic_cmake_self_test Boost::atomic ) diff --git a/src/boost/libs/atomic/test/test_cmake/main.cpp b/src/boost/libs/atomic/test/test_cmake/main.cpp new file mode 100644 index 000000000..98d8453ad --- /dev/null +++ b/src/boost/libs/atomic/test/test_cmake/main.cpp @@ -0,0 +1,22 @@ +// Copyright (c) 2018 Mike Dev +// +// Distributed under the Boost Software License, Version 1.0. +// See accompanying file LICENSE_1_0.txt or copy at +// https://www.boost.org/LICENSE_1_0.txt) + +#include <boost/atomic.hpp> + +struct Dummy +{ + int x[128]; +}; + +int main() +{ + Dummy d = {}; + boost::atomic<Dummy> ad; + + // this operation requires functions from + // the compiled part of Boost.Atomic + ad = d; +} diff --git a/src/boost/libs/atomic/test/value_with_epsilon.hpp b/src/boost/libs/atomic/test/value_with_epsilon.hpp new file mode 100644 index 000000000..32180a7db --- /dev/null +++ b/src/boost/libs/atomic/test/value_with_epsilon.hpp @@ -0,0 +1,78 @@ +// Copyright (c) 2018 Andrey Semashev +// +// 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_TESTS_VALUE_WITH_EPSILON_H_INCLUDED_ +#define BOOST_ATOMIC_TESTS_VALUE_WITH_EPSILON_H_INCLUDED_ + +#include <limits> +#include <iosfwd> + +template< typename T > +class value_with_epsilon +{ +private: + T m_value; + T m_epsilon; + +public: + value_with_epsilon(T value, T epsilon) : m_value(value), m_epsilon(epsilon) {} + + T value() const + { + return m_value; + } + + T epsilon() const + { + return m_epsilon; + } + + bool equal(T value) const + { + return value >= (m_value - m_epsilon) && value <= (m_value + m_epsilon); + } + + friend bool operator== (T left, value_with_epsilon< T > const& right) + { + return right.equal(left); + } + friend bool operator== (value_with_epsilon< T > const& left, T right) + { + return left.equal(right); + } + + friend bool operator!= (T left, value_with_epsilon< T > const& right) + { + return !right.equal(left); + } + friend bool operator!= (value_with_epsilon< T > const& left, T right) + { + return !left.equal(right); + } +}; + +template< typename Char, typename Traits, typename T > +inline std::basic_ostream< Char, Traits >& operator<< (std::basic_ostream< Char, Traits >& strm, value_with_epsilon< T > const& val) +{ + // Note: libstdc++ does not provide output operators for __float128. There may also be no operators for long double. + // We don't use such floating point values in our tests where the cast would matter. + strm << static_cast< double >(val.value()) << " (+/-" << static_cast< double >(val.epsilon()) << ")"; + return strm; +} + +template< typename T, typename U > +inline value_with_epsilon< T > approx(T value, U epsilon) +{ + return value_with_epsilon< T >(value, static_cast< T >(epsilon)); +} + +template< typename T > +inline value_with_epsilon< T > approx(T value) +{ + return value_with_epsilon< T >(value, static_cast< T >(0.0000001)); +} + +#endif // BOOST_ATOMIC_TESTS_VALUE_WITH_EPSILON_H_INCLUDED_ |