diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-27 18:24:20 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-27 18:24:20 +0000 |
commit | 483eb2f56657e8e7f419ab1a4fab8dce9ade8609 (patch) | |
tree | e5d88d25d870d5dedacb6bbdbe2a966086a0a5cf /src/boost/libs/atomic | |
parent | Initial commit. (diff) | |
download | ceph-483eb2f56657e8e7f419ab1a4fab8dce9ade8609.tar.xz ceph-483eb2f56657e8e7f419ab1a4fab8dce9ade8609.zip |
Adding upstream version 14.2.21.upstream/14.2.21upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to '')
20 files changed, 2539 insertions, 0 deletions
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 + <threading>multi + <link>shared:<define>BOOST_ATOMIC_DYN_LINK=1 + <link>static:<define>BOOST_ATOMIC_STATIC_LINK=1 + <define>BOOST_ATOMIC_SOURCE + <target-os>windows:<define>BOOST_USE_WINDOWS_H + <target-os>windows:<define>_WIN32_WINNT=0x0500 + <toolset>gcc,<target-os>windows:<linkflags>"-lkernel32" + : usage-requirements + <link>shared:<define>BOOST_ATOMIC_DYN_LINK=1 + <link>static:<define>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 @@ +<html> +<head> +<meta http-equiv="refresh" content="0; URL=../../doc/html/atomic.html"> +</head> +<body> +Automatic redirection failed, please go to +<a href="../../doc/html/atomic.html">../../doc/html/atomic.html</a> <hr> +<p>© Copyright Beman Dawes, 2001</p> +<p>Distributed under the Boost Software License, Version 1.0. (See accompanying +file <a href="../../LICENSE_1_0.txt">LICENSE_1_0.txt</a> or copy +at <a href="http://www.boost.org/LICENSE_1_0.txt">www.boost.org/LICENSE_1_0.txt</a>)</p> +</body> +</html> 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 <hcb -at- chaoticmind.net>", + "Tim Blechmann <tim -at- klingt.org>", + "Andrey Semashev <andrey.semashev -at- gmail.com>" + ] +} 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 <cstddef> +#include <boost/config.hpp> +#include <boost/assert.hpp> +#include <boost/memory_order.hpp> +#include <boost/atomic/capabilities.hpp> + +#if BOOST_ATOMIC_FLAG_LOCK_FREE == 2 +#include <boost/atomic/detail/operations_lockfree.hpp> +#elif !defined(BOOST_HAS_PTHREADS) +#error Boost.Atomic: Unsupported target platform, POSIX threads are required when native atomic operations are not available +#else +#include <pthread.h> +#define BOOST_ATOMIC_USE_PTHREAD +#endif + +#include <boost/atomic/detail/lockpool.hpp> +#include <boost/atomic/detail/pause.hpp> + +#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 + <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 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 <boost/atomic.hpp> +#include <cstddef> +#include <cstring> +#include <limits> +#include <iostream> +#include <boost/config.hpp> +#include <boost/cstdint.hpp> +#include <boost/type_traits/integral_constant.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> + +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" + +/* 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<typename T> +void test_base_operators(T value1, T value2, T value3) +{ + /* explicit load/store */ + { + boost::atomic<T> a(value1); + BOOST_TEST_EQ( a.load(), value1 ); + } + + { + boost::atomic<T> a(value1); + a.store(value2); + BOOST_TEST_EQ( a.load(), value2 ); + } + + /* overloaded assignment/conversion */ + { + boost::atomic<T> a(value1); + BOOST_TEST( value1 == a ); + } + + { + boost::atomic<T> a; + a = value2; + BOOST_TEST( value2 == a ); + } + + /* exchange-type operators */ + { + boost::atomic<T> a(value1); + T n = a.exchange(value2); + BOOST_TEST_EQ( a.load(), value2 ); + BOOST_TEST_EQ( n, value1 ); + } + + { + boost::atomic<T> 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<T> 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<T> 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<T> 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 <typename T> +void test_constexpr_ctor() +{ +#ifndef BOOST_NO_CXX11_CONSTEXPR + const T value(0); + const 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, + 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<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; + { + boost::atomic<T> 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<T> 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<T> 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<T> 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<T> 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<T> 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<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 */ + { + boost::atomic<T> a(value); + T n = a.fetch_add(delta); + BOOST_TEST_EQ( a.load(), T((AddType)value + delta) ); + BOOST_TEST_EQ( n, value ); + } + + { + boost::atomic<T> 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<T> 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<T> 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<T> a(value); + T n = a++; + BOOST_TEST_EQ( a.load(), T((AddType)value + 1) ); + BOOST_TEST_EQ( n, value ); + } + + { + boost::atomic<T> a(value); + T n = ++a; + BOOST_TEST_EQ( a.load(), T((AddType)value + 1) ); + BOOST_TEST_EQ( n, T((AddType)value + 1) ); + } + + { + boost::atomic<T> a(value); + T n = a--; + BOOST_TEST_EQ( a.load(), T((AddType)value - 1) ); + BOOST_TEST_EQ( n, value ); + } + + { + boost::atomic<T> 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<T> 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<T> 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<T> a(value); + a.opaque_add(delta); + BOOST_TEST_EQ( a.load(), T((AddType)value + delta) ); + } + + { + boost::atomic<T> 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<typename T, typename D> +void test_additive_operators(T value, D delta) +{ + test_additive_operators_with_type<T, D, T>(value, delta); +} + +template< typename T > +void test_negation() +{ + { + boost::atomic<T> 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<T> 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<T> 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<T> 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<T> a((T)0); + bool f = a.negate_and_test(); + BOOST_TEST_EQ( f, false ); + BOOST_TEST_EQ( a.load(), (T)0 ); + } +} + +template<typename T> +void test_additive_wrap(T value) +{ + { + boost::atomic<T> a(value); + T n = a.fetch_add(1) + (T)1; + BOOST_TEST_EQ( a.load(), n ); + } + { + boost::atomic<T> a(value); + T n = a.fetch_sub(1) - (T)1; + BOOST_TEST_EQ( a.load(), n ); + } +} + +template<typename T> +void test_bit_operators(T value, T delta) +{ + /* explicit and/or/xor */ + { + boost::atomic<T> a(value); + T n = a.fetch_and(delta); + BOOST_TEST_EQ( a.load(), T(value & delta) ); + BOOST_TEST_EQ( n, value ); + } + + { + boost::atomic<T> a(value); + T n = a.fetch_or(delta); + BOOST_TEST_EQ( a.load(), T(value | delta) ); + BOOST_TEST_EQ( n, value ); + } + + { + boost::atomic<T> a(value); + T n = a.fetch_xor(delta); + BOOST_TEST_EQ( a.load(), T(value ^ delta) ); + BOOST_TEST_EQ( n, value ); + } + + { + boost::atomic<T> a(value); + T n = a.fetch_complement(); + BOOST_TEST_EQ( a.load(), T(~value) ); + BOOST_TEST_EQ( n, value ); + } + + /* overloaded modify/assign */ + { + boost::atomic<T> a(value); + T n = (a &= delta); + BOOST_TEST_EQ( a.load(), T(value & delta) ); + BOOST_TEST_EQ( n, T(value & delta) ); + } + + { + boost::atomic<T> a(value); + T n = (a |= delta); + BOOST_TEST_EQ( a.load(), T(value | delta) ); + BOOST_TEST_EQ( n, T(value | delta) ); + } + + { + boost::atomic<T> 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<T> 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<T> 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<T> 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<T> a(value); + T n = a.bitwise_complement(); + BOOST_TEST_EQ( a.load(), T(~value) ); + BOOST_TEST_EQ( n, T(~value) ); + } + + // Opaque operations + { + boost::atomic<T> a(value); + a.opaque_and(delta); + BOOST_TEST_EQ( a.load(), T(value & delta) ); + } + + { + boost::atomic<T> a(value); + a.opaque_or(delta); + BOOST_TEST_EQ( a.load(), T(value | delta) ); + } + + { + boost::atomic<T> a(value); + a.opaque_xor(delta); + BOOST_TEST_EQ( a.load(), T(value ^ delta) ); + } + + { + boost::atomic<T> a(value); + a.opaque_complement(); + BOOST_TEST_EQ( a.load(), T(~value) ); + } + + // Modify and test operations + { + boost::atomic<T> 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<T> 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<T> 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<T> 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<T> 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<T> 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<T> 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<typename T> +void do_test_integral_api(boost::false_type) +{ + BOOST_TEST(sizeof(boost::atomic<T>) >= sizeof(T)); + + test_base_operators<T>(42, 43, 44); + test_additive_operators<T, T>(42, 17); + test_bit_operators<T>((T)0x5f5f5f5f5f5f5f5fULL, (T)0xf5f5f5f5f5f5f5f5ULL); + + /* test for unsigned overflow/underflow */ + test_additive_operators<T, T>((T)-1, 1); + test_additive_operators<T, T>(0, 1); + /* test for signed overflow/underflow */ + test_additive_operators<T, T>(((T)-1) >> (sizeof(T) * 8 - 1), 1); + test_additive_operators<T, T>(1 + (((T)-1) >> (sizeof(T) * 8 - 1)), 1); +} + +template<typename T> +void do_test_integral_api(boost::true_type) +{ + do_test_integral_api<T>(boost::false_type()); + + test_additive_wrap<T>(0u); + BOOST_CONSTEXPR_OR_CONST T all_ones = ~(T)0u; + test_additive_wrap<T>(all_ones); + BOOST_CONSTEXPR_OR_CONST T max_signed_twos_compl = all_ones >> 1; + test_additive_wrap<T>(all_ones ^ max_signed_twos_compl); + test_additive_wrap<T>(max_signed_twos_compl); +} + +template<typename T> +inline void test_integral_api(void) +{ + do_test_integral_api<T>(boost::is_unsigned<T>()); + + if (boost::is_signed<T>::value) + test_negation<T>(); +} + +#if !defined(BOOST_ATOMIC_NO_FLOATING_POINT) + +template<typename T, typename D> +void test_fp_additive_operators(T value, D delta) +{ + /* explicit add/sub */ + { + boost::atomic<T> 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<T> 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<T> 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<T> 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<T> 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<T> 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<T> a(value); + a.opaque_add(delta); + BOOST_TEST_EQ( a.load(), approx(T(value + delta)) ); + } + + { + boost::atomic<T> a(value); + a.opaque_sub(delta); + BOOST_TEST_EQ( a.load(), approx(T(value - delta)) ); + } +} + +template< typename T > +void test_fp_negation() +{ + { + boost::atomic<T> 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<T> 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<T> 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<typename T> +inline void test_floating_point_api(void) +{ + BOOST_TEST(sizeof(boost::atomic<T>) >= 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<T>(static_cast<T>(42.1), static_cast<T>(43.2), static_cast<T>(44.3)); + + test_fp_additive_operators<T, T>(static_cast<T>(42.5), static_cast<T>(17.7)); + test_fp_additive_operators<T, T>(static_cast<T>(-42.5), static_cast<T>(-17.7)); + + test_fp_negation<T>(); +#endif +} + + +template<typename T> +void test_pointer_api(void) +{ + BOOST_TEST_GE( sizeof(boost::atomic<T *>), sizeof(T *)); + BOOST_TEST_GE( sizeof(boost::atomic<void *>), sizeof(T *)); + + T values[3]; + + test_base_operators<T*>(&values[0], &values[1], &values[2]); + test_additive_operators<T*>(&values[1], 1); + + test_base_operators<void*>(&values[0], &values[1], &values[2]); + +#if defined(BOOST_HAS_INTPTR_T) + boost::atomic<void *> ptr; + boost::atomic<boost::intptr_t> 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<typename T> +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<typename T> +void +test_struct_api(void) +{ + T a = {1}, b = {2}, c = {3}; + + test_base_operators(a, b, c); + + { + boost::atomic<T> sa; + boost::atomic<typename T::value_type> si; + 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==(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<typename T> +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<test_struct_with_ctor> 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 <boost/atomic.hpp> + +#include <algorithm> +#include <boost/ref.hpp> +#include <boost/bind.hpp> +#include <boost/function.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/locks.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(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(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::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<bool(size_t)> 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<bool> 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<typename value_type, size_t shift_> +bool +test_arithmetic(boost::atomic<value_type> & 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<typename value_type, size_t shift_> +bool +test_bitops(boost::atomic<value_type> & 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<<k); + } + for (size_t k = 0; k < 8; k++) { + value_type mod = 255 ^ (1 << 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), _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), _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<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 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 <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 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 <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 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 <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 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 <boost/atomic.hpp> +#include <boost/cstdint.hpp> + +#include "api_test_helpers.hpp" + +int main(int, char *[]) +{ + test_flag_api(); + + test_integral_api<char>(); + test_integral_api<signed char>(); + test_integral_api<unsigned char>(); + test_integral_api<boost::uint8_t>(); + test_integral_api<boost::int8_t>(); + test_integral_api<short>(); + test_integral_api<unsigned short>(); + test_integral_api<boost::uint16_t>(); + test_integral_api<boost::int16_t>(); + test_integral_api<int>(); + test_integral_api<unsigned int>(); + test_integral_api<boost::uint32_t>(); + test_integral_api<boost::int32_t>(); + test_integral_api<long>(); + test_integral_api<unsigned long>(); + test_integral_api<boost::uint64_t>(); + test_integral_api<boost::int64_t>(); + test_integral_api<long long>(); + test_integral_api<unsigned long long>(); +#if defined(BOOST_HAS_INT128) + test_integral_api<boost::int128_type>(); + test_integral_api<boost::uint128_type>(); +#endif + +#if !defined(BOOST_ATOMIC_NO_FLOATING_POINT) + test_floating_point_api<float>(); + test_floating_point_api<double>(); + test_floating_point_api<long double>(); +#if (defined(BOOST_HAS_INT128) || !defined(BOOST_NO_ALIGNMENT)) && defined(BOOST_HAS_FLOAT128) + test_floating_point_api<boost::float128_type>(); +#endif +#endif + + test_pointer_api<int>(); + + test_enum_api(); + + test_struct_api<test_struct<boost::uint8_t> >(); + test_struct_api<test_struct<boost::uint16_t> >(); + test_struct_api<test_struct<boost::uint32_t> >(); + test_struct_api<test_struct<boost::uint64_t> >(); + + // https://svn.boost.org/trac/boost/ticket/10994 + test_struct_x2_api<test_struct_x2<boost::uint64_t> >(); + + // https://svn.boost.org/trac/boost/ticket/9985 + test_struct_api<test_struct<double> >(); + + test_large_struct_api(); + + // Test that boost::atomic<T> 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 <boost/atomic.hpp> + +#include <iostream> +#include <boost/config.hpp> +#include <boost/core/lightweight_test.hpp> + +static const char * 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(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"; +} + +#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>("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 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 <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<char>(); + test_integral_api<signed char>(); + test_integral_api<unsigned char>(); + test_integral_api<boost::uint8_t>(); + test_integral_api<boost::int8_t>(); + test_integral_api<short>(); + test_integral_api<unsigned short>(); + test_integral_api<boost::uint16_t>(); + test_integral_api<boost::int16_t>(); + test_integral_api<int>(); + test_integral_api<unsigned int>(); + test_integral_api<boost::uint32_t>(); + test_integral_api<boost::int32_t>(); + test_integral_api<long>(); + test_integral_api<unsigned long>(); + test_integral_api<boost::uint64_t>(); + test_integral_api<boost::int64_t>(); + test_integral_api<long long>(); + test_integral_api<unsigned long long>(); +#if defined(BOOST_HAS_INT128) + test_integral_api<boost::int128_type>(); + test_integral_api<boost::uint128_type>(); +#endif + + test_constexpr_ctor<char>(); + test_constexpr_ctor<short>(); + test_constexpr_ctor<int>(); + test_constexpr_ctor<long>(); + test_constexpr_ctor<int*>(); + +#if !defined(BOOST_ATOMIC_NO_FLOATING_POINT) + test_floating_point_api<float>(); + test_floating_point_api<double>(); + test_floating_point_api<long double>(); +#if (defined(BOOST_HAS_INT128) || !defined(BOOST_NO_ALIGNMENT)) && defined(BOOST_HAS_FLOAT128) + test_floating_point_api<boost::float128_type>(); +#endif +#endif + + test_pointer_api<int>(); + + test_enum_api(); + + test_struct_api<test_struct<boost::uint8_t> >(); + test_struct_api<test_struct<boost::uint16_t> >(); + test_struct_api<test_struct<boost::uint32_t> >(); + test_struct_api<test_struct<boost::uint64_t> >(); +#if defined(BOOST_HAS_INT128) + test_struct_api<test_struct<boost::uint128_type> >(); +#endif + + // https://svn.boost.org/trac/boost/ticket/10994 + test_struct_x2_api<test_struct_x2<boost::uint64_t> >(); + + // https://svn.boost.org/trac/boost/ticket/9985 + test_struct_api<test_struct<double> >(); + + test_large_struct_api(); + + // Test that boost::atomic<T> 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 <boost/atomic.hpp> + +#include <boost/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/locks.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); + + 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::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<boost::memory_order store_order, boost::memory_order load_order> +void +total_store_order_test<store_order, load_order>::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<boost::memory_order store_order, boost::memory_order load_order> +void +total_store_order_test<store_order, load_order>::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<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::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<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 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 <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 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 <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_ |