1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192 |
- // Copyright 2021 Hans Dembinski
- //
- // 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_HISTOGRAM_DETAIL_ATOMIC_NUMBER_HPP
- #define BOOST_HISTOGRAM_DETAIL_ATOMIC_NUMBER_HPP
- #include <atomic>
- #include <boost/histogram/detail/priority.hpp>
- #include <type_traits>
- namespace boost {
- namespace histogram {
- namespace detail {
- // copyable arithmetic type with thread-safe operator++ and operator+=
- template <class T>
- struct atomic_number : std::atomic<T> {
- static_assert(std::is_arithmetic<T>(), "");
- using base_t = std::atomic<T>;
- using std::atomic<T>::atomic;
- atomic_number() noexcept = default;
- atomic_number(const atomic_number& o) noexcept : std::atomic<T>{o.load()} {}
- atomic_number& operator=(const atomic_number& o) noexcept {
- this->store(o.load());
- return *this;
- }
- // not defined for float
- atomic_number& operator++() noexcept {
- increment_impl(static_cast<base_t&>(*this), priority<1>{});
- return *this;
- }
- // operator is not defined for floating point before C++20
- atomic_number& operator+=(const T& x) noexcept {
- add_impl(static_cast<base_t&>(*this), x, priority<1>{});
- return *this;
- }
- // not thread-safe
- atomic_number& operator*=(const T& x) noexcept {
- this->store(this->load() * x);
- return *this;
- }
- // not thread-safe
- atomic_number& operator/=(const T& x) noexcept {
- this->store(this->load() / x);
- return *this;
- }
- private:
- // for integral types
- template <class U = T>
- auto increment_impl(std::atomic<U>& a, priority<1>) noexcept -> decltype(++a) {
- return ++a;
- }
- // fallback implementation for floating point types
- template <class U = T>
- void increment_impl(std::atomic<U>&, priority<0>) noexcept {
- this->operator+=(static_cast<U>(1));
- }
- // always available for integral types, in C++20 also available for float
- template <class U = T>
- static auto add_impl(std::atomic<U>& a, const U& x, priority<1>) noexcept
- -> decltype(a += x) {
- return a += x;
- }
- // pre-C++20 fallback implementation for floating point
- template <class U = T>
- static void add_impl(std::atomic<U>& a, const U& x, priority<0>) noexcept {
- U expected = a.load();
- // if another tread changed `expected` in the meantime, compare_exchange returns
- // false and updates `expected`; we then loop and try to update again;
- // see https://en.cppreference.com/w/cpp/atomic/atomic/compare_exchange
- while (!a.compare_exchange_weak(expected, expected + x))
- ;
- }
- };
- } // namespace detail
- } // namespace histogram
- } // namespace boost
- #endif
|