diff --git a/doc/atomic.qbk b/doc/atomic.qbk index 38a720c..abf724f 100644 --- a/doc/atomic.qbk +++ b/doc/atomic.qbk @@ -375,6 +375,9 @@ The following macros affect library behavior: of this instruction, so running the library code on older CPUs will result in crashes, unless this macro is defined. Note that the macro does not affect MSVC, GCC and compatible compilers because the library infers this information from the compiler-defined macros.]] + [[`BOOST_ATOMIC_NO_FLOATING_POINT`] [When defined, support for floating point operations is disabled. + Floating point types shall be treated similar to trivially copyable structs and no capability macros + will be defined.]] [[`BOOST_ATOMIC_FORCE_FALLBACK`] [When defined, all operations are implemented with locks. This is mostly used for testing and should not be used in real world projects.]] [[`BOOST_ATOMIC_DYN_LINK` and `BOOST_ALL_DYN_LINK`] [Control library linking. If defined, @@ -535,7 +538,8 @@ examples of the types compatible with this requirement: Note that classes with virtual functions or virtual base classes do not satisfy the requirements. Also be warned that structures with "padding" between data members may compare -non-equal via [^memcmp] even though all members are equal. +non-equal via [^memcmp] even though all members are equal. This may also be +the case with some floating point types, which include padding bits themselves. [section:interface_atomic_generic [^boost::atomic<['T]>] template class] @@ -780,9 +784,90 @@ constraint which always defaults to `memory_order_seq_cst`. [endsect] +[section:interface_atomic_floating_point [^boost::atomic<['floating-point]>] template class] + +[note The support for floating point types is optional and can be disabled by defining `BOOST_ATOMIC_NO_FLOATING_POINT`.] + +In addition to the operations applicable to all atomic objects, +[^boost::atomic<['F]>] for floating point +types [^['F]] supports the following operations, +which correspond to [^std::atomic<['F]>]: + +[table + [[Syntax] [Description]] + [ + [`F fetch_add(F v, memory_order order)`] + [Add `v` to variable, returning previous value] + ] + [ + [`F fetch_sub(F v, memory_order order)`] + [Subtract `v` from variable, returning previous value] + ] +] + +Additionally, as a [*Boost.Atomic] extension, the following operations are also provided: + +[table + [[Syntax] [Description]] + [ + [`F fetch_negate(memory_order order)`] + [Change the sign of the value stored in the variable, returning previous value] + ] + [ + [`F negate(memory_order order)`] + [Change the sign of the value stored in the variable, returning the result] + ] + [ + [`F add(F v, memory_order order)`] + [Add `v` to variable, returning the result] + ] + [ + [`F sub(F v, memory_order order)`] + [Subtract `v` from variable, returning the result] + ] + [ + [`void opaque_negate(memory_order order)`] + [Change the sign of the value stored in the variable, returning nothing] + ] + [ + [`void opaque_add(F v, memory_order order)`] + [Add `v` to variable, returning nothing] + ] + [ + [`void opaque_sub(F v, memory_order order)`] + [Subtract `v` from variable, returning nothing] + ] +] + +`order` always has `memory_order_seq_cst` as default parameter. + +The [^opaque_['op]] variants of the operations +may result in a more efficient code on some architectures because +the original value of the atomic variable is not preserved. + +In addition to these explicit operations, each +[^boost::atomic<['F]>] object also supports operators `+=` and `-=`. +Avoid using these operators, as they do not allow to specify a memory ordering +constraint which always defaults to `memory_order_seq_cst`. + +When using atomic operations with floating point types, bear in mind that [*Boost.Atomic] +always performs bitwise comparison of the stored values. This means that operations like +`compare_exchange*` may fail if the stored value and comparand have different binary representation, +even if they would normally compare equal. This is typically the case when either of the numbers +is [@https://en.wikipedia.org/wiki/Denormal_number denormalized]. This also means that the behavior +with regard to special floating point values like NaN and signed zero is also different from normal C++. + +Another source of the problem is padding bits that are added to some floating point types for alignment. +One wide-spread example of that is Intel x87 extended double format, which is typically stored as 80 bits +of value padded with 16 or 48 unused bits. These padding bits are often uninitialized and contain garbage, +which makes two equal numbers have different binary representation. The library accounts for the known +such cases, but it is possible that some exotic platforms exist that are not covered yet. + +[endsect] + [section:interface_atomic_pointer [^boost::atomic<['pointer]>] template class] -In addition to the operations applicable to all atomic object, +In addition to the operations applicable to all atomic objects, [^boost::atomic<['P]>] for pointer types [^['P]] (other than pointers to [^void], function or member pointers) support the following operations, which correspond to [^std::atomic<['P]>]: @@ -1027,6 +1112,26 @@ to indicate whether the corresponding operations are lock-free or not. In the table above, `intN_type` is a type that fits storage of contiguous `N` bits, suitably aligned for atomic operations. +For floating-point types the following macros are similarly defined: + +[table + [[Macro] [Description]] + [ + [`BOOST_ATOMIC_FLOAT_LOCK_FREE`] + [Indicate whether `atomic` is lock-free.] + ] + [ + [`BOOST_ATOMIC_DOUBLE_LOCK_FREE`] + [Indicate whether `atomic` is lock-free.] + ] + [ + [`BOOST_ATOMIC_LONG_DOUBLE_LOCK_FREE`] + [Indicate whether `atomic` is lock-free.] + ] +] + +These macros are not defined when support for floating point types is disabled by user. + [endsect] [endsect] diff --git a/include/boost/atomic/atomic.hpp b/include/boost/atomic/atomic.hpp index 2793dde..5a80588 100644 --- a/include/boost/atomic/atomic.hpp +++ b/include/boost/atomic/atomic.hpp @@ -22,6 +22,10 @@ #include #include #include +#if !defined(BOOST_ATOMIC_NO_FLOATING_POINT) +#include +#include +#endif #ifdef BOOST_HAS_PRAGMA_ONCE #pragma once @@ -81,6 +85,12 @@ using atomics::atomic_uint_fast64_t; using atomics::atomic_intmax_t; using atomics::atomic_uintmax_t; +#if !defined(BOOST_ATOMIC_NO_FLOATING_POINT) +using atomics::atomic_float_t; +using atomics::atomic_double_t; +using atomics::atomic_long_double_t; +#endif + using atomics::atomic_size_t; using atomics::atomic_ptrdiff_t; diff --git a/include/boost/atomic/capabilities.hpp b/include/boost/atomic/capabilities.hpp index 7e5205d..5c7434d 100644 --- a/include/boost/atomic/capabilities.hpp +++ b/include/boost/atomic/capabilities.hpp @@ -17,6 +17,9 @@ #include #include #include +#if !defined(BOOST_ATOMIC_NO_FLOATING_POINT) +#include +#endif #if !defined(BOOST_ATOMIC_EMULATED) #include BOOST_ATOMIC_DETAIL_BACKEND_HEADER(boost/atomic/detail/caps_) @@ -150,6 +153,52 @@ #define BOOST_ATOMIC_FLAG_LOCK_FREE BOOST_ATOMIC_BOOL_LOCK_FREE #endif +#if !defined(BOOST_ATOMIC_NO_FLOATING_POINT) + +#if !defined(BOOST_ATOMIC_FLOAT_LOCK_FREE) && defined(BOOST_ATOMIC_DETAIL_SIZEOF_FLOAT) +#if BOOST_ATOMIC_DETAIL_SIZEOF_FLOAT == 2 +#define BOOST_ATOMIC_FLOAT_LOCK_FREE BOOST_ATOMIC_INT16_LOCK_FREE +#elif BOOST_ATOMIC_DETAIL_SIZEOF_FLOAT == 4 +#define BOOST_ATOMIC_FLOAT_LOCK_FREE BOOST_ATOMIC_INT32_LOCK_FREE +#elif BOOST_ATOMIC_DETAIL_SIZEOF_FLOAT == 8 +#define BOOST_ATOMIC_FLOAT_LOCK_FREE BOOST_ATOMIC_INT64_LOCK_FREE +#elif BOOST_ATOMIC_DETAIL_SIZEOF_FLOAT == 16 +#define BOOST_ATOMIC_FLOAT_LOCK_FREE BOOST_ATOMIC_INT128_LOCK_FREE +#else +#define BOOST_ATOMIC_FLOAT_LOCK_FREE 0 +#endif +#endif + +#if !defined(BOOST_ATOMIC_DOUBLE_LOCK_FREE) && defined(BOOST_ATOMIC_DETAIL_SIZEOF_DOUBLE) +#if BOOST_ATOMIC_DETAIL_SIZEOF_DOUBLE == 2 +#define BOOST_ATOMIC_DOUBLE_LOCK_FREE BOOST_ATOMIC_INT16_LOCK_FREE +#elif BOOST_ATOMIC_DETAIL_SIZEOF_DOUBLE == 4 +#define BOOST_ATOMIC_DOUBLE_LOCK_FREE BOOST_ATOMIC_INT32_LOCK_FREE +#elif BOOST_ATOMIC_DETAIL_SIZEOF_DOUBLE == 8 +#define BOOST_ATOMIC_DOUBLE_LOCK_FREE BOOST_ATOMIC_INT64_LOCK_FREE +#elif BOOST_ATOMIC_DETAIL_SIZEOF_DOUBLE == 16 +#define BOOST_ATOMIC_DOUBLE_LOCK_FREE BOOST_ATOMIC_INT128_LOCK_FREE +#else +#define BOOST_ATOMIC_DOUBLE_LOCK_FREE 0 +#endif +#endif + +#if !defined(BOOST_ATOMIC_LONG_DOUBLE_LOCK_FREE) && defined(BOOST_ATOMIC_DETAIL_SIZEOF_LONG_DOUBLE) +#if BOOST_ATOMIC_DETAIL_SIZEOF_LONG_DOUBLE == 2 +#define BOOST_ATOMIC_LONG_DOUBLE_LOCK_FREE BOOST_ATOMIC_INT16_LOCK_FREE +#elif BOOST_ATOMIC_DETAIL_SIZEOF_LONG_DOUBLE == 4 +#define BOOST_ATOMIC_LONG_DOUBLE_LOCK_FREE BOOST_ATOMIC_INT32_LOCK_FREE +#elif BOOST_ATOMIC_DETAIL_SIZEOF_LONG_DOUBLE == 8 +#define BOOST_ATOMIC_LONG_DOUBLE_LOCK_FREE BOOST_ATOMIC_INT64_LOCK_FREE +#elif BOOST_ATOMIC_DETAIL_SIZEOF_LONG_DOUBLE == 16 +#define BOOST_ATOMIC_LONG_DOUBLE_LOCK_FREE BOOST_ATOMIC_INT128_LOCK_FREE +#else +#define BOOST_ATOMIC_LONG_DOUBLE_LOCK_FREE 0 +#endif +#endif + +#endif // !defined(BOOST_ATOMIC_NO_FLOATING_POINT) + #ifndef BOOST_ATOMIC_THREAD_FENCE #define BOOST_ATOMIC_THREAD_FENCE 0 #endif diff --git a/include/boost/atomic/detail/addressof.hpp b/include/boost/atomic/detail/addressof.hpp new file mode 100644 index 0000000..87e1534 --- /dev/null +++ b/include/boost/atomic/detail/addressof.hpp @@ -0,0 +1,42 @@ +/* + * 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) 2018 Andrey Semashev + */ +/*! + * \file atomic/detail/addressof.hpp + * + * This header defines \c addressof helper function + */ + +#ifndef BOOST_ATOMIC_DETAIL_ADDRESSOF_HPP_INCLUDED_ +#define BOOST_ATOMIC_DETAIL_ADDRESSOF_HPP_INCLUDED_ + +#include + +#ifdef BOOST_HAS_PRAGMA_ONCE +#pragma once +#endif + +namespace boost { +namespace atomics { +namespace detail { + +template< typename T > +BOOST_FORCEINLINE T* addressof(T& value) BOOST_NOEXCEPT +{ + // Note: The point of using a local struct as the intermediate type instead of char is to avoid gcc warnings + // if T is a const volatile char*: + // warning: casting 'const volatile char* const' to 'const volatile char&' does not dereference pointer + // The local struct makes sure T is not related to the cast target type. + struct opaque_type; + return reinterpret_cast< T* >(&const_cast< opaque_type& >(reinterpret_cast< const volatile opaque_type& >(value))); +} + +} // namespace detail +} // namespace atomics +} // namespace boost + +#endif // BOOST_ATOMIC_DETAIL_ADDRESSOF_HPP_INCLUDED_ diff --git a/include/boost/atomic/detail/atomic_template.hpp b/include/boost/atomic/detail/atomic_template.hpp index 57244d8..bf65add 100644 --- a/include/boost/atomic/detail/atomic_template.hpp +++ b/include/boost/atomic/detail/atomic_template.hpp @@ -28,8 +28,14 @@ #include #include #include +#include #include #include +#if !defined(BOOST_ATOMIC_NO_FLOATING_POINT) +#include +#include +#include +#endif #ifdef BOOST_HAS_PRAGMA_ONCE #pragma once @@ -74,32 +80,37 @@ struct classify_pointer< T, true > typedef void type; }; -template< typename T, bool IsInt = atomics::detail::is_integral< T >::value > +template< typename T, bool IsInt = atomics::detail::is_integral< T >::value, bool IsFloat = atomics::detail::is_floating_point< T >::value > struct classify { typedef void type; }; template< typename T > -struct classify< T, true > { typedef int type; }; +struct classify< T, true, false > { typedef int type; }; + +#if !defined(BOOST_ATOMIC_NO_FLOATING_POINT) +template< typename T > +struct classify< T, false, true > { typedef float type; }; +#endif template< typename T > -struct classify< T*, false > { typedef typename classify_pointer< T >::type type; }; +struct classify< T*, false, false > { typedef typename classify_pointer< T >::type type; }; template< > -struct classify< void*, false > { typedef void type; }; +struct classify< void*, false, false > { typedef void type; }; template< > -struct classify< const void*, false > { typedef void type; }; +struct classify< const void*, false, false > { typedef void type; }; template< > -struct classify< volatile void*, false > { typedef void type; }; +struct classify< volatile void*, false, false > { typedef void type; }; template< > -struct classify< const volatile void*, false > { typedef void type; }; +struct classify< const volatile void*, false, false > { typedef void type; }; template< typename T, typename U > -struct classify< T U::*, false > { typedef void type; }; +struct classify< T U::*, false, false > { typedef void type; }; template< typename T, typename Kind > @@ -243,7 +254,7 @@ protected: typename operations::aligned_storage_type m_storage; public: - BOOST_DEFAULTED_FUNCTION(base_atomic(), {}) + BOOST_DEFAULTED_FUNCTION(base_atomic() BOOST_NOEXCEPT, {}) BOOST_CONSTEXPR explicit base_atomic(value_type v) BOOST_NOEXCEPT : m_storage(v) {} // Standard methods @@ -567,7 +578,7 @@ protected: operations::aligned_storage_type m_storage; public: - BOOST_DEFAULTED_FUNCTION(base_atomic(), {}) + BOOST_DEFAULTED_FUNCTION(base_atomic() BOOST_NOEXCEPT, {}) BOOST_CONSTEXPR explicit base_atomic(value_type v) BOOST_NOEXCEPT : m_storage(v) {} // Standard methods @@ -661,6 +672,185 @@ private: }; +#if !defined(BOOST_ATOMIC_NO_FLOATING_POINT) + +//! Implementation for floating point types +template< typename T > +class base_atomic< T, float > +{ +public: + typedef T value_type; + typedef T difference_type; + +protected: + typedef atomics::detail::operations< storage_size_of< value_type >::value, false > operations; + typedef atomics::detail::extra_operations< operations, operations::storage_size, operations::is_signed > extra_operations; + typedef atomics::detail::fp_operations< extra_operations, value_type, operations::storage_size > fp_operations; + typedef atomics::detail::extra_fp_operations< fp_operations, value_type, operations::storage_size > extra_fp_operations; + typedef value_type value_arg_type; + +public: + typedef typename operations::storage_type storage_type; + +private: + typedef atomics::detail::integral_constant< bool, sizeof(value_type) == sizeof(storage_type) > value_matches_storage; + +protected: + typename operations::aligned_storage_type m_storage; + +public: + BOOST_DEFAULTED_FUNCTION(base_atomic() BOOST_NOEXCEPT, {}) + BOOST_FORCEINLINE explicit base_atomic(value_type v) BOOST_NOEXCEPT : m_storage(atomics::detail::bitwise_fp_cast< storage_type >(v)) {} + + BOOST_FORCEINLINE void store(value_arg_type v, memory_order order = memory_order_seq_cst) volatile BOOST_NOEXCEPT + { + BOOST_ASSERT(order != memory_order_consume); + BOOST_ASSERT(order != memory_order_acquire); + BOOST_ASSERT(order != memory_order_acq_rel); + + operations::store(m_storage.value, atomics::detail::bitwise_fp_cast< storage_type >(v), order); + } + + BOOST_FORCEINLINE value_type load(memory_order order = memory_order_seq_cst) const volatile BOOST_NOEXCEPT + { + BOOST_ASSERT(order != memory_order_release); + BOOST_ASSERT(order != memory_order_acq_rel); + + return atomics::detail::bitwise_fp_cast< value_type >(operations::load(m_storage.value, order)); + } + + BOOST_FORCEINLINE value_type fetch_add(difference_type v, memory_order order = memory_order_seq_cst) volatile BOOST_NOEXCEPT + { + return fp_operations::fetch_add(m_storage.value, v, order); + } + + BOOST_FORCEINLINE value_type fetch_sub(difference_type v, memory_order order = memory_order_seq_cst) volatile BOOST_NOEXCEPT + { + return fp_operations::fetch_sub(m_storage.value, v, order); + } + + BOOST_FORCEINLINE value_type exchange(value_arg_type v, memory_order order = memory_order_seq_cst) volatile BOOST_NOEXCEPT + { + return atomics::detail::bitwise_fp_cast< value_type >(operations::exchange(m_storage.value, atomics::detail::bitwise_fp_cast< storage_type >(v), order)); + } + + BOOST_FORCEINLINE bool compare_exchange_strong(value_type& expected, value_arg_type desired, memory_order success_order, memory_order failure_order) volatile BOOST_NOEXCEPT + { + BOOST_ASSERT(failure_order != memory_order_release); + BOOST_ASSERT(failure_order != memory_order_acq_rel); + BOOST_ASSERT(cas_failure_order_must_not_be_stronger_than_success_order(success_order, failure_order)); + + return compare_exchange_strong_impl(expected, desired, success_order, failure_order, value_matches_storage()); + } + + BOOST_FORCEINLINE bool compare_exchange_strong(value_type& expected, value_arg_type desired, memory_order order = memory_order_seq_cst) volatile BOOST_NOEXCEPT + { + return compare_exchange_strong(expected, desired, order, atomics::detail::deduce_failure_order(order)); + } + + BOOST_FORCEINLINE bool compare_exchange_weak(value_type& expected, value_arg_type desired, memory_order success_order, memory_order failure_order) volatile BOOST_NOEXCEPT + { + BOOST_ASSERT(failure_order != memory_order_release); + BOOST_ASSERT(failure_order != memory_order_acq_rel); + BOOST_ASSERT(cas_failure_order_must_not_be_stronger_than_success_order(success_order, failure_order)); + + return compare_exchange_weak_impl(expected, desired, success_order, failure_order, value_matches_storage()); + } + + BOOST_FORCEINLINE bool compare_exchange_weak(value_type& expected, value_arg_type desired, memory_order order = memory_order_seq_cst) volatile BOOST_NOEXCEPT + { + return compare_exchange_weak(expected, desired, order, atomics::detail::deduce_failure_order(order)); + } + + // Boost.Atomic extensions + BOOST_FORCEINLINE value_type fetch_negate(memory_order order = memory_order_seq_cst) volatile BOOST_NOEXCEPT + { + return extra_fp_operations::fetch_negate(m_storage.value, order); + } + + BOOST_FORCEINLINE value_type add(difference_type v, memory_order order = memory_order_seq_cst) volatile BOOST_NOEXCEPT + { + return extra_fp_operations::add(m_storage.value, v, order); + } + + BOOST_FORCEINLINE value_type sub(difference_type v, memory_order order = memory_order_seq_cst) volatile BOOST_NOEXCEPT + { + return extra_fp_operations::sub(m_storage.value, v, order); + } + + BOOST_FORCEINLINE value_type negate(memory_order order = memory_order_seq_cst) volatile BOOST_NOEXCEPT + { + return extra_fp_operations::negate(m_storage.value, order); + } + + BOOST_FORCEINLINE void opaque_add(difference_type v, memory_order order = memory_order_seq_cst) volatile BOOST_NOEXCEPT + { + extra_fp_operations::opaque_add(m_storage.value, v, order); + } + + BOOST_FORCEINLINE void opaque_sub(difference_type v, memory_order order = memory_order_seq_cst) volatile BOOST_NOEXCEPT + { + extra_fp_operations::opaque_sub(m_storage.value, v, order); + } + + BOOST_FORCEINLINE void opaque_negate(memory_order order = memory_order_seq_cst) volatile BOOST_NOEXCEPT + { + extra_fp_operations::opaque_negate(m_storage.value, order); + } + + // Operators + BOOST_FORCEINLINE value_type operator+=(difference_type v) volatile BOOST_NOEXCEPT + { + return add(v); + } + + BOOST_FORCEINLINE value_type operator-=(difference_type v) volatile BOOST_NOEXCEPT + { + return sub(v); + } + + BOOST_DELETED_FUNCTION(base_atomic(base_atomic const&)) + BOOST_DELETED_FUNCTION(base_atomic& operator=(base_atomic const&)) + +private: + BOOST_FORCEINLINE bool compare_exchange_strong_impl(value_type& expected, value_arg_type desired, memory_order success_order, memory_order failure_order, atomics::detail::true_type) volatile BOOST_NOEXCEPT + { +#if defined(BOOST_ATOMIC_DETAIL_STORAGE_TYPE_MAY_ALIAS) + return operations::compare_exchange_strong(m_storage.value, reinterpret_cast< storage_type& >(expected), atomics::detail::bitwise_fp_cast< storage_type >(desired), success_order, failure_order); +#else + return compare_exchange_strong_impl(expected, desired, success_order, failure_order, atomics::detail::false_type()); +#endif + } + + BOOST_FORCEINLINE bool compare_exchange_strong_impl(value_type& expected, value_arg_type desired, memory_order success_order, memory_order failure_order, atomics::detail::false_type) volatile BOOST_NOEXCEPT + { + storage_type old_value = atomics::detail::bitwise_fp_cast< storage_type >(expected); + const bool res = operations::compare_exchange_strong(m_storage.value, old_value, atomics::detail::bitwise_fp_cast< storage_type >(desired), success_order, failure_order); + expected = atomics::detail::bitwise_fp_cast< value_type >(old_value); + return res; + } + + BOOST_FORCEINLINE bool compare_exchange_weak_impl(value_type& expected, value_arg_type desired, memory_order success_order, memory_order failure_order, atomics::detail::true_type) volatile BOOST_NOEXCEPT + { +#if defined(BOOST_ATOMIC_DETAIL_STORAGE_TYPE_MAY_ALIAS) + return operations::compare_exchange_weak(m_storage.value, reinterpret_cast< storage_type& >(expected), atomics::detail::bitwise_fp_cast< storage_type >(desired), success_order, failure_order); +#else + return compare_exchange_weak_impl(expected, desired, success_order, failure_order, atomics::detail::false_type()); +#endif + } + + BOOST_FORCEINLINE bool compare_exchange_weak_impl(value_type& expected, value_arg_type desired, memory_order success_order, memory_order failure_order, atomics::detail::false_type) volatile BOOST_NOEXCEPT + { + storage_type old_value = atomics::detail::bitwise_fp_cast< storage_type >(expected); + const bool res = operations::compare_exchange_weak(m_storage.value, old_value, atomics::detail::bitwise_fp_cast< storage_type >(desired), success_order, failure_order); + expected = atomics::detail::bitwise_fp_cast< value_type >(old_value); + return res; + } +}; + +#endif // !defined(BOOST_ATOMIC_NO_FLOATING_POINT) + + //! Implementation for pointers to object types template< typename T > class base_atomic< T*, void* > @@ -692,7 +882,7 @@ protected: typename operations::aligned_storage_type m_storage; public: - BOOST_DEFAULTED_FUNCTION(base_atomic(), {}) + BOOST_DEFAULTED_FUNCTION(base_atomic() BOOST_NOEXCEPT, {}) BOOST_FORCEINLINE explicit base_atomic(value_type const& v) BOOST_NOEXCEPT : m_storage(atomics::detail::bitwise_cast< uintptr_storage_type >(v)) { } @@ -879,7 +1069,7 @@ public: static BOOST_CONSTEXPR_OR_CONST bool is_always_lock_free = base_type::operations::is_always_lock_free; public: - BOOST_DEFAULTED_FUNCTION(atomic(), BOOST_NOEXCEPT {}) + BOOST_DEFAULTED_FUNCTION(atomic() BOOST_NOEXCEPT, {}) // NOTE: The constructor is made explicit because gcc 4.7 complains that // operator=(value_arg_type) is considered ambiguous with operator=(atomic const&) @@ -967,6 +1157,12 @@ typedef atomic< uint_fast64_t > atomic_uint_fast64_t; typedef atomic< intmax_t > atomic_intmax_t; typedef atomic< uintmax_t > atomic_uintmax_t; +#if !defined(BOOST_ATOMIC_NO_FLOATING_POINT) +typedef atomic< float > atomic_float_t; +typedef atomic< double > atomic_double_t; +typedef atomic< long double > atomic_long_double_t; +#endif + typedef atomic< std::size_t > atomic_size_t; typedef atomic< std::ptrdiff_t > atomic_ptrdiff_t; diff --git a/include/boost/atomic/detail/bitwise_cast.hpp b/include/boost/atomic/detail/bitwise_cast.hpp index 68f72d4..3d40b9a 100644 --- a/include/boost/atomic/detail/bitwise_cast.hpp +++ b/include/boost/atomic/detail/bitwise_cast.hpp @@ -17,6 +17,7 @@ #define BOOST_ATOMIC_DETAIL_BITWISE_CAST_HPP_INCLUDED_ #include +#include #include #ifdef BOOST_HAS_PRAGMA_ONCE @@ -33,17 +34,6 @@ namespace boost { namespace atomics { namespace detail { -template< typename T > -BOOST_FORCEINLINE T* addressof(T& value) BOOST_NOEXCEPT -{ - // Note: The point of using a local struct as the intermediate type instead of char is to avoid gcc warnings - // if T is a const volatile char*: - // warning: casting 'const volatile char* const' to 'const volatile char&' does not dereference pointer - // The local struct makes sure T is not related to the cast target type. - struct opaque_type; - return reinterpret_cast< T* >(&const_cast< opaque_type& >(reinterpret_cast< const volatile opaque_type& >(value))); -} - template< typename To, typename From > BOOST_FORCEINLINE To bitwise_cast(From const& from) BOOST_NOEXCEPT { diff --git a/include/boost/atomic/detail/bitwise_fp_cast.hpp b/include/boost/atomic/detail/bitwise_fp_cast.hpp new file mode 100644 index 0000000..705033a --- /dev/null +++ b/include/boost/atomic/detail/bitwise_fp_cast.hpp @@ -0,0 +1,108 @@ +/* + * 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) 2018 Andrey Semashev + */ +/*! + * \file atomic/detail/bitwise_fp_cast.hpp + * + * This header defines \c bitwise_fp_cast used to convert between storage and floating point value types + */ + +#ifndef BOOST_ATOMIC_DETAIL_BITWISE_FP_CAST_HPP_INCLUDED_ +#define BOOST_ATOMIC_DETAIL_BITWISE_FP_CAST_HPP_INCLUDED_ + +#include +#include +#include +#include +#include + +#ifdef BOOST_HAS_PRAGMA_ONCE +#pragma once +#endif + +#if defined(BOOST_GCC) && (BOOST_GCC+0) >= 40600 +#pragma GCC diagnostic push +// missing initializer for member var +#pragma GCC diagnostic ignored "-Wmissing-field-initializers" +#endif + +namespace boost { +namespace atomics { +namespace detail { + +/*! + * \brief The type trait returns the size of the value of the specified floating point type + * + * This size may be less than sizeof(T) if the implementation uses padding bytes for a particular FP type. This is + * often the case with 80-bit extended double, which is stored in 12 or 16 bytes with padding filled with garbage. + */ +template< typename T > +struct value_sizeof +{ + static BOOST_CONSTEXPR_OR_CONST std::size_t value = sizeof(T); +}; + +#if defined(BOOST_ATOMIC_DETAIL_SIZEOF_FLOAT_VALUE) +template< > +struct value_sizeof< float > +{ + static BOOST_CONSTEXPR_OR_CONST std::size_t value = BOOST_ATOMIC_DETAIL_SIZEOF_FLOAT_VALUE; +}; +#endif + +#if defined(BOOST_ATOMIC_DETAIL_SIZEOF_DOUBLE_VALUE) +template< > +struct value_sizeof< double > +{ + static BOOST_CONSTEXPR_OR_CONST std::size_t value = BOOST_ATOMIC_DETAIL_SIZEOF_DOUBLE_VALUE; +}; +#endif + +#if defined(BOOST_ATOMIC_DETAIL_SIZEOF_LONG_DOUBLE_VALUE) +template< > +struct value_sizeof< long double > +{ + static BOOST_CONSTEXPR_OR_CONST std::size_t value = BOOST_ATOMIC_DETAIL_SIZEOF_LONG_DOUBLE_VALUE; +}; +#endif + +template< typename T > +struct value_sizeof< const T > : value_sizeof< T > {}; + +template< typename T > +struct value_sizeof< volatile T > : value_sizeof< T > {}; + +template< typename T > +struct value_sizeof< const volatile T > : value_sizeof< T > {}; + + +template< typename To, typename From > +BOOST_FORCEINLINE To bitwise_fp_cast(From const& from) BOOST_NOEXCEPT +{ + struct + { + To to; + } + value = {}; + BOOST_ATOMIC_DETAIL_MEMCPY + ( + atomics::detail::addressof(value.to), + atomics::detail::addressof(from), + (atomics::detail::value_sizeof< From >::value < sizeof(To) ? atomics::detail::value_sizeof< From >::value : sizeof(To)) + ); + return value.to; +} + +} // namespace detail +} // namespace atomics +} // namespace boost + +#if defined(BOOST_GCC) && (BOOST_GCC+0) >= 40600 +#pragma GCC diagnostic pop +#endif + +#endif // BOOST_ATOMIC_DETAIL_BITWISE_FP_CAST_HPP_INCLUDED_ diff --git a/include/boost/atomic/detail/config.hpp b/include/boost/atomic/detail/config.hpp index fcb6bcd..f46a049 100644 --- a/include/boost/atomic/detail/config.hpp +++ b/include/boost/atomic/detail/config.hpp @@ -82,6 +82,12 @@ #define BOOST_ATOMIC_DETAIL_IS_CONSTANT(x) false #endif +#if (defined(__BYTE_ORDER__) && defined(__FLOAT_WORD_ORDER__) && (__BYTE_ORDER__+0) == (__FLOAT_WORD_ORDER__+0)) ||\ + defined(__i386__) || defined(__x86_64__) || defined(_M_IX86) || defined(_M_X64) +// This macro indicates that integer and floating point endianness is the same +#define BOOST_ATOMIC_DETAIL_INT_FP_ENDIAN_MATCH +#endif + // Deprecated symbols markup #if !defined(BOOST_ATOMIC_DETAIL_DEPRECATED) && defined(_MSC_VER) #if (_MSC_VER) >= 1400 diff --git a/include/boost/atomic/detail/extra_fp_operations.hpp b/include/boost/atomic/detail/extra_fp_operations.hpp new file mode 100644 index 0000000..854d8c9 --- /dev/null +++ b/include/boost/atomic/detail/extra_fp_operations.hpp @@ -0,0 +1,28 @@ +/* + * 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) 2018 Andrey Semashev + */ +/*! + * \file atomic/detail/extra_fp_operations.hpp + * + * This header defines extra floating point atomic operations, including the generic version. + */ + +#ifndef BOOST_ATOMIC_DETAIL_EXTRA_FP_OPERATIONS_HPP_INCLUDED_ +#define BOOST_ATOMIC_DETAIL_EXTRA_FP_OPERATIONS_HPP_INCLUDED_ + +#include +#include + +#if !defined(BOOST_ATOMIC_DETAIL_EXTRA_FP_BACKEND_GENERIC) +#include BOOST_ATOMIC_DETAIL_EXTRA_FP_BACKEND_HEADER(boost/atomic/detail/extra_fp_ops_) +#endif + +#ifdef BOOST_HAS_PRAGMA_ONCE +#pragma once +#endif + +#endif // BOOST_ATOMIC_DETAIL_EXTRA_FP_OPERATIONS_HPP_INCLUDED_ diff --git a/include/boost/atomic/detail/extra_fp_operations_fwd.hpp b/include/boost/atomic/detail/extra_fp_operations_fwd.hpp new file mode 100644 index 0000000..79bca9d --- /dev/null +++ b/include/boost/atomic/detail/extra_fp_operations_fwd.hpp @@ -0,0 +1,35 @@ +/* + * 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) 2018 Andrey Semashev + */ +/*! + * \file atomic/detail/extra_fp_operations_fwd.hpp + * + * This header contains forward declaration of the \c extra_fp_operations template. + */ + +#ifndef BOOST_ATOMIC_DETAIL_EXTRA_FP_OPERATIONS_FWD_HPP_INCLUDED_ +#define BOOST_ATOMIC_DETAIL_EXTRA_FP_OPERATIONS_FWD_HPP_INCLUDED_ + +#include +#include + +#ifdef BOOST_HAS_PRAGMA_ONCE +#pragma once +#endif + +namespace boost { +namespace atomics { +namespace detail { + +template< typename Base, typename Value, std::size_t Size, bool = Base::is_always_lock_free > +struct extra_fp_operations; + +} // namespace detail +} // namespace atomics +} // namespace boost + +#endif // BOOST_ATOMIC_DETAIL_EXTRA_FP_OPERATIONS_FWD_HPP_INCLUDED_ diff --git a/include/boost/atomic/detail/extra_fp_ops_emulated.hpp b/include/boost/atomic/detail/extra_fp_ops_emulated.hpp new file mode 100644 index 0000000..e04b2f5 --- /dev/null +++ b/include/boost/atomic/detail/extra_fp_ops_emulated.hpp @@ -0,0 +1,107 @@ +/* + * 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) 2018 Andrey Semashev + */ +/*! + * \file atomic/detail/extra_fp_ops_emulated.hpp + * + * This header contains emulated (lock-based) implementation of the extra floating point atomic operations. + */ + +#ifndef BOOST_ATOMIC_DETAIL_EXTRA_FP_OPS_EMULATED_HPP_INCLUDED_ +#define BOOST_ATOMIC_DETAIL_EXTRA_FP_OPS_EMULATED_HPP_INCLUDED_ + +#include +#include +#include +#include +#include +#include + +#ifdef BOOST_HAS_PRAGMA_ONCE +#pragma once +#endif + +namespace boost { +namespace atomics { +namespace detail { + +//! Generic implementation of extra floating point operations +template< typename Base, typename Value, std::size_t Size > +struct emulated_extra_fp_operations : + public Base +{ + typedef Base base_type; + typedef typename base_type::storage_type storage_type; + typedef Value value_type; + + static BOOST_FORCEINLINE value_type fetch_negate(storage_type volatile& storage, memory_order) BOOST_NOEXCEPT + { + storage_type& s = const_cast< storage_type& >(storage); + lockpool::scoped_lock lock(&storage); + value_type old_val = atomics::detail::bitwise_fp_cast< value_type >(s); + value_type new_val = -old_val; + s = atomics::detail::bitwise_fp_cast< storage_type >(new_val); + return old_val; + } + + static BOOST_FORCEINLINE value_type negate(storage_type volatile& storage, memory_order) BOOST_NOEXCEPT + { + storage_type& s = const_cast< storage_type& >(storage); + lockpool::scoped_lock lock(&storage); + value_type old_val = atomics::detail::bitwise_fp_cast< value_type >(s); + value_type new_val = -old_val; + s = atomics::detail::bitwise_fp_cast< storage_type >(new_val); + return new_val; + } + + static BOOST_FORCEINLINE value_type add(storage_type volatile& storage, value_type v, memory_order) BOOST_NOEXCEPT + { + storage_type& s = const_cast< storage_type& >(storage); + lockpool::scoped_lock lock(&storage); + value_type old_val = atomics::detail::bitwise_fp_cast< value_type >(s); + value_type new_val = old_val + v; + s = atomics::detail::bitwise_fp_cast< storage_type >(new_val); + return new_val; + } + + static BOOST_FORCEINLINE value_type sub(storage_type volatile& storage, value_type v, memory_order) BOOST_NOEXCEPT + { + storage_type& s = const_cast< storage_type& >(storage); + lockpool::scoped_lock lock(&storage); + value_type old_val = atomics::detail::bitwise_fp_cast< value_type >(s); + value_type new_val = old_val - v; + s = atomics::detail::bitwise_fp_cast< storage_type >(new_val); + return new_val; + } + + static BOOST_FORCEINLINE void opaque_negate(storage_type volatile& storage, memory_order order) BOOST_NOEXCEPT + { + fetch_negate(storage, order); + } + + static BOOST_FORCEINLINE void opaque_add(storage_type volatile& storage, value_type v, memory_order order) BOOST_NOEXCEPT + { + base_type::fetch_add(storage, v, order); + } + + static BOOST_FORCEINLINE void opaque_sub(storage_type volatile& storage, value_type v, memory_order order) BOOST_NOEXCEPT + { + base_type::fetch_sub(storage, v, order); + } +}; + +template< typename Base, typename Value, std::size_t Size > +struct extra_fp_operations< Base, Value, Size, false > : + public emulated_extra_fp_operations< Base, Value, Size > +{ +}; + +} // namespace detail +} // namespace atomics +} // namespace boost + +#endif // BOOST_ATOMIC_DETAIL_EXTRA_FP_OPS_EMULATED_HPP_INCLUDED_ diff --git a/include/boost/atomic/detail/extra_fp_ops_generic.hpp b/include/boost/atomic/detail/extra_fp_ops_generic.hpp new file mode 100644 index 0000000..34902c4 --- /dev/null +++ b/include/boost/atomic/detail/extra_fp_ops_generic.hpp @@ -0,0 +1,189 @@ +/* + * 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) 2018 Andrey Semashev + */ +/*! + * \file atomic/detail/extra_fp_ops_generic.hpp + * + * This header contains generic implementation of the extra floating point atomic operations. + */ + +#ifndef BOOST_ATOMIC_DETAIL_EXTRA_FP_OPS_GENERIC_HPP_INCLUDED_ +#define BOOST_ATOMIC_DETAIL_EXTRA_FP_OPS_GENERIC_HPP_INCLUDED_ + +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef BOOST_HAS_PRAGMA_ONCE +#pragma once +#endif + +#if defined(BOOST_GCC) && (BOOST_GCC+0) >= 60000 +#pragma GCC diagnostic push +// ignoring attributes on template argument X - this warning is because we need to pass storage_type as a template argument; no problem in this case +#pragma GCC diagnostic ignored "-Wignored-attributes" +#endif + +namespace boost { +namespace atomics { +namespace detail { + +//! Negate implementation +template< + typename Base, + typename Value, + std::size_t Size +#if defined(BOOST_ATOMIC_DETAIL_INT_FP_ENDIAN_MATCH) + , bool = atomics::detail::is_iec559< Value >::value && atomics::detail::is_integral< typename Base::storage_type >::value +#endif +> +struct generic_extra_fp_negate : + public Base +{ + typedef Base base_type; + typedef typename base_type::storage_type storage_type; + typedef Value value_type; + + static BOOST_FORCEINLINE value_type fetch_negate(storage_type volatile& storage, memory_order order) BOOST_NOEXCEPT + { + storage_type old_storage, new_storage; + value_type old_val, new_val; + atomics::detail::non_atomic_load(storage, old_storage); + do + { + old_val = atomics::detail::bitwise_fp_cast< value_type >(old_storage); + new_val = -old_val; + new_storage = atomics::detail::bitwise_fp_cast< storage_type >(new_val); + } + while (!base_type::compare_exchange_weak(storage, old_storage, new_storage, order, memory_order_relaxed)); + return old_val; + } + + static BOOST_FORCEINLINE value_type negate(storage_type volatile& storage, memory_order order) BOOST_NOEXCEPT + { + storage_type old_storage, new_storage; + value_type old_val, new_val; + atomics::detail::non_atomic_load(storage, old_storage); + do + { + old_val = atomics::detail::bitwise_fp_cast< value_type >(old_storage); + new_val = -old_val; + new_storage = atomics::detail::bitwise_fp_cast< storage_type >(new_val); + } + while (!base_type::compare_exchange_weak(storage, old_storage, new_storage, order, memory_order_relaxed)); + return new_val; + } + + static BOOST_FORCEINLINE void opaque_negate(storage_type volatile& storage, memory_order order) BOOST_NOEXCEPT + { + fetch_negate(storage, order); + } +}; + +#if defined(BOOST_ATOMIC_DETAIL_INT_FP_ENDIAN_MATCH) + +//! Negate implementation for IEEE 754 / IEC 559 floating point types. We leverage the fact that the sign bit is the most significant bit in the value. +template< typename Base, typename Value, std::size_t Size > +struct generic_extra_fp_negate< Base, Value, Size, true > : + public Base +{ + typedef Base base_type; + typedef typename base_type::storage_type storage_type; + typedef Value value_type; + + //! The mask with only one sign bit set to 1 + static BOOST_CONSTEXPR_OR_CONST storage_type sign_mask = static_cast< storage_type >(1u) << (atomics::detail::value_sizeof< value_type >::value * 8u - 1u); + + static BOOST_FORCEINLINE value_type fetch_negate(storage_type volatile& storage, memory_order order) BOOST_NOEXCEPT + { + return atomics::detail::bitwise_fp_cast< value_type >(base_type::fetch_xor(storage, sign_mask, order)); + } + + static BOOST_FORCEINLINE value_type negate(storage_type volatile& storage, memory_order order) BOOST_NOEXCEPT + { + return atomics::detail::bitwise_fp_cast< value_type >(base_type::bitwise_xor(storage, sign_mask, order)); + } + + static BOOST_FORCEINLINE void opaque_negate(storage_type volatile& storage, memory_order order) BOOST_NOEXCEPT + { + base_type::opaque_xor(storage, sign_mask, order); + } +}; + +#endif // defined(BOOST_ATOMIC_DETAIL_INT_FP_ENDIAN_MATCH) + +//! Generic implementation of floating point operations +template< typename Base, typename Value, std::size_t Size > +struct generic_extra_fp_operations : + public generic_extra_fp_negate< Base, Value, Size > +{ + typedef generic_extra_fp_negate< Base, Value, Size > base_type; + typedef typename base_type::storage_type storage_type; + typedef Value value_type; + + static BOOST_FORCEINLINE value_type add(storage_type volatile& storage, value_type v, memory_order order) BOOST_NOEXCEPT + { + storage_type old_storage, new_storage; + value_type old_val, new_val; + atomics::detail::non_atomic_load(storage, old_storage); + do + { + old_val = atomics::detail::bitwise_fp_cast< value_type >(old_storage); + new_val = old_val + v; + new_storage = atomics::detail::bitwise_fp_cast< storage_type >(new_val); + } + while (!base_type::compare_exchange_weak(storage, old_storage, new_storage, order, memory_order_relaxed)); + return new_val; + } + + static BOOST_FORCEINLINE value_type sub(storage_type volatile& storage, value_type v, memory_order order) BOOST_NOEXCEPT + { + storage_type old_storage, new_storage; + value_type old_val, new_val; + atomics::detail::non_atomic_load(storage, old_storage); + do + { + old_val = atomics::detail::bitwise_fp_cast< value_type >(old_storage); + new_val = old_val - v; + new_storage = atomics::detail::bitwise_fp_cast< storage_type >(new_val); + } + while (!base_type::compare_exchange_weak(storage, old_storage, new_storage, order, memory_order_relaxed)); + return new_val; + } + + static BOOST_FORCEINLINE void opaque_add(storage_type volatile& storage, value_type v, memory_order order) BOOST_NOEXCEPT + { + base_type::fetch_add(storage, v, order); + } + + static BOOST_FORCEINLINE void opaque_sub(storage_type volatile& storage, value_type v, memory_order order) BOOST_NOEXCEPT + { + base_type::fetch_sub(storage, v, order); + } +}; + +// Default extra_fp_operations template definition will be used unless specialized for a specific platform +template< typename Base, typename Value, std::size_t Size > +struct extra_fp_operations< Base, Value, Size, true > : + public generic_extra_fp_operations< Base, Value, Size > +{ +}; + +} // namespace detail +} // namespace atomics +} // namespace boost + +#if defined(BOOST_GCC) && (BOOST_GCC+0) >= 60000 +#pragma GCC diagnostic pop +#endif + +#endif // BOOST_ATOMIC_DETAIL_FP_OPS_GENERIC_HPP_INCLUDED_ diff --git a/include/boost/atomic/detail/float_sizes.hpp b/include/boost/atomic/detail/float_sizes.hpp new file mode 100644 index 0000000..4c3a346 --- /dev/null +++ b/include/boost/atomic/detail/float_sizes.hpp @@ -0,0 +1,142 @@ +/* + * 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) 2018 Andrey Semashev + */ +/*! + * \file atomic/detail/float_sizes.hpp + * + * This header defines macros for testing buitin floating point type sizes + */ + +#ifndef BOOST_ATOMIC_DETAIL_FLOAT_SIZES_HPP_INCLUDED_ +#define BOOST_ATOMIC_DETAIL_FLOAT_SIZES_HPP_INCLUDED_ + +#include +#include + +#ifdef BOOST_HAS_PRAGMA_ONCE +#pragma once +#endif + +// Detect value sizes of the different floating point types. The value sizes may be less than the corresponding type sizes +// if the type contains padding bits. This is typical e.g. with 80-bit extended float types, which are often represented as 128-bit types. +// See: https://en.wikipedia.org/wiki/IEEE_754 +// For Intel x87 extended double see: https://en.wikipedia.org/wiki/Extended_precision#x86_Architecture_Extended_Precision_Format +// For IBM extended double (a.k.a. double-double) see: https://en.wikipedia.org/wiki/Long_double#Implementations, https://gcc.gnu.org/wiki/Ieee128PowerPC +#if (FLT_RADIX+0) == 2 + +#if ((FLT_MANT_DIG+0) == 11) && ((FLT_MAX_EXP+0) == 16) // IEEE 754 binary16 +#define BOOST_ATOMIC_DETAIL_SIZEOF_FLOAT_VALUE 2 +#elif ((FLT_MANT_DIG+0) == 24) && ((FLT_MAX_EXP+0) == 128) // IEEE 754 binary32 +#define BOOST_ATOMIC_DETAIL_SIZEOF_FLOAT_VALUE 4 +#elif ((FLT_MANT_DIG+0) == 53) && ((FLT_MAX_EXP+0) == 1024) // IEEE 754 binary64 +#define BOOST_ATOMIC_DETAIL_SIZEOF_FLOAT_VALUE 8 +#elif ((FLT_MANT_DIG+0) == 64) && ((FLT_MAX_EXP+0) == 16384) // x87 extended double +#define BOOST_ATOMIC_DETAIL_SIZEOF_FLOAT_VALUE 10 +#elif ((FLT_MANT_DIG+0) == 106) && ((FLT_MAX_EXP+0) == 1024) // IBM extended double +#define BOOST_ATOMIC_DETAIL_SIZEOF_FLOAT_VALUE 16 +#elif ((FLT_MANT_DIG+0) == 113) && ((FLT_MAX_EXP+0) == 16384) // IEEE 754 binary128 +#define BOOST_ATOMIC_DETAIL_SIZEOF_FLOAT_VALUE 16 +#elif ((FLT_MANT_DIG+0) == 237) && ((FLT_MAX_EXP+0) == 262144) // IEEE 754 binary256 +#define BOOST_ATOMIC_DETAIL_SIZEOF_FLOAT_VALUE 32 +#endif + +#if ((DBL_MANT_DIG+0) == 11) && ((DBL_MAX_EXP+0) == 16) // IEEE 754 binary16 +#define BOOST_ATOMIC_DETAIL_SIZEOF_DOUBLE_VALUE 2 +#elif ((DBL_MANT_DIG+0) == 24) && ((DBL_MAX_EXP+0) == 128) // IEEE 754 binary32 +#define BOOST_ATOMIC_DETAIL_SIZEOF_DOUBLE_VALUE 4 +#elif ((DBL_MANT_DIG+0) == 53) && ((DBL_MAX_EXP+0) == 1024) // IEEE 754 binary64 +#define BOOST_ATOMIC_DETAIL_SIZEOF_DOUBLE_VALUE 8 +#elif ((DBL_MANT_DIG+0) == 64) && ((DBL_MAX_EXP+0) == 16384) // x87 extended double +#define BOOST_ATOMIC_DETAIL_SIZEOF_DOUBLE_VALUE 10 +#elif ((DBL_MANT_DIG+0) == 106) && ((DBL_MAX_EXP+0) == 1024) // IBM extended double +#define BOOST_ATOMIC_DETAIL_SIZEOF_DOUBLE_VALUE 16 +#elif ((DBL_MANT_DIG+0) == 113) && ((DBL_MAX_EXP+0) == 16384) // IEEE 754 binary128 +#define BOOST_ATOMIC_DETAIL_SIZEOF_DOUBLE_VALUE 16 +#elif ((DBL_MANT_DIG+0) == 237) && ((DBL_MAX_EXP+0) == 262144) // IEEE 754 binary256 +#define BOOST_ATOMIC_DETAIL_SIZEOF_DOUBLE_VALUE 32 +#endif + +#if ((LDBL_MANT_DIG+0) == 11) && ((LDBL_MAX_EXP+0) == 16) // IEEE 754 binary16 +#define BOOST_ATOMIC_DETAIL_SIZEOF_LONG_DOUBLE_VALUE 2 +#elif ((LDBL_MANT_DIG+0) == 24) && ((LDBL_MAX_EXP+0) == 128) // IEEE 754 binary32 +#define BOOST_ATOMIC_DETAIL_SIZEOF_LONG_DOUBLE_VALUE 4 +#elif ((LDBL_MANT_DIG+0) == 53) && ((LDBL_MAX_EXP+0) == 1024) // IEEE 754 binary64 +#define BOOST_ATOMIC_DETAIL_SIZEOF_LONG_DOUBLE_VALUE 8 +#elif ((LDBL_MANT_DIG+0) == 64) && ((LDBL_MAX_EXP+0) == 16384) // x87 extended double +#define BOOST_ATOMIC_DETAIL_SIZEOF_LONG_DOUBLE_VALUE 10 +#elif ((LDBL_MANT_DIG+0) == 106) && ((LDBL_MAX_EXP+0) == 1024) // IBM extended double +#define BOOST_ATOMIC_DETAIL_SIZEOF_LONG_DOUBLE_VALUE 16 +#elif ((LDBL_MANT_DIG+0) == 113) && ((LDBL_MAX_EXP+0) == 16384) // IEEE 754 binary128 +#define BOOST_ATOMIC_DETAIL_SIZEOF_LONG_DOUBLE_VALUE 16 +#elif ((LDBL_MANT_DIG+0) == 237) && ((LDBL_MAX_EXP+0) == 262144) // IEEE 754 binary256 +#define BOOST_ATOMIC_DETAIL_SIZEOF_LONG_DOUBLE_VALUE 32 +#endif + +#elif (FLT_RADIX+0) == 10 + +#if ((FLT_MANT_DIG+0) == 7) && ((FLT_MAX_EXP+0) == 97) // IEEE 754 decimal32 +#define BOOST_ATOMIC_DETAIL_SIZEOF_FLOAT_VALUE 4 +#elif ((FLT_MANT_DIG+0) == 16) && ((FLT_MAX_EXP+0) == 385) // IEEE 754 decimal64 +#define BOOST_ATOMIC_DETAIL_SIZEOF_FLOAT_VALUE 8 +#elif ((FLT_MANT_DIG+0) == 34) && ((FLT_MAX_EXP+0) == 6145) // IEEE 754 decimal128 +#define BOOST_ATOMIC_DETAIL_SIZEOF_FLOAT_VALUE 16 +#endif + +#if ((DBL_MANT_DIG+0) == 7) && ((DBL_MAX_EXP+0) == 97) // IEEE 754 decimal32 +#define BOOST_ATOMIC_DETAIL_SIZEOF_DOUBLE_VALUE 4 +#elif ((DBL_MANT_DIG+0) == 16) && ((DBL_MAX_EXP+0) == 385) // IEEE 754 decimal64 +#define BOOST_ATOMIC_DETAIL_SIZEOF_DOUBLE_VALUE 8 +#elif ((DBL_MANT_DIG+0) == 34) && ((DBL_MAX_EXP+0) == 6145) // IEEE 754 decimal128 +#define BOOST_ATOMIC_DETAIL_SIZEOF_DOUBLE_VALUE 16 +#endif + +#if ((LDBL_MANT_DIG+0) == 7) && ((LDBL_MAX_EXP+0) == 97) // IEEE 754 decimal32 +#define BOOST_ATOMIC_DETAIL_SIZEOF_LONG_DOUBLE_VALUE 4 +#elif ((LDBL_MANT_DIG+0) == 16) && ((LDBL_MAX_EXP+0) == 385) // IEEE 754 decimal64 +#define BOOST_ATOMIC_DETAIL_SIZEOF_LONG_DOUBLE_VALUE 8 +#elif ((LDBL_MANT_DIG+0) == 34) && ((LDBL_MAX_EXP+0) == 6145) // IEEE 754 decimal128 +#define BOOST_ATOMIC_DETAIL_SIZEOF_LONG_DOUBLE_VALUE 16 +#endif + +#endif + +// GCC and compatible compilers define internal macros with builtin type traits +#if defined(__SIZEOF_FLOAT__) +#define BOOST_ATOMIC_DETAIL_SIZEOF_FLOAT __SIZEOF_FLOAT__ +#endif +#if defined(__SIZEOF_DOUBLE__) +#define BOOST_ATOMIC_DETAIL_SIZEOF_DOUBLE __SIZEOF_DOUBLE__ +#endif +#if defined(__SIZEOF_LONG_DOUBLE__) +#define BOOST_ATOMIC_DETAIL_SIZEOF_LONG_DOUBLE __SIZEOF_LONG_DOUBLE__ +#endif + +#if !defined(BOOST_ATOMIC_DETAIL_SIZEOF_FLOAT) || !defined(BOOST_ATOMIC_DETAIL_SIZEOF_DOUBLE) || !defined(BOOST_ATOMIC_DETAIL_SIZEOF_LONG_DOUBLE) + +#define BOOST_ATOMIC_DETAIL_ALIGN_SIZE_TO_POWER_OF_2(x)\ + ((x) == 1u ? 1u : ((x) == 2u ? 2u : ((x) <= 4u ? 4u : ((x) <= 8u ? 8u : ((x) <= 16u ? 16u : ((x) <= 32u ? 32u : (x))))))) + +// Make our best guess. These sizes may not be accurate, but they are good enough to estimate the size of the storage required to hold these types. +#if !defined(BOOST_ATOMIC_DETAIL_SIZEOF_FLOAT) && defined(BOOST_ATOMIC_DETAIL_SIZEOF_FLOAT_VALUE) +#define BOOST_ATOMIC_DETAIL_SIZEOF_FLOAT BOOST_ATOMIC_DETAIL_ALIGN_SIZE_TO_POWER_OF_2(BOOST_ATOMIC_DETAIL_SIZEOF_FLOAT_VALUE) +#endif +#if !defined(BOOST_ATOMIC_DETAIL_SIZEOF_DOUBLE) && defined(BOOST_ATOMIC_DETAIL_SIZEOF_DOUBLE_VALUE) +#define BOOST_ATOMIC_DETAIL_SIZEOF_DOUBLE BOOST_ATOMIC_DETAIL_ALIGN_SIZE_TO_POWER_OF_2(BOOST_ATOMIC_DETAIL_SIZEOF_DOUBLE_VALUE) +#endif +#if !defined(BOOST_ATOMIC_DETAIL_SIZEOF_LONG_DOUBLE) && defined(BOOST_ATOMIC_DETAIL_SIZEOF_LONG_DOUBLE_VALUE) +#define BOOST_ATOMIC_DETAIL_SIZEOF_LONG_DOUBLE BOOST_ATOMIC_DETAIL_ALIGN_SIZE_TO_POWER_OF_2(BOOST_ATOMIC_DETAIL_SIZEOF_LONG_DOUBLE_VALUE) +#endif + +#endif // !defined(BOOST_ATOMIC_DETAIL_SIZEOF_FLOAT) || !defined(BOOST_ATOMIC_DETAIL_SIZEOF_DOUBLE) || !defined(BOOST_ATOMIC_DETAIL_SIZEOF_LONG_DOUBLE) + +#if !defined(BOOST_ATOMIC_DETAIL_SIZEOF_FLOAT_VALUE) || !defined(BOOST_ATOMIC_DETAIL_SIZEOF_FLOAT) ||\ + !defined(BOOST_ATOMIC_DETAIL_SIZEOF_DOUBLE_VALUE) || !defined(BOOST_ATOMIC_DETAIL_SIZEOF_DOUBLE) ||\ + !defined(BOOST_ATOMIC_DETAIL_SIZEOF_LONG_DOUBLE_VALUE) || !defined(BOOST_ATOMIC_DETAIL_SIZEOF_LONG_DOUBLE) +#error Boost.Atomic: Failed to determine builtin floating point type sizes, the target platform is not supported. Please, report to the developers (patches are welcome). +#endif + +#endif // BOOST_ATOMIC_DETAIL_FLOAT_SIZES_HPP_INCLUDED_ diff --git a/include/boost/atomic/detail/fp_operations.hpp b/include/boost/atomic/detail/fp_operations.hpp new file mode 100644 index 0000000..69cb0d1 --- /dev/null +++ b/include/boost/atomic/detail/fp_operations.hpp @@ -0,0 +1,28 @@ +/* + * 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) 2018 Andrey Semashev + */ +/*! + * \file atomic/detail/fp_operations.hpp + * + * This header defines floating point atomic operations, including the generic version. + */ + +#ifndef BOOST_ATOMIC_DETAIL_FP_OPERATIONS_HPP_INCLUDED_ +#define BOOST_ATOMIC_DETAIL_FP_OPERATIONS_HPP_INCLUDED_ + +#include +#include + +#if !defined(BOOST_ATOMIC_DETAIL_FP_BACKEND_GENERIC) +#include BOOST_ATOMIC_DETAIL_FP_BACKEND_HEADER(boost/atomic/detail/fp_ops_) +#endif + +#ifdef BOOST_HAS_PRAGMA_ONCE +#pragma once +#endif + +#endif // BOOST_ATOMIC_DETAIL_FP_OPERATIONS_HPP_INCLUDED_ diff --git a/include/boost/atomic/detail/fp_operations_fwd.hpp b/include/boost/atomic/detail/fp_operations_fwd.hpp new file mode 100644 index 0000000..8696de3 --- /dev/null +++ b/include/boost/atomic/detail/fp_operations_fwd.hpp @@ -0,0 +1,35 @@ +/* + * 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) 2018 Andrey Semashev + */ +/*! + * \file atomic/detail/fp_operations_fwd.hpp + * + * This header contains forward declaration of the \c fp_operations template. + */ + +#ifndef BOOST_ATOMIC_DETAIL_FP_OPERATIONS_FWD_HPP_INCLUDED_ +#define BOOST_ATOMIC_DETAIL_FP_OPERATIONS_FWD_HPP_INCLUDED_ + +#include +#include + +#ifdef BOOST_HAS_PRAGMA_ONCE +#pragma once +#endif + +namespace boost { +namespace atomics { +namespace detail { + +template< typename Base, typename Value, std::size_t Size, bool = Base::is_always_lock_free > +struct fp_operations; + +} // namespace detail +} // namespace atomics +} // namespace boost + +#endif // BOOST_ATOMIC_DETAIL_FP_OPERATIONS_FWD_HPP_INCLUDED_ diff --git a/include/boost/atomic/detail/fp_ops_emulated.hpp b/include/boost/atomic/detail/fp_ops_emulated.hpp new file mode 100644 index 0000000..a87f181 --- /dev/null +++ b/include/boost/atomic/detail/fp_ops_emulated.hpp @@ -0,0 +1,72 @@ +/* + * 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) 2018 Andrey Semashev + */ +/*! + * \file atomic/detail/fp_ops_emulated.hpp + * + * This header contains emulated (lock-based) implementation of the floating point atomic operations. + */ + +#ifndef BOOST_ATOMIC_DETAIL_FP_OPS_EMULATED_HPP_INCLUDED_ +#define BOOST_ATOMIC_DETAIL_FP_OPS_EMULATED_HPP_INCLUDED_ + +#include +#include +#include +#include +#include +#include + +#ifdef BOOST_HAS_PRAGMA_ONCE +#pragma once +#endif + +namespace boost { +namespace atomics { +namespace detail { + +//! Generic implementation of floating point operations +template< typename Base, typename Value, std::size_t Size > +struct emulated_fp_operations : + public Base +{ + typedef Base base_type; + typedef typename base_type::storage_type storage_type; + typedef Value value_type; + + static BOOST_FORCEINLINE value_type fetch_add(storage_type volatile& storage, value_type v, memory_order) BOOST_NOEXCEPT + { + storage_type& s = const_cast< storage_type& >(storage); + lockpool::scoped_lock lock(&storage); + value_type old_val = atomics::detail::bitwise_fp_cast< value_type >(s); + value_type new_val = old_val + v; + s = atomics::detail::bitwise_fp_cast< storage_type >(new_val); + return old_val; + } + + static BOOST_FORCEINLINE value_type fetch_sub(storage_type volatile& storage, value_type v, memory_order) BOOST_NOEXCEPT + { + storage_type& s = const_cast< storage_type& >(storage); + lockpool::scoped_lock lock(&storage); + value_type old_val = atomics::detail::bitwise_fp_cast< value_type >(s); + value_type new_val = old_val - v; + s = atomics::detail::bitwise_fp_cast< storage_type >(new_val); + return old_val; + } +}; + +template< typename Base, typename Value, std::size_t Size > +struct fp_operations< Base, Value, Size, false > : + public emulated_fp_operations< Base, Value, Size > +{ +}; + +} // namespace detail +} // namespace atomics +} // namespace boost + +#endif // BOOST_ATOMIC_DETAIL_FP_OPS_EMULATED_HPP_INCLUDED_ diff --git a/include/boost/atomic/detail/fp_ops_generic.hpp b/include/boost/atomic/detail/fp_ops_generic.hpp new file mode 100644 index 0000000..b83e85a --- /dev/null +++ b/include/boost/atomic/detail/fp_ops_generic.hpp @@ -0,0 +1,83 @@ +/* + * 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) 2018 Andrey Semashev + */ +/*! + * \file atomic/detail/fp_ops_generic.hpp + * + * This header contains generic implementation of the floating point atomic operations. + */ + +#ifndef BOOST_ATOMIC_DETAIL_FP_OPS_GENERIC_HPP_INCLUDED_ +#define BOOST_ATOMIC_DETAIL_FP_OPS_GENERIC_HPP_INCLUDED_ + +#include +#include +#include +#include +#include +#include + +#ifdef BOOST_HAS_PRAGMA_ONCE +#pragma once +#endif + +namespace boost { +namespace atomics { +namespace detail { + +//! Generic implementation of floating point operations +template< typename Base, typename Value, std::size_t Size > +struct generic_fp_operations : + public Base +{ + typedef Base base_type; + typedef typename base_type::storage_type storage_type; + typedef Value value_type; + + static BOOST_FORCEINLINE value_type fetch_add(storage_type volatile& storage, value_type v, memory_order order) BOOST_NOEXCEPT + { + storage_type old_storage, new_storage; + value_type old_val, new_val; + atomics::detail::non_atomic_load(storage, old_storage); + do + { + old_val = atomics::detail::bitwise_fp_cast< value_type >(old_storage); + new_val = old_val + v; + new_storage = atomics::detail::bitwise_fp_cast< storage_type >(new_val); + } + while (!base_type::compare_exchange_weak(storage, old_storage, new_storage, order, memory_order_relaxed)); + return old_val; + } + + static BOOST_FORCEINLINE value_type fetch_sub(storage_type volatile& storage, value_type v, memory_order order) BOOST_NOEXCEPT + { + storage_type old_storage, new_storage; + value_type old_val, new_val; + atomics::detail::non_atomic_load(storage, old_storage); + do + { + old_val = atomics::detail::bitwise_fp_cast< value_type >(old_storage); + new_val = old_val - v; + new_storage = atomics::detail::bitwise_fp_cast< storage_type >(new_val); + } + while (!base_type::compare_exchange_weak(storage, old_storage, new_storage, order, memory_order_relaxed)); + return old_val; + } +}; + +// Default fp_operations template definition will be used unless specialized for a specific platform +template< typename Base, typename Value, std::size_t Size > +struct fp_operations< Base, Value, Size, true > : + public generic_fp_operations< Base, Value, Size > +{ +}; + +} // namespace detail +} // namespace atomics +} // namespace boost + +#endif // BOOST_ATOMIC_DETAIL_FP_OPS_GENERIC_HPP_INCLUDED_ diff --git a/include/boost/atomic/detail/int_sizes.hpp b/include/boost/atomic/detail/int_sizes.hpp index eada4ff..2a9757c 100644 --- a/include/boost/atomic/detail/int_sizes.hpp +++ b/include/boost/atomic/detail/int_sizes.hpp @@ -39,7 +39,7 @@ #if defined(__SIZEOF_POINTER__) #define BOOST_ATOMIC_DETAIL_SIZEOF_POINTER __SIZEOF_POINTER__ #elif defined(_MSC_VER) -#if defined(_M_AMD64) || defined(_M_IA64) +#if defined(_M_AMD64) || defined(_M_ARM64) || defined(_M_IA64) #define BOOST_ATOMIC_DETAIL_SIZEOF_POINTER 8 #else #define BOOST_ATOMIC_DETAIL_SIZEOF_POINTER 4 @@ -117,7 +117,7 @@ #include #include - #if defined(_MSC_VER) && ( _MSC_VER <= 1310 || defined(UNDER_CE) && _MSC_VER <= 1500 ) +#if defined(_MSC_VER) && (_MSC_VER <= 1310 || defined(UNDER_CE) && _MSC_VER <= 1500) // MSVC 7.1 and MSVC 8 (arm) define WCHAR_MAX to a value not suitable for constant expressions #define BOOST_ATOMIC_DETAIL_SIZEOF_WCHAR_T 2 #elif (WCHAR_MAX + 0) == 0xff || (WCHAR_MAX + 0) == 0x7f @@ -134,7 +134,7 @@ #if !defined(BOOST_ATOMIC_DETAIL_SIZEOF_SHORT) || !defined(BOOST_ATOMIC_DETAIL_SIZEOF_INT) ||\ !defined(BOOST_ATOMIC_DETAIL_SIZEOF_LONG) || !defined(BOOST_ATOMIC_DETAIL_SIZEOF_LLONG) ||\ !defined(BOOST_ATOMIC_DETAIL_SIZEOF_WCHAR_T) -#error Boost.Atomic: Failed to determine builtin integer sizes, the target platform is not supported. Please, report to the developers. +#error Boost.Atomic: Failed to determine builtin integer sizes, the target platform is not supported. Please, report to the developers (patches are welcome). #endif #endif // BOOST_ATOMIC_DETAIL_INT_SIZES_HPP_INCLUDED_ diff --git a/include/boost/atomic/detail/platform.hpp b/include/boost/atomic/detail/platform.hpp index 117dff2..df4cc30 100644 --- a/include/boost/atomic/detail/platform.hpp +++ b/include/boost/atomic/detail/platform.hpp @@ -140,12 +140,24 @@ #define BOOST_ATOMIC_EMULATED #endif +#if !defined(BOOST_ATOMIC_DETAIL_FP_BACKEND) +#define BOOST_ATOMIC_DETAIL_FP_BACKEND generic +#define BOOST_ATOMIC_DETAIL_FP_BACKEND_GENERIC +#endif + #if !defined(BOOST_ATOMIC_DETAIL_EXTRA_BACKEND) #define BOOST_ATOMIC_DETAIL_EXTRA_BACKEND generic #define BOOST_ATOMIC_DETAIL_EXTRA_BACKEND_GENERIC #endif +#if !defined(BOOST_ATOMIC_DETAIL_EXTRA_FP_BACKEND) +#define BOOST_ATOMIC_DETAIL_EXTRA_FP_BACKEND generic +#define BOOST_ATOMIC_DETAIL_EXTRA_FP_BACKEND_GENERIC +#endif + #define BOOST_ATOMIC_DETAIL_BACKEND_HEADER(prefix) +#define BOOST_ATOMIC_DETAIL_FP_BACKEND_HEADER(prefix) #define BOOST_ATOMIC_DETAIL_EXTRA_BACKEND_HEADER(prefix) +#define BOOST_ATOMIC_DETAIL_EXTRA_FP_BACKEND_HEADER(prefix) #endif // BOOST_ATOMIC_DETAIL_PLATFORM_HPP_INCLUDED_ diff --git a/include/boost/atomic/detail/storage_type.hpp b/include/boost/atomic/detail/storage_type.hpp index 2f21526..5d824d3 100644 --- a/include/boost/atomic/detail/storage_type.hpp +++ b/include/boost/atomic/detail/storage_type.hpp @@ -196,11 +196,8 @@ struct make_storage_type< 16u > template< typename T > struct storage_size_of { - enum _ - { - size = sizeof(T), - value = (size == 3 ? 4 : (size >= 5 && size <= 7 ? 8 : (size >= 9 && size <= 15 ? 16 : size))) - }; + static BOOST_CONSTEXPR_OR_CONST std::size_t size = sizeof(T); + static BOOST_CONSTEXPR_OR_CONST std::size_t value = (size == 3u ? 4u : (size >= 5u && size <= 7u ? 8u : (size >= 9u && size <= 15u ? 16u : size))); }; } // namespace detail diff --git a/include/boost/atomic/detail/string_ops.hpp b/include/boost/atomic/detail/string_ops.hpp index 4982c53..ce145b9 100644 --- a/include/boost/atomic/detail/string_ops.hpp +++ b/include/boost/atomic/detail/string_ops.hpp @@ -27,9 +27,13 @@ #if __has_builtin(__builtin_memcmp) #define BOOST_ATOMIC_DETAIL_HAS_BUILTIN_MEMCMP #endif +#if __has_builtin(__builtin_memset) +#define BOOST_ATOMIC_DETAIL_HAS_BUILTIN_MEMSET +#endif #elif defined(BOOST_GCC) #define BOOST_ATOMIC_DETAIL_HAS_BUILTIN_MEMCPY #define BOOST_ATOMIC_DETAIL_HAS_BUILTIN_MEMCMP +#define BOOST_ATOMIC_DETAIL_HAS_BUILTIN_MEMSET #endif #if defined(BOOST_ATOMIC_DETAIL_HAS_BUILTIN_MEMCPY) @@ -44,7 +48,13 @@ #define BOOST_ATOMIC_DETAIL_MEMCMP std::memcmp #endif -#if !defined(BOOST_ATOMIC_DETAIL_HAS_BUILTIN_MEMCPY) || !defined(BOOST_ATOMIC_DETAIL_HAS_BUILTIN_MEMCMP) +#if defined(BOOST_ATOMIC_DETAIL_HAS_BUILTIN_MEMSET) +#define BOOST_ATOMIC_DETAIL_MEMSET __builtin_memset +#else +#define BOOST_ATOMIC_DETAIL_MEMSET std::memset +#endif + +#if !defined(BOOST_ATOMIC_DETAIL_HAS_BUILTIN_MEMCPY) || !defined(BOOST_ATOMIC_DETAIL_HAS_BUILTIN_MEMCMP) || !defined(BOOST_ATOMIC_DETAIL_HAS_BUILTIN_MEMSET) #include #endif diff --git a/include/boost/atomic/detail/type_traits/is_floating_point.hpp b/include/boost/atomic/detail/type_traits/is_floating_point.hpp new file mode 100644 index 0000000..cdcf269 --- /dev/null +++ b/include/boost/atomic/detail/type_traits/is_floating_point.hpp @@ -0,0 +1,42 @@ +/* + * 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) 2018 Andrey Semashev + */ +/*! + * \file atomic/detail/type_traits/is_floating_point.hpp + * + * This header defines \c is_floating_point type trait + */ + +#ifndef BOOST_ATOMIC_DETAIL_TYPE_TRAITS_IS_FLOATING_POINT_HPP_INCLUDED_ +#define BOOST_ATOMIC_DETAIL_TYPE_TRAITS_IS_FLOATING_POINT_HPP_INCLUDED_ + +#include +#if !defined(BOOST_ATOMIC_DETAIL_NO_CXX11_HDR_TYPE_TRAITS) +#include +#else +#include +#endif + +#ifdef BOOST_HAS_PRAGMA_ONCE +#pragma once +#endif + +namespace boost { +namespace atomics { +namespace detail { + +#if !defined(BOOST_ATOMIC_DETAIL_NO_CXX11_HDR_TYPE_TRAITS) +using std::is_floating_point; +#else +using boost::is_floating_point; +#endif + +} // namespace detail +} // namespace atomics +} // namespace boost + +#endif // BOOST_ATOMIC_DETAIL_TYPE_TRAITS_IS_FLOATING_POINT_HPP_INCLUDED_ diff --git a/include/boost/atomic/detail/type_traits/is_iec559.hpp b/include/boost/atomic/detail/type_traits/is_iec559.hpp new file mode 100644 index 0000000..299c4f0 --- /dev/null +++ b/include/boost/atomic/detail/type_traits/is_iec559.hpp @@ -0,0 +1,47 @@ +/* + * 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) 2018 Andrey Semashev + */ +/*! + * \file atomic/detail/type_traits/is_iec559.hpp + * + * This header defines \c is_iec559 type trait + */ + +#ifndef BOOST_ATOMIC_DETAIL_TYPE_TRAITS_IS_IEC559_HPP_INCLUDED_ +#define BOOST_ATOMIC_DETAIL_TYPE_TRAITS_IS_IEC559_HPP_INCLUDED_ + +#include +#include + +#ifdef BOOST_HAS_PRAGMA_ONCE +#pragma once +#endif + +namespace boost { +namespace atomics { +namespace detail { + +template< typename T > +struct is_iec559 +{ + static BOOST_CONSTEXPR_OR_CONST bool value = !!std::numeric_limits< T >::is_iec559; +}; + +#if defined(BOOST_HAS_FLOAT128) +// libstdc++ does not specialize numeric_limits for __float128 +template< > +struct is_iec559< boost::float128_type > +{ + static BOOST_CONSTEXPR_OR_CONST bool value = true; +}; +#endif // defined(BOOST_HAS_FLOAT128) + +} // namespace detail +} // namespace atomics +} // namespace boost + +#endif // BOOST_ATOMIC_DETAIL_TYPE_TRAITS_IS_IEC559_HPP_INCLUDED_ diff --git a/test/api_test_helpers.hpp b/test/api_test_helpers.hpp index 57f95b4..a4ed4f0 100644 --- a/test/api_test_helpers.hpp +++ b/test/api_test_helpers.hpp @@ -1,5 +1,5 @@ // Copyright (c) 2011 Helge Bahmann -// Copyright (c) 2017 Andrey Semashev +// Copyright (c) 2017 - 2018 Andrey Semashev // // Distributed under the Boost Software License, Version 1.0. // See accompanying file LICENSE_1_0.txt or copy at @@ -88,6 +88,14 @@ struct test_stream_type 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 = {}; @@ -96,6 +104,8 @@ const test_stream_type 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 @@ -738,7 +748,7 @@ void test_bit_operators(T value, T delta) template void do_test_integral_api(boost::false_type) { - BOOST_TEST( sizeof(boost::atomic) >= sizeof(T)); + BOOST_TEST(sizeof(boost::atomic) >= sizeof(T)); test_base_operators(42, 43, 44); test_additive_operators(42, 17); @@ -774,6 +784,123 @@ inline void test_integral_api(void) 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(42, 43, 44); + + test_fp_additive_operators(42, 17); + test_fp_additive_operators(-42, -17); + + test_fp_negation(); +#endif +} + + template void test_pointer_api(void) { diff --git a/test/fallback_api.cpp b/test/fallback_api.cpp index 99d3a91..c7fd76e 100644 --- a/test/fallback_api.cpp +++ b/test/fallback_api.cpp @@ -35,6 +35,17 @@ int main(int, char *[]) test_integral_api(); test_integral_api(); test_integral_api(); +#if defined(BOOST_HAS_INT128) + test_integral_api(); + test_integral_api(); +#endif + + test_floating_point_api(); + test_floating_point_api(); + test_floating_point_api(); +#if defined(BOOST_HAS_FLOAT128) + test_floating_point_api(); +#endif test_pointer_api(); diff --git a/test/lockfree.cpp b/test/lockfree.cpp index 33c62e2..f1cd9a1 100644 --- a/test/lockfree.cpp +++ b/test/lockfree.cpp @@ -191,6 +191,26 @@ int main(int, char *[]) 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))))); + +#ifdef 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 || diff --git a/test/native_api.cpp b/test/native_api.cpp index fd4ba0f..a67d99b 100644 --- a/test/native_api.cpp +++ b/test/native_api.cpp @@ -45,6 +45,13 @@ int main(int, char *[]) test_constexpr_ctor(); test_constexpr_ctor(); + test_floating_point_api(); + test_floating_point_api(); + test_floating_point_api(); +#if defined(BOOST_HAS_FLOAT128) + test_floating_point_api(); +#endif + test_pointer_api(); test_enum_api(); diff --git a/test/value_with_epsilon.hpp b/test/value_with_epsilon.hpp new file mode 100644 index 0000000..32180a7 --- /dev/null +++ b/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_