Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2025-01-18 09:38:11

0001 // Copyright 2021 Hans Dembinski
0002 //
0003 // Distributed under the Boost Software License, version 1.0.
0004 // (See accompanying file LICENSE_1_0.txt
0005 // or copy at http://www.boost.org/LICENSE_1_0.txt)
0006 
0007 #ifndef BOOST_HISTOGRAM_DETAIL_ATOMIC_NUMBER_HPP
0008 #define BOOST_HISTOGRAM_DETAIL_ATOMIC_NUMBER_HPP
0009 
0010 #include <atomic>
0011 #include <boost/histogram/detail/priority.hpp>
0012 #include <type_traits>
0013 
0014 namespace boost {
0015 namespace histogram {
0016 namespace detail {
0017 
0018 // copyable arithmetic type with thread-safe operator++ and operator+=
0019 template <class T>
0020 struct atomic_number : std::atomic<T> {
0021   static_assert(std::is_arithmetic<T>(), "");
0022 
0023   using base_t = std::atomic<T>;
0024   using std::atomic<T>::atomic;
0025 
0026   atomic_number() noexcept = default;
0027   atomic_number(const atomic_number& o) noexcept : std::atomic<T>{o.load()} {}
0028   atomic_number& operator=(const atomic_number& o) noexcept {
0029     this->store(o.load());
0030     return *this;
0031   }
0032 
0033   // not defined for float
0034   atomic_number& operator++() noexcept {
0035     increment_impl(static_cast<base_t&>(*this), priority<1>{});
0036     return *this;
0037   }
0038 
0039   // operator is not defined for floating point before C++20
0040   atomic_number& operator+=(const T& x) noexcept {
0041     add_impl(static_cast<base_t&>(*this), x, priority<1>{});
0042     return *this;
0043   }
0044 
0045   // not thread-safe
0046   atomic_number& operator*=(const T& x) noexcept {
0047     this->store(this->load() * x);
0048     return *this;
0049   }
0050 
0051   // not thread-safe
0052   atomic_number& operator/=(const T& x) noexcept {
0053     this->store(this->load() / x);
0054     return *this;
0055   }
0056 
0057 private:
0058   // for integral types
0059   template <class U = T>
0060   auto increment_impl(std::atomic<U>& a, priority<1>) noexcept -> decltype(++a) {
0061     return ++a;
0062   }
0063 
0064   // fallback implementation for floating point types
0065   template <class U = T>
0066   void increment_impl(std::atomic<U>&, priority<0>) noexcept {
0067     this->operator+=(static_cast<U>(1));
0068   }
0069 
0070   // always available for integral types, in C++20 also available for float
0071   template <class U = T>
0072   static auto add_impl(std::atomic<U>& a, const U& x, priority<1>) noexcept
0073       -> decltype(a += x) {
0074     return a += x;
0075   }
0076 
0077   // pre-C++20 fallback implementation for floating point
0078   template <class U = T>
0079   static void add_impl(std::atomic<U>& a, const U& x, priority<0>) noexcept {
0080     U expected = a.load();
0081     // if another tread changed `expected` in the meantime, compare_exchange returns
0082     // false and updates `expected`; we then loop and try to update again;
0083     // see https://en.cppreference.com/w/cpp/atomic/atomic/compare_exchange
0084     while (!a.compare_exchange_weak(expected, expected + x))
0085       ;
0086   }
0087 };
0088 } // namespace detail
0089 } // namespace histogram
0090 } // namespace boost
0091 
0092 #endif