atomic_number.hpp 2.6 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192
  1. // Copyright 2021 Hans Dembinski
  2. //
  3. // Distributed under the Boost Software License, version 1.0.
  4. // (See accompanying file LICENSE_1_0.txt
  5. // or copy at http://www.boost.org/LICENSE_1_0.txt)
  6. #ifndef BOOST_HISTOGRAM_DETAIL_ATOMIC_NUMBER_HPP
  7. #define BOOST_HISTOGRAM_DETAIL_ATOMIC_NUMBER_HPP
  8. #include <atomic>
  9. #include <boost/histogram/detail/priority.hpp>
  10. #include <type_traits>
  11. namespace boost {
  12. namespace histogram {
  13. namespace detail {
  14. // copyable arithmetic type with thread-safe operator++ and operator+=
  15. template <class T>
  16. struct atomic_number : std::atomic<T> {
  17. static_assert(std::is_arithmetic<T>(), "");
  18. using base_t = std::atomic<T>;
  19. using std::atomic<T>::atomic;
  20. atomic_number() noexcept = default;
  21. atomic_number(const atomic_number& o) noexcept : std::atomic<T>{o.load()} {}
  22. atomic_number& operator=(const atomic_number& o) noexcept {
  23. this->store(o.load());
  24. return *this;
  25. }
  26. // not defined for float
  27. atomic_number& operator++() noexcept {
  28. increment_impl(static_cast<base_t&>(*this), priority<1>{});
  29. return *this;
  30. }
  31. // operator is not defined for floating point before C++20
  32. atomic_number& operator+=(const T& x) noexcept {
  33. add_impl(static_cast<base_t&>(*this), x, priority<1>{});
  34. return *this;
  35. }
  36. // not thread-safe
  37. atomic_number& operator*=(const T& x) noexcept {
  38. this->store(this->load() * x);
  39. return *this;
  40. }
  41. // not thread-safe
  42. atomic_number& operator/=(const T& x) noexcept {
  43. this->store(this->load() / x);
  44. return *this;
  45. }
  46. private:
  47. // for integral types
  48. template <class U = T>
  49. auto increment_impl(std::atomic<U>& a, priority<1>) noexcept -> decltype(++a) {
  50. return ++a;
  51. }
  52. // fallback implementation for floating point types
  53. template <class U = T>
  54. void increment_impl(std::atomic<U>&, priority<0>) noexcept {
  55. this->operator+=(static_cast<U>(1));
  56. }
  57. // always available for integral types, in C++20 also available for float
  58. template <class U = T>
  59. static auto add_impl(std::atomic<U>& a, const U& x, priority<1>) noexcept
  60. -> decltype(a += x) {
  61. return a += x;
  62. }
  63. // pre-C++20 fallback implementation for floating point
  64. template <class U = T>
  65. static void add_impl(std::atomic<U>& a, const U& x, priority<0>) noexcept {
  66. U expected = a.load();
  67. // if another tread changed `expected` in the meantime, compare_exchange returns
  68. // false and updates `expected`; we then loop and try to update again;
  69. // see https://en.cppreference.com/w/cpp/atomic/atomic/compare_exchange
  70. while (!a.compare_exchange_weak(expected, expected + x))
  71. ;
  72. }
  73. };
  74. } // namespace detail
  75. } // namespace histogram
  76. } // namespace boost
  77. #endif