93 lines
2.6 KiB
C++
93 lines
2.6 KiB
C++
// 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
|