From 483eb2f56657e8e7f419ab1a4fab8dce9ade8609 Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Sat, 27 Apr 2024 20:24:20 +0200 Subject: Adding upstream version 14.2.21. Signed-off-by: Daniel Baumann --- src/boost/libs/atomic/CMakeLists.txt | 26 + src/boost/libs/atomic/README.md | 26 + src/boost/libs/atomic/build/Jamfile.v2 | 38 + src/boost/libs/atomic/index.html | 13 + src/boost/libs/atomic/meta/libraries.json | 18 + src/boost/libs/atomic/src/lockpool.cpp | 167 +++ src/boost/libs/atomic/test/Jamfile.v2 | 32 + src/boost/libs/atomic/test/api_test_helpers.hpp | 1090 ++++++++++++++++++++ src/boost/libs/atomic/test/atomicity.cpp | 281 +++++ src/boost/libs/atomic/test/c_implicit_ctor.cpp | 34 + src/boost/libs/atomic/test/cf_arith_func_ptr.cpp | 17 + src/boost/libs/atomic/test/cf_arith_mem_ptr.cpp | 22 + src/boost/libs/atomic/test/cf_arith_void_ptr.cpp | 16 + src/boost/libs/atomic/test/fallback_api.cpp | 74 ++ src/boost/libs/atomic/test/lockfree.cpp | 225 ++++ src/boost/libs/atomic/test/native_api.cpp | 82 ++ src/boost/libs/atomic/test/ordering.cpp | 256 +++++ .../libs/atomic/test/test_cmake/CMakeLists.txt | 22 + src/boost/libs/atomic/test/test_cmake/main.cpp | 22 + src/boost/libs/atomic/test/value_with_epsilon.hpp | 78 ++ 20 files changed, 2539 insertions(+) create mode 100644 src/boost/libs/atomic/CMakeLists.txt create mode 100644 src/boost/libs/atomic/README.md create mode 100644 src/boost/libs/atomic/build/Jamfile.v2 create mode 100644 src/boost/libs/atomic/index.html create mode 100644 src/boost/libs/atomic/meta/libraries.json create mode 100644 src/boost/libs/atomic/src/lockpool.cpp create mode 100644 src/boost/libs/atomic/test/Jamfile.v2 create mode 100644 src/boost/libs/atomic/test/api_test_helpers.hpp create mode 100644 src/boost/libs/atomic/test/atomicity.cpp create mode 100644 src/boost/libs/atomic/test/c_implicit_ctor.cpp create mode 100644 src/boost/libs/atomic/test/cf_arith_func_ptr.cpp create mode 100644 src/boost/libs/atomic/test/cf_arith_mem_ptr.cpp create mode 100644 src/boost/libs/atomic/test/cf_arith_void_ptr.cpp create mode 100644 src/boost/libs/atomic/test/fallback_api.cpp create mode 100644 src/boost/libs/atomic/test/lockfree.cpp create mode 100644 src/boost/libs/atomic/test/native_api.cpp create mode 100644 src/boost/libs/atomic/test/ordering.cpp create mode 100644 src/boost/libs/atomic/test/test_cmake/CMakeLists.txt create mode 100644 src/boost/libs/atomic/test/test_cmake/main.cpp create mode 100644 src/boost/libs/atomic/test/value_with_epsilon.hpp (limited to 'src/boost/libs/atomic') diff --git a/src/boost/libs/atomic/CMakeLists.txt b/src/boost/libs/atomic/CMakeLists.txt new file mode 100644 index 00000000..73378fe4 --- /dev/null +++ b/src/boost/libs/atomic/CMakeLists.txt @@ -0,0 +1,26 @@ +# 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 + +cmake_minimum_required(VERSION 3.5) +project(BoostAtomic LANGUAGES CXX) + +add_library(boost_atomic src/lockpool.cpp) +add_library(Boost::atomic ALIAS boost_atomic) + +target_include_directories(boost_atomic PUBLIC include) + +target_link_libraries(boost_atomic + PUBLIC + Boost::assert + Boost::config + Boost::type_traits +) + +target_compile_definitions(boost_atomic PRIVATE BOOST_ATOMIC_SOURCE) + +if(${BUILD_SHARED_LIBS}) + target_compile_definitions(boost_atomic PUBLIC BOOST_ATOMIC_DYN_LINK) +else() + target_compile_definitions(boost_atomic PUBLIC BOOST_ATOMIC_STATIC_LINK) +endif() diff --git a/src/boost/libs/atomic/README.md b/src/boost/libs/atomic/README.md new file mode 100644 index 00000000..7ac813d8 --- /dev/null +++ b/src/boost/libs/atomic/README.md @@ -0,0 +1,26 @@ +# ![Boost.Atomic](doc/logo.png) + +Boost.Atomic, part of collection of the [Boost C++ Libraries](https://github.com/boostorg), implements atomic operations for various CPU architectures, reflecting and extending the standard interface defined in C++11. + +### Directories + +* **build** - Boost.Atomic build scripts +* **doc** - QuickBook documentation sources +* **include** - Interface headers of Boost.Atomic +* **src** - Compilable source code of Boost.Atomic +* **test** - Boost.Atomic unit tests + +### More information + +* [Documentation](https://boost.org/libs/atomic) +* [Report bugs](https://svn.boost.org/trac/boost/newticket?component=atomic;version=Boost%20Release%20Branch). Be sure to mention Boost version, platform and compiler you're using. A small compilable code sample to reproduce the problem is always good as well. +* Submit your patches as pull requests against **develop** branch. Note that by submitting patches you agree to license your modifications under the [Boost Software License, Version 1.0](https://www.boost.org/LICENSE_1_0.txt). + +### Build status + +Master: [![AppVeyor](https://ci.appveyor.com/api/projects/status/c64xu59bydnmb7kt/branch/master?svg=true)](https://ci.appveyor.com/project/Lastique/atomic/branch/master) [![Travis CI](https://travis-ci.org/boostorg/atomic.svg?branch=master)](https://travis-ci.org/boostorg/atomic) +Develop: [![AppVeyor](https://ci.appveyor.com/api/projects/status/c64xu59bydnmb7kt/branch/develop?svg=true)](https://ci.appveyor.com/project/Lastique/atomic/branch/develop) [![Travis CI](https://travis-ci.org/boostorg/atomic.svg?branch=develop)](https://travis-ci.org/boostorg/atomic) + +### License + +Distributed under the [Boost Software License, Version 1.0](https://www.boost.org/LICENSE_1_0.txt). diff --git a/src/boost/libs/atomic/build/Jamfile.v2 b/src/boost/libs/atomic/build/Jamfile.v2 new file mode 100644 index 00000000..433ebc8b --- /dev/null +++ b/src/boost/libs/atomic/build/Jamfile.v2 @@ -0,0 +1,38 @@ +# Boost.Atomic Library Jamfile +# +# Copyright Helge Bahmann 2011. +# Copyright Andrey Semashev 2018. +# +# 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 common ; + +project boost/atomic + : requirements + multi + shared:BOOST_ATOMIC_DYN_LINK=1 + static:BOOST_ATOMIC_STATIC_LINK=1 + BOOST_ATOMIC_SOURCE + windows:BOOST_USE_WINDOWS_H + windows:_WIN32_WINNT=0x0500 + gcc,windows:"-lkernel32" + : usage-requirements + shared:BOOST_ATOMIC_DYN_LINK=1 + static:BOOST_ATOMIC_STATIC_LINK=1 + : source-location ../src + ; + +alias atomic_sources + : lockpool.cpp + ; + +explicit atomic_sources ; + + +lib boost_atomic + : atomic_sources + ; + +boost-install boost_atomic ; diff --git a/src/boost/libs/atomic/index.html b/src/boost/libs/atomic/index.html new file mode 100644 index 00000000..62a6c59f --- /dev/null +++ b/src/boost/libs/atomic/index.html @@ -0,0 +1,13 @@ + + + + + +Automatic redirection failed, please go to +../../doc/html/atomic.html  
+

© Copyright Beman Dawes, 2001

+

Distributed under the Boost Software License, Version 1.0. (See accompanying +file LICENSE_1_0.txt or copy +at www.boost.org/LICENSE_1_0.txt)

+ + diff --git a/src/boost/libs/atomic/meta/libraries.json b/src/boost/libs/atomic/meta/libraries.json new file mode 100644 index 00000000..42e0fe3c --- /dev/null +++ b/src/boost/libs/atomic/meta/libraries.json @@ -0,0 +1,18 @@ +{ + "key": "atomic", + "name": "Atomic", + "authors": [ + "Helge Bahmann", + "Tim Blechmann", + "Andrey Semashev" + ], + "description": "C++11-style atomic<>.", + "category": [ + "Concurrent" + ], + "maintainers": [ + "Helge Bahmann ", + "Tim Blechmann ", + "Andrey Semashev " + ] +} diff --git a/src/boost/libs/atomic/src/lockpool.cpp b/src/boost/libs/atomic/src/lockpool.cpp new file mode 100644 index 00000000..a1292fa7 --- /dev/null +++ b/src/boost/libs/atomic/src/lockpool.cpp @@ -0,0 +1,167 @@ +/* + * 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) + * + * Copyright (c) 2011 Helge Bahmann + * Copyright (c) 2013-2014 Andrey Semashev + */ +/*! + * \file lockpool.cpp + * + * This file contains implementation of the lockpool used to emulate atomic ops. + */ + +#include +#include +#include +#include +#include + +#if BOOST_ATOMIC_FLAG_LOCK_FREE == 2 +#include +#elif !defined(BOOST_HAS_PTHREADS) +#error Boost.Atomic: Unsupported target platform, POSIX threads are required when native atomic operations are not available +#else +#include +#define BOOST_ATOMIC_USE_PTHREAD +#endif + +#include +#include + +#if defined(BOOST_MSVC) +#pragma warning(push) +// 'struct_name' : structure was padded due to __declspec(align()) +#pragma warning(disable: 4324) +#endif + +namespace boost { +namespace atomics { +namespace detail { + +namespace { + +// Cache line size, in bytes +// NOTE: This constant is made as a macro because some compilers (gcc 4.4 for one) don't allow enums or namespace scope constants in alignment attributes +#if defined(__s390__) || defined(__s390x__) +#define BOOST_ATOMIC_CACHE_LINE_SIZE 256 +#elif defined(powerpc) || defined(__powerpc__) || defined(__ppc__) +#define BOOST_ATOMIC_CACHE_LINE_SIZE 128 +#else +#define BOOST_ATOMIC_CACHE_LINE_SIZE 64 +#endif + +#if defined(BOOST_ATOMIC_USE_PTHREAD) +typedef pthread_mutex_t lock_type; +#else +typedef atomics::detail::operations< 1u, false > lock_operations; +typedef lock_operations::storage_type lock_type; +#endif + +enum +{ + padding_size = (sizeof(lock_type) <= BOOST_ATOMIC_CACHE_LINE_SIZE ? + (BOOST_ATOMIC_CACHE_LINE_SIZE - sizeof(lock_type)) : + (BOOST_ATOMIC_CACHE_LINE_SIZE - sizeof(lock_type) % BOOST_ATOMIC_CACHE_LINE_SIZE)) +}; + +template< unsigned int PaddingSize > +struct BOOST_ALIGNMENT(BOOST_ATOMIC_CACHE_LINE_SIZE) padded_lock +{ + lock_type lock; + // The additional padding is needed to avoid false sharing between locks + char padding[PaddingSize]; +}; + +template< > +struct BOOST_ALIGNMENT(BOOST_ATOMIC_CACHE_LINE_SIZE) padded_lock< 0u > +{ + lock_type lock; +}; + +typedef padded_lock< padding_size > padded_lock_t; + +static padded_lock_t g_lock_pool[41] +#if defined(BOOST_ATOMIC_USE_PTHREAD) += +{ + { PTHREAD_MUTEX_INITIALIZER }, { PTHREAD_MUTEX_INITIALIZER }, { PTHREAD_MUTEX_INITIALIZER }, { PTHREAD_MUTEX_INITIALIZER }, { PTHREAD_MUTEX_INITIALIZER }, + { PTHREAD_MUTEX_INITIALIZER }, { PTHREAD_MUTEX_INITIALIZER }, { PTHREAD_MUTEX_INITIALIZER }, { PTHREAD_MUTEX_INITIALIZER }, { PTHREAD_MUTEX_INITIALIZER }, + { PTHREAD_MUTEX_INITIALIZER }, { PTHREAD_MUTEX_INITIALIZER }, { PTHREAD_MUTEX_INITIALIZER }, { PTHREAD_MUTEX_INITIALIZER }, { PTHREAD_MUTEX_INITIALIZER }, + { PTHREAD_MUTEX_INITIALIZER }, { PTHREAD_MUTEX_INITIALIZER }, { PTHREAD_MUTEX_INITIALIZER }, { PTHREAD_MUTEX_INITIALIZER }, { PTHREAD_MUTEX_INITIALIZER }, + { PTHREAD_MUTEX_INITIALIZER }, { PTHREAD_MUTEX_INITIALIZER }, { PTHREAD_MUTEX_INITIALIZER }, { PTHREAD_MUTEX_INITIALIZER }, { PTHREAD_MUTEX_INITIALIZER }, + { PTHREAD_MUTEX_INITIALIZER }, { PTHREAD_MUTEX_INITIALIZER }, { PTHREAD_MUTEX_INITIALIZER }, { PTHREAD_MUTEX_INITIALIZER }, { PTHREAD_MUTEX_INITIALIZER }, + { PTHREAD_MUTEX_INITIALIZER }, { PTHREAD_MUTEX_INITIALIZER }, { PTHREAD_MUTEX_INITIALIZER }, { PTHREAD_MUTEX_INITIALIZER }, { PTHREAD_MUTEX_INITIALIZER }, + { PTHREAD_MUTEX_INITIALIZER }, { PTHREAD_MUTEX_INITIALIZER }, { PTHREAD_MUTEX_INITIALIZER }, { PTHREAD_MUTEX_INITIALIZER }, { PTHREAD_MUTEX_INITIALIZER }, + { PTHREAD_MUTEX_INITIALIZER } +} +#endif +; + +} // namespace + + +#if !defined(BOOST_ATOMIC_USE_PTHREAD) + +// NOTE: This function must NOT be inline. Otherwise MSVC 9 will sometimes generate broken code for modulus operation which result in crashes. +BOOST_ATOMIC_DECL lockpool::scoped_lock::scoped_lock(const volatile void* addr) BOOST_NOEXCEPT : + m_lock(&g_lock_pool[reinterpret_cast< std::size_t >(addr) % (sizeof(g_lock_pool) / sizeof(*g_lock_pool))].lock) +{ + while (lock_operations::test_and_set(*static_cast< lock_type* >(m_lock), memory_order_acquire)) + { + do + { + atomics::detail::pause(); + } + while (!!lock_operations::load(*static_cast< lock_type* >(m_lock), memory_order_relaxed)); + } +} + +BOOST_ATOMIC_DECL lockpool::scoped_lock::~scoped_lock() BOOST_NOEXCEPT +{ + lock_operations::clear(*static_cast< lock_type* >(m_lock), memory_order_release); +} + +BOOST_ATOMIC_DECL void signal_fence() BOOST_NOEXCEPT; + +#else // !defined(BOOST_ATOMIC_USE_PTHREAD) + +BOOST_ATOMIC_DECL lockpool::scoped_lock::scoped_lock(const volatile void* addr) BOOST_NOEXCEPT : + m_lock(&g_lock_pool[reinterpret_cast< std::size_t >(addr) % (sizeof(g_lock_pool) / sizeof(*g_lock_pool))].lock) +{ + BOOST_VERIFY(pthread_mutex_lock(static_cast< pthread_mutex_t* >(m_lock)) == 0); +} + +BOOST_ATOMIC_DECL lockpool::scoped_lock::~scoped_lock() BOOST_NOEXCEPT +{ + BOOST_VERIFY(pthread_mutex_unlock(static_cast< pthread_mutex_t* >(m_lock)) == 0); +} + +#endif // !defined(BOOST_ATOMIC_USE_PTHREAD) + +BOOST_ATOMIC_DECL void lockpool::thread_fence() BOOST_NOEXCEPT +{ +#if BOOST_ATOMIC_THREAD_FENCE > 0 + atomics::detail::thread_fence(memory_order_seq_cst); +#else + // Emulate full fence by locking/unlocking a mutex + scoped_lock lock(0); +#endif +} + +BOOST_ATOMIC_DECL void lockpool::signal_fence() BOOST_NOEXCEPT +{ + // This function is intentionally non-inline, even if empty. This forces the compiler to treat its call as a compiler barrier. +#if BOOST_ATOMIC_SIGNAL_FENCE > 0 + atomics::detail::signal_fence(memory_order_seq_cst); +#endif +} + +} // namespace detail +} // namespace atomics +} // namespace boost + +#if defined(BOOST_MSVC) +#pragma warning(pop) +#endif diff --git a/src/boost/libs/atomic/test/Jamfile.v2 b/src/boost/libs/atomic/test/Jamfile.v2 new file mode 100644 index 00000000..ccab23d9 --- /dev/null +++ b/src/boost/libs/atomic/test/Jamfile.v2 @@ -0,0 +1,32 @@ +# Boost.Atomic Library test Jamfile +# +# 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) + +import testing ; + +project boost/atomic/test + : requirements + multi + /boost/thread//boost_thread + /boost/atomic//boost_atomic + windows:BOOST_USE_WINDOWS_H + windows:_WIN32_WINNT=0x0500 + gcc,windows:"-lkernel32" + ; + +test-suite atomic + : [ run native_api.cpp ] + [ run fallback_api.cpp ] + [ run atomicity.cpp ] + [ run ordering.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/api_test_helpers.hpp b/src/boost/libs/atomic/test/api_test_helpers.hpp new file mode 100644 index 00000000..06f3b198 --- /dev/null +++ b/src/boost/libs/atomic/test/api_test_helpers.hpp @@ -0,0 +1,1090 @@ +// Copyright (c) 2011 Helge Bahmann +// Copyright (c) 2017 - 2019 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +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 + +#include "value_with_epsilon.hpp" + +/* provide helpers that exercise whether the API +functions of "boost::atomic" provide the correct +operational semantic in the case of sequential +execution */ + +static void +test_flag_api(void) +{ +#ifndef BOOST_ATOMIC_NO_ATOMIC_FLAG_INIT + boost::atomic_flag f = BOOST_ATOMIC_FLAG_INIT; +#else + boost::atomic_flag f; +#endif + + BOOST_TEST( !f.test_and_set() ); + BOOST_TEST( f.test_and_set() ); + f.clear(); + BOOST_TEST( !f.test_and_set() ); +} + +template +void test_base_operators(T value1, T value2, T value3) +{ + /* explicit load/store */ + { + boost::atomic a(value1); + BOOST_TEST_EQ( a.load(), value1 ); + } + + { + boost::atomic a(value1); + a.store(value2); + BOOST_TEST_EQ( a.load(), value2 ); + } + + /* overloaded assignment/conversion */ + { + boost::atomic a(value1); + BOOST_TEST( value1 == a ); + } + + { + boost::atomic a; + a = value2; + BOOST_TEST( value2 == a ); + } + + /* exchange-type operators */ + { + boost::atomic a(value1); + T n = a.exchange(value2); + BOOST_TEST_EQ( a.load(), value2 ); + BOOST_TEST_EQ( n, value1 ); + } + + { + boost::atomic a(value1); + T expected = value1; + bool success = a.compare_exchange_strong(expected, value3); + BOOST_TEST( success ); + BOOST_TEST_EQ( a.load(), value3 ); + BOOST_TEST_EQ( expected, value1 ); + } + + { + boost::atomic a(value1); + T expected = value2; + bool success = a.compare_exchange_strong(expected, value3); + BOOST_TEST( !success ); + BOOST_TEST_EQ( a.load(), value1 ); + BOOST_TEST_EQ( expected, value1 ); + } + + { + boost::atomic a(value1); + T expected; + bool success; + do { + expected = value1; + success = a.compare_exchange_weak(expected, value3); + } while(!success); + BOOST_TEST( success ); + BOOST_TEST_EQ( a.load(), value3 ); + BOOST_TEST_EQ( expected, value1 ); + } + + { + boost::atomic a(value1); + T expected; + bool success; + do { + expected = value2; + success = a.compare_exchange_weak(expected, value3); + if (expected != value2) + break; + } while(!success); + BOOST_TEST( !success ); + BOOST_TEST_EQ( a.load(), value1 ); + BOOST_TEST_EQ( expected, value1 ); + } +} + +// T requires an int constructor +template +void test_constexpr_ctor() +{ +#ifndef BOOST_NO_CXX11_CONSTEXPR + const T value(0); + const boost::atomic tester(value); + BOOST_TEST( tester == value ); +#endif +} + +//! The type traits provides max and min values of type D that can be added/subtracted to T(0) without signed overflow +template< typename T, typename D, bool IsSigned = boost::is_signed< D >::value > +struct distance_limits +{ + //! Difference type D promoted to the width of type T + typedef typename boost::conditional< + IsSigned, + typename boost::make_signed< T >::type, + typename 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 + +template +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; + { + boost::atomic a(zero_value); + 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) ); + } + { + boost::atomic a(zero_value); + 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)()) ); + } + { + boost::atomic a(zero_value); + bool f = a.add_and_test((distance_limits< T, D >::min)()); + BOOST_TEST_EQ( f, ((distance_limits< T, D >::min)() != 0) ); + BOOST_TEST_EQ( a.load(), T(zero_add + (distance_limits< T, D >::min)()) ); + } + + { + boost::atomic a(zero_value); + bool f = a.sub_and_test(zero_diff); + BOOST_TEST_EQ( f, 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) ); + } + { + boost::atomic a(zero_value); + 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)()) ); + } + { + boost::atomic a(zero_value); + bool f = a.sub_and_test((distance_limits< T, D >::min)()); + BOOST_TEST_EQ( f, ((distance_limits< T, D >::min)() != 0) ); + // 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) ); + } +} + +template +void test_additive_operators_with_type(T value, D delta) +{ + /* note: the tests explicitly cast the result of any addition + to the type to be tested to force truncation of the result to + the correct range in case of overflow */ + + /* explicit add/sub */ + { + boost::atomic a(value); + T n = a.fetch_add(delta); + BOOST_TEST_EQ( a.load(), T((AddType)value + delta) ); + BOOST_TEST_EQ( n, value ); + } + + { + boost::atomic a(value); + T n = a.fetch_sub(delta); + BOOST_TEST_EQ( a.load(), T((AddType)value - delta) ); + BOOST_TEST_EQ( n, value ); + } + + /* overloaded modify/assign*/ + { + boost::atomic a(value); + T n = (a += delta); + BOOST_TEST_EQ( a.load(), T((AddType)value + delta) ); + BOOST_TEST_EQ( n, T((AddType)value + delta) ); + } + + { + boost::atomic a(value); + T n = (a -= delta); + BOOST_TEST_EQ( a.load(), T((AddType)value - delta) ); + BOOST_TEST_EQ( n, T((AddType)value - delta) ); + } + + /* overloaded increment/decrement */ + { + boost::atomic a(value); + T n = a++; + BOOST_TEST_EQ( a.load(), T((AddType)value + 1) ); + BOOST_TEST_EQ( n, value ); + } + + { + boost::atomic a(value); + T n = ++a; + BOOST_TEST_EQ( a.load(), T((AddType)value + 1) ); + BOOST_TEST_EQ( n, T((AddType)value + 1) ); + } + + { + boost::atomic a(value); + T n = a--; + BOOST_TEST_EQ( a.load(), T((AddType)value - 1) ); + BOOST_TEST_EQ( n, value ); + } + + { + boost::atomic a(value); + T n = --a; + BOOST_TEST_EQ( a.load(), T((AddType)value - 1) ); + BOOST_TEST_EQ( n, T((AddType)value - 1) ); + } + + // Operations returning the actual resulting value + { + boost::atomic a(value); + T n = a.add(delta); + BOOST_TEST_EQ( a.load(), T((AddType)value + delta) ); + BOOST_TEST_EQ( n, T((AddType)value + delta) ); + } + + { + boost::atomic a(value); + T n = a.sub(delta); + BOOST_TEST_EQ( a.load(), T((AddType)value - delta) ); + BOOST_TEST_EQ( n, T((AddType)value - delta) ); + } + + // Opaque operations + { + boost::atomic a(value); + a.opaque_add(delta); + BOOST_TEST_EQ( a.load(), T((AddType)value + delta) ); + } + + { + boost::atomic a(value); + a.opaque_sub(delta); + BOOST_TEST_EQ( a.load(), T((AddType)value - delta) ); + } + + // Modify and test operations + test_additive_operators_with_type_and_test< T, D, AddType >(); +} + +template +void test_additive_operators(T value, D delta) +{ + test_additive_operators_with_type(value, delta); +} + +template< typename T > +void test_negation() +{ + { + boost::atomic a((T)1); + T n = a.fetch_negate(); + BOOST_TEST_EQ( a.load(), (T)-1 ); + BOOST_TEST_EQ( n, (T)1 ); + + n = a.fetch_negate(); + BOOST_TEST_EQ( a.load(), (T)1 ); + BOOST_TEST_EQ( n, (T)-1 ); + } + { + boost::atomic a((T)1); + 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 ); + } + { + boost::atomic a((T)1); + a.opaque_negate(); + BOOST_TEST_EQ( a.load(), (T)-1 ); + + a.opaque_negate(); + BOOST_TEST_EQ( a.load(), (T)1 ); + } + { + boost::atomic a((T)1); + 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 ); + } + { + boost::atomic a((T)0); + bool f = a.negate_and_test(); + BOOST_TEST_EQ( f, false ); + BOOST_TEST_EQ( a.load(), (T)0 ); + } +} + +template +void test_additive_wrap(T value) +{ + { + boost::atomic a(value); + T n = a.fetch_add(1) + (T)1; + BOOST_TEST_EQ( a.load(), n ); + } + { + boost::atomic a(value); + T n = a.fetch_sub(1) - (T)1; + BOOST_TEST_EQ( a.load(), n ); + } +} + +template +void test_bit_operators(T value, T delta) +{ + /* explicit and/or/xor */ + { + boost::atomic a(value); + T n = a.fetch_and(delta); + BOOST_TEST_EQ( a.load(), T(value & delta) ); + BOOST_TEST_EQ( n, value ); + } + + { + boost::atomic a(value); + T n = a.fetch_or(delta); + BOOST_TEST_EQ( a.load(), T(value | delta) ); + BOOST_TEST_EQ( n, value ); + } + + { + boost::atomic a(value); + T n = a.fetch_xor(delta); + BOOST_TEST_EQ( a.load(), T(value ^ delta) ); + BOOST_TEST_EQ( n, value ); + } + + { + boost::atomic a(value); + T n = a.fetch_complement(); + BOOST_TEST_EQ( a.load(), T(~value) ); + BOOST_TEST_EQ( n, value ); + } + + /* overloaded modify/assign */ + { + boost::atomic a(value); + T n = (a &= delta); + BOOST_TEST_EQ( a.load(), T(value & delta) ); + BOOST_TEST_EQ( n, T(value & delta) ); + } + + { + boost::atomic a(value); + T n = (a |= delta); + BOOST_TEST_EQ( a.load(), T(value | delta) ); + BOOST_TEST_EQ( n, T(value | delta) ); + } + + { + boost::atomic a(value); + T n = (a ^= delta); + BOOST_TEST_EQ( a.load(), T(value ^ delta) ); + BOOST_TEST_EQ( n, T(value ^ delta) ); + } + + // Operations returning the actual resulting value + { + boost::atomic a(value); + T n = a.bitwise_and(delta); + BOOST_TEST_EQ( a.load(), T(value & delta) ); + BOOST_TEST_EQ( n, T(value & delta) ); + } + + { + boost::atomic a(value); + T n = a.bitwise_or(delta); + BOOST_TEST_EQ( a.load(), T(value | delta) ); + BOOST_TEST_EQ( n, T(value | delta) ); + } + + { + boost::atomic a(value); + T n = a.bitwise_xor(delta); + BOOST_TEST_EQ( a.load(), T(value ^ delta) ); + BOOST_TEST_EQ( n, T(value ^ delta) ); + } + + { + boost::atomic a(value); + T n = a.bitwise_complement(); + BOOST_TEST_EQ( a.load(), T(~value) ); + BOOST_TEST_EQ( n, T(~value) ); + } + + // Opaque operations + { + boost::atomic a(value); + a.opaque_and(delta); + BOOST_TEST_EQ( a.load(), T(value & delta) ); + } + + { + boost::atomic a(value); + a.opaque_or(delta); + BOOST_TEST_EQ( a.load(), T(value | delta) ); + } + + { + boost::atomic a(value); + a.opaque_xor(delta); + BOOST_TEST_EQ( a.load(), T(value ^ delta) ); + } + + { + boost::atomic a(value); + a.opaque_complement(); + BOOST_TEST_EQ( a.load(), T(~value) ); + } + + // Modify and test operations + { + boost::atomic a((T)1); + bool f = a.and_and_test((T)1); + BOOST_TEST_EQ( f, 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) ); + } + + { + boost::atomic a((T)0); + 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) ); + } + + { + boost::atomic a((T)0); + 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) ); + } + + { + boost::atomic a((T)0); + 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 + { + boost::atomic a((T)42); + bool f = a.bit_test_and_set(0); + BOOST_TEST_EQ( f, false ); + BOOST_TEST_EQ( a.load(), T(43) ); + + f = a.bit_test_and_set(1); + BOOST_TEST_EQ( f, true ); + BOOST_TEST_EQ( a.load(), T(43) ); + + f = a.bit_test_and_set(2); + BOOST_TEST_EQ( f, false ); + BOOST_TEST_EQ( a.load(), T(47) ); + } + + { + boost::atomic a((T)42); + bool f = a.bit_test_and_reset(0); + BOOST_TEST_EQ( f, false ); + BOOST_TEST_EQ( a.load(), T(42) ); + + f = a.bit_test_and_reset(1); + BOOST_TEST_EQ( f, true ); + BOOST_TEST_EQ( a.load(), T(40) ); + + f = a.bit_test_and_set(2); + BOOST_TEST_EQ( f, false ); + BOOST_TEST_EQ( a.load(), T(44) ); + } + + { + boost::atomic a((T)42); + bool f = a.bit_test_and_complement(0); + BOOST_TEST_EQ( f, false ); + BOOST_TEST_EQ( a.load(), T(43) ); + + f = a.bit_test_and_complement(1); + BOOST_TEST_EQ( f, true ); + BOOST_TEST_EQ( a.load(), T(41) ); + + f = a.bit_test_and_complement(2); + BOOST_TEST_EQ( f, false ); + BOOST_TEST_EQ( a.load(), T(45) ); + } +} + +template +void do_test_integral_api(boost::false_type) +{ + BOOST_TEST(sizeof(boost::atomic) >= sizeof(T)); + + test_base_operators(42, 43, 44); + test_additive_operators(42, 17); + test_bit_operators((T)0x5f5f5f5f5f5f5f5fULL, (T)0xf5f5f5f5f5f5f5f5ULL); + + /* test for unsigned overflow/underflow */ + test_additive_operators((T)-1, 1); + test_additive_operators(0, 1); + /* test for signed overflow/underflow */ + test_additive_operators(((T)-1) >> (sizeof(T) * 8 - 1), 1); + test_additive_operators(1 + (((T)-1) >> (sizeof(T) * 8 - 1)), 1); +} + +template +void do_test_integral_api(boost::true_type) +{ + do_test_integral_api(boost::false_type()); + + test_additive_wrap(0u); + BOOST_CONSTEXPR_OR_CONST T all_ones = ~(T)0u; + test_additive_wrap(all_ones); + BOOST_CONSTEXPR_OR_CONST T max_signed_twos_compl = all_ones >> 1; + test_additive_wrap(all_ones ^ max_signed_twos_compl); + test_additive_wrap(max_signed_twos_compl); +} + +template +inline void test_integral_api(void) +{ + do_test_integral_api(boost::is_unsigned()); + + if (boost::is_signed::value) + test_negation(); +} + +#if !defined(BOOST_ATOMIC_NO_FLOATING_POINT) + +template +void test_fp_additive_operators(T value, D delta) +{ + /* explicit add/sub */ + { + boost::atomic a(value); + T n = a.fetch_add(delta); + BOOST_TEST_EQ( a.load(), approx(T(value + delta)) ); + BOOST_TEST_EQ( n, approx(value) ); + } + + { + boost::atomic a(value); + T n = a.fetch_sub(delta); + BOOST_TEST_EQ( a.load(), approx(T(value - delta)) ); + BOOST_TEST_EQ( n, approx(value) ); + } + + /* overloaded modify/assign*/ + { + boost::atomic a(value); + T n = (a += delta); + BOOST_TEST_EQ( a.load(), approx(T(value + delta)) ); + BOOST_TEST_EQ( n, approx(T(value + delta)) ); + } + + { + boost::atomic a(value); + 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 + { + boost::atomic a(value); + T n = a.add(delta); + BOOST_TEST_EQ( a.load(), approx(T(value + delta)) ); + BOOST_TEST_EQ( n, approx(T(value + delta)) ); + } + + { + boost::atomic a(value); + T n = a.sub(delta); + BOOST_TEST_EQ( a.load(), approx(T(value - delta)) ); + BOOST_TEST_EQ( n, approx(T(value - delta)) ); + } + + // Opaque operations + { + boost::atomic a(value); + a.opaque_add(delta); + BOOST_TEST_EQ( a.load(), approx(T(value + delta)) ); + } + + { + boost::atomic a(value); + a.opaque_sub(delta); + BOOST_TEST_EQ( a.load(), approx(T(value - delta)) ); + } +} + +template< typename T > +void test_fp_negation() +{ + { + boost::atomic a((T)1); + 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) ); + } + { + boost::atomic a((T)1); + 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) ); + } + { + boost::atomic a((T)1); + 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 +inline void test_floating_point_api(void) +{ + BOOST_TEST(sizeof(boost::atomic) >= sizeof(T)); + + // 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(static_cast(42.1), static_cast(43.2), static_cast(44.3)); + + test_fp_additive_operators(static_cast(42.5), static_cast(17.7)); + test_fp_additive_operators(static_cast(-42.5), static_cast(-17.7)); + + test_fp_negation(); +#endif +} + + +template +void test_pointer_api(void) +{ + BOOST_TEST_GE( sizeof(boost::atomic), sizeof(T *)); + BOOST_TEST_GE( sizeof(boost::atomic), sizeof(T *)); + + T values[3]; + + test_base_operators(&values[0], &values[1], &values[2]); + test_additive_operators(&values[1], 1); + + test_base_operators(&values[0], &values[1], &values[2]); + +#if defined(BOOST_HAS_INTPTR_T) + boost::atomic ptr; + boost::atomic integral; + BOOST_TEST_EQ( ptr.is_lock_free(), integral.is_lock_free() ); +#endif +} + +enum test_enum +{ + foo, bar, baz +}; + +static void +test_enum_api(void) +{ + test_base_operators(foo, bar, baz); +} + +template +struct test_struct +{ + typedef T value_type; + value_type i; + inline bool operator==(const test_struct & c) const {return i == c.i;} + inline bool operator!=(const test_struct & c) const {return i != c.i;} +}; + +template< typename Char, typename Traits, typename T > +inline std::basic_ostream< Char, Traits >& operator<< (std::basic_ostream< Char, Traits >& strm, test_struct< T > const& s) +{ + test_stream << "{" << s.i << "}"; + return strm; +} + +template +void +test_struct_api(void) +{ + T a = {1}, b = {2}, c = {3}; + + test_base_operators(a, b, c); + + { + boost::atomic sa; + boost::atomic si; + BOOST_TEST_EQ( sa.is_lock_free(), si.is_lock_free() ); + } +} + +template +struct test_struct_x2 +{ + typedef T value_type; + value_type i, j; + inline bool operator==(const test_struct_x2 & c) const {return i == c.i && j == c.j;} + inline bool operator!=(const test_struct_x2 & c) const {return i != c.i && j != c.j;} +}; + +template< typename Char, typename Traits, typename T > +inline std::basic_ostream< Char, Traits >& operator<< (std::basic_ostream< Char, Traits >& strm, test_struct_x2< T > const& s) +{ + test_stream << "{" << s.i << ", " << s.j << "}"; + return strm; +} + +template +void +test_struct_x2_api(void) +{ + T a = {1, 1}, b = {2, 2}, c = {3, 3}; + + test_base_operators(a, b, c); +} + +struct large_struct +{ + long data[64]; + + inline bool operator==(const large_struct & c) const + { + return std::memcmp(data, &c.data, sizeof(data)) == 0; + } + inline bool operator!=(const large_struct & c) const + { + return std::memcmp(data, &c.data, sizeof(data)) != 0; + } +}; + +template< typename Char, typename Traits > +inline std::basic_ostream< Char, Traits >& operator<< (std::basic_ostream< Char, Traits >& strm, large_struct const&) +{ + strm << "[large_struct]"; + return strm; +} + +static void +test_large_struct_api(void) +{ + large_struct a = {{1}}, b = {{2}}, c = {{3}}; + test_base_operators(a, b, c); +} + +struct test_struct_with_ctor +{ + typedef unsigned int value_type; + value_type i; + test_struct_with_ctor() : i(0x01234567) {} + inline bool operator==(const test_struct_with_ctor & c) const {return i == c.i;} + inline bool operator!=(const test_struct_with_ctor & c) const {return i != c.i;} +}; + +template< typename Char, typename Traits > +inline std::basic_ostream< Char, Traits >& operator<< (std::basic_ostream< Char, Traits >& strm, test_struct_with_ctor const&) +{ + strm << "[test_struct_with_ctor]"; + return strm; +} + +static void +test_struct_with_ctor_api(void) +{ + { + test_struct_with_ctor s; + boost::atomic sa; + // Check that the default constructor was called + BOOST_TEST( sa.load() == s ); + } + + test_struct_with_ctor a, b, c; + a.i = 1; + b.i = 2; + c.i = 3; + + test_base_operators(a, b, c); +} + +#endif diff --git a/src/boost/libs/atomic/test/atomicity.cpp b/src/boost/libs/atomic/test/atomicity.cpp new file mode 100644 index 00000000..eff80cf5 --- /dev/null +++ b/src/boost/libs/atomic/test/atomicity.cpp @@ -0,0 +1,281 @@ +// 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 + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* 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 & fn, + boost::posix_time::time_duration & timeout) + { + concurrent_runner runner(fn); + runner.wait_finish(timeout); + return !runner.failure(); + } + + + concurrent_runner( + const boost::function & 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::mutex::scoped_lock 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 throw() { + return finished_.load(boost::memory_order_relaxed); + } + + bool + failure(void) const throw() { + return failure_; + } +private: + void + thread_function(boost::function function, size_t instance) + { + while (!finished()) { + if (!function(instance)) { + boost::mutex::scoped_lock guard(m_); + failure_ = true; + finished_.store(true, boost::memory_order_relaxed); + c_.notify_all(); + break; + } + } + } + + + boost::mutex m_; + boost::condition_variable c_; + + boost::atomic finished_; + bool failure_; + + boost::thread first_thread_; + boost::thread second_thread_; +}; + +bool +racy_add(volatile unsigned int & value, size_t instance) +{ + size_t shift = instance * 8; + unsigned int mask = 0xff << shift; + for (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 */ +static double +estimate_avg_race_time(void) +{ + double sum = 0.0; + + /* take 10 samples */ + for (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), _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 +bool +test_arithmetic(boost::atomic & shared_value, size_t instance) +{ + size_t shift = instance * 8; + value_type mask = 0xff << shift; + value_type increment = 1 << shift; + + value_type expected = 0; + + for (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 (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 +bool +test_bitops(boost::atomic & shared_value, size_t instance) +{ + size_t shift = instance * 8; + value_type mask = 0xff << shift; + + value_type expected = 0; + + for (size_t k = 0; k < 8; k++) { + value_type mod = 1 << 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 (size_t k = 0; k < 8; k++) { + value_type tmp = shared_value.fetch_and( ~ (1 << (shift + k)), boost::memory_order_relaxed); + if ( (tmp & mask) != (expected << shift)) + return false; + expected = expected & ~(1< 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, boost::ref(value), _1), + tmp + ); + + BOOST_TEST(success); // concurrent arithmetic error + } + + { + boost::atomic 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, boost::ref(value), _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 00000000..1a68a707 --- /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 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 +#include +#include +#include + +int main(int, char *[]) +{ + static_assert(std::is_convertible< int, boost::atomic< int > >::value, "boost::atomic 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 00000000..dfb42f61 --- /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 + +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 00000000..6ccf44aa --- /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 + +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 00000000..7c2d8fc2 --- /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 + +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 00000000..e2ec5748 --- /dev/null +++ b/src/boost/libs/atomic/test/fallback_api.cpp @@ -0,0 +1,74 @@ +// 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) + +/* force fallback implementation using locks */ +#define BOOST_ATOMIC_FORCE_FALLBACK 1 + +#include +#include + +#include "api_test_helpers.hpp" + +int main(int, char *[]) +{ + test_flag_api(); + + test_integral_api(); + test_integral_api(); + test_integral_api(); + test_integral_api(); + test_integral_api(); + test_integral_api(); + test_integral_api(); + test_integral_api(); + test_integral_api(); + test_integral_api(); + test_integral_api(); + test_integral_api(); + test_integral_api(); + test_integral_api(); + test_integral_api(); + test_integral_api(); + test_integral_api(); + test_integral_api(); + test_integral_api(); +#if defined(BOOST_HAS_INT128) + test_integral_api(); + test_integral_api(); +#endif + +#if !defined(BOOST_ATOMIC_NO_FLOATING_POINT) + test_floating_point_api(); + test_floating_point_api(); + test_floating_point_api(); +#if (defined(BOOST_HAS_INT128) || !defined(BOOST_NO_ALIGNMENT)) && defined(BOOST_HAS_FLOAT128) + test_floating_point_api(); +#endif +#endif + + test_pointer_api(); + + test_enum_api(); + + test_struct_api >(); + test_struct_api >(); + test_struct_api >(); + test_struct_api >(); + + // https://svn.boost.org/trac/boost/ticket/10994 + test_struct_x2_api >(); + + // https://svn.boost.org/trac/boost/ticket/9985 + test_struct_api >(); + + test_large_struct_api(); + + // Test that boost::atomic only requires T to be trivially copyable. + // Other non-trivial constructors are allowed. + test_struct_with_ctor_api(); + + 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 00000000..b1ef72b8 --- /dev/null +++ b/src/boost/libs/atomic/test/lockfree.cpp @@ -0,0 +1,225 @@ +// 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) + +// 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 + +#include +#include +#include + +static const char * lock_free_level[] = { + "never", + "sometimes", + "always" +}; + +template +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 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(boost::atomic::is_always_lock_free == (lock_free_expect == 2)); + + std::cout << "atomic<" << type_name << "> is " << lock_free_level[lock_free_macro_val] << " lock free\n"; +} + +#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) && (defined(BOOST_HAS_INT128) || !defined(BOOST_NO_ALIGNMENT)) +#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", BOOST_ATOMIC_CHAR_LOCK_FREE, EXPECT_CHAR_LOCK_FREE); + verify_lock_free("short", BOOST_ATOMIC_SHORT_LOCK_FREE, EXPECT_SHORT_LOCK_FREE); + verify_lock_free("int", BOOST_ATOMIC_INT_LOCK_FREE, EXPECT_INT_LOCK_FREE); + verify_lock_free("long", BOOST_ATOMIC_LONG_LOCK_FREE, EXPECT_LONG_LOCK_FREE); +#ifdef BOOST_HAS_LONG_LONG + verify_lock_free("long long", BOOST_ATOMIC_LLONG_LOCK_FREE, EXPECT_LLONG_LOCK_FREE); +#endif +#ifdef BOOST_HAS_INT128 + verify_lock_free("int128", BOOST_ATOMIC_INT128_LOCK_FREE, EXPECT_INT128_LOCK_FREE); +#endif + verify_lock_free("void *", BOOST_ATOMIC_POINTER_LOCK_FREE, EXPECT_SHORT_LOCK_FREE); + verify_lock_free("bool", BOOST_ATOMIC_BOOL_LOCK_FREE, EXPECT_BOOL_LOCK_FREE); + +#ifndef BOOST_ATOMIC_NO_FLOATING_POINT + + verify_lock_free("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", 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", 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("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 00000000..faefc24a --- /dev/null +++ b/src/boost/libs/atomic/test/native_api.cpp @@ -0,0 +1,82 @@ +// 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) + +#include + +#include +#include + +#include "api_test_helpers.hpp" + +int main(int, char *[]) +{ + test_flag_api(); + + test_integral_api(); + test_integral_api(); + test_integral_api(); + test_integral_api(); + test_integral_api(); + test_integral_api(); + test_integral_api(); + test_integral_api(); + test_integral_api(); + test_integral_api(); + test_integral_api(); + test_integral_api(); + test_integral_api(); + test_integral_api(); + test_integral_api(); + test_integral_api(); + test_integral_api(); + test_integral_api(); + test_integral_api(); +#if defined(BOOST_HAS_INT128) + test_integral_api(); + test_integral_api(); +#endif + + test_constexpr_ctor(); + test_constexpr_ctor(); + test_constexpr_ctor(); + test_constexpr_ctor(); + test_constexpr_ctor(); + +#if !defined(BOOST_ATOMIC_NO_FLOATING_POINT) + test_floating_point_api(); + test_floating_point_api(); + test_floating_point_api(); +#if (defined(BOOST_HAS_INT128) || !defined(BOOST_NO_ALIGNMENT)) && defined(BOOST_HAS_FLOAT128) + test_floating_point_api(); +#endif +#endif + + test_pointer_api(); + + test_enum_api(); + + test_struct_api >(); + test_struct_api >(); + test_struct_api >(); + test_struct_api >(); +#if defined(BOOST_HAS_INT128) + test_struct_api >(); +#endif + + // https://svn.boost.org/trac/boost/ticket/10994 + test_struct_x2_api >(); + + // https://svn.boost.org/trac/boost/ticket/9985 + test_struct_api >(); + + test_large_struct_api(); + + // Test that boost::atomic only requires T to be trivially copyable. + // Other non-trivial constructors are allowed. + test_struct_with_ctor_api(); + + 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 00000000..e9dcb4fc --- /dev/null +++ b/src/boost/libs/atomic/test/ordering.cpp @@ -0,0 +1,256 @@ +// 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 + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// 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 +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); + + boost::atomic 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 b_; + + char pad2_[512]; + boost::barrier barrier_; + + int vrfyb1_, vrfya2_; + + boost::atomic terminate_threads_; + boost::atomic termination_consensus_; + + bool detected_conflict_; + boost::mutex m_; + boost::condition_variable c_; +}; + +template +total_store_order_test::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 +void +total_store_order_test::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::mutex::scoped_lock 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 +void +total_store_order_test::thread1fn(void) +{ + for (;;) { + 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)) for (;;) { + 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 +void +total_store_order_test::thread2fn(void) +{ + for (;;) { + 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)) for (;;) { + 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 +void +total_store_order_test::check_conflict(void) +{ + if (vrfyb1_ == 0 && vrfya2_ == 0) { + boost::mutex::scoped_lock 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 (size_t n = 0; n < 10; n++) { + boost::posix_time::time_duration timeout(0, 0, 10); + + total_store_order_test 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 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 00000000..31170bde --- /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 00000000..98d8453a --- /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 + +struct Dummy +{ + int x[128]; +}; + +int main() +{ + Dummy d = {}; + boost::atomic 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 00000000..32180a7d --- /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 +#include + +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_ -- cgit v1.2.3