atomic.h 3.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104
  1. #ifndef AL_ATOMIC_H
  2. #define AL_ATOMIC_H
  3. #include <atomic>
  4. #include <cstddef>
  5. #include <memory>
  6. #include "almalloc.h"
  7. template<typename T>
  8. auto IncrementRef(std::atomic<T> &ref) noexcept
  9. { return ref.fetch_add(1u, std::memory_order_acq_rel)+1u; }
  10. template<typename T>
  11. auto DecrementRef(std::atomic<T> &ref) noexcept
  12. { return ref.fetch_sub(1u, std::memory_order_acq_rel)-1u; }
  13. /* WARNING: A livelock is theoretically possible if another thread keeps
  14. * changing the head without giving this a chance to actually swap in the new
  15. * one (practically impossible with this little code, but...).
  16. */
  17. template<typename T>
  18. inline void AtomicReplaceHead(std::atomic<T> &head, T newhead)
  19. {
  20. T first_ = head.load(std::memory_order_acquire);
  21. do {
  22. newhead->next.store(first_, std::memory_order_relaxed);
  23. } while(!head.compare_exchange_weak(first_, newhead,
  24. std::memory_order_acq_rel, std::memory_order_acquire));
  25. }
  26. namespace al {
  27. template<typename T, typename D=std::default_delete<T>>
  28. class atomic_unique_ptr {
  29. std::atomic<gsl::owner<T*>> mPointer{};
  30. using unique_ptr_t = std::unique_ptr<T,D>;
  31. public:
  32. atomic_unique_ptr() = default;
  33. atomic_unique_ptr(const atomic_unique_ptr&) = delete;
  34. explicit atomic_unique_ptr(std::nullptr_t) noexcept { }
  35. explicit atomic_unique_ptr(gsl::owner<T*> ptr) noexcept : mPointer{ptr} { }
  36. explicit atomic_unique_ptr(unique_ptr_t&& rhs) noexcept : mPointer{rhs.release()} { }
  37. ~atomic_unique_ptr()
  38. {
  39. if(auto ptr = mPointer.exchange(nullptr, std::memory_order_relaxed))
  40. D{}(ptr);
  41. }
  42. auto operator=(const atomic_unique_ptr&) -> atomic_unique_ptr& = delete;
  43. auto operator=(std::nullptr_t) noexcept -> atomic_unique_ptr&
  44. {
  45. if(auto ptr = mPointer.exchange(nullptr))
  46. D{}(ptr);
  47. return *this;
  48. }
  49. auto operator=(unique_ptr_t&& rhs) noexcept -> atomic_unique_ptr&
  50. {
  51. if(auto ptr = mPointer.exchange(rhs.release()))
  52. D{}(ptr);
  53. return *this;
  54. }
  55. [[nodiscard]]
  56. auto load(std::memory_order m=std::memory_order_seq_cst) const noexcept -> T*
  57. { return mPointer.load(m); }
  58. void store(std::nullptr_t, std::memory_order m=std::memory_order_seq_cst) noexcept
  59. {
  60. if(auto oldptr = mPointer.exchange(nullptr, m))
  61. D{}(oldptr);
  62. }
  63. void store(gsl::owner<T*> ptr, std::memory_order m=std::memory_order_seq_cst) noexcept
  64. {
  65. if(auto oldptr = mPointer.exchange(ptr, m))
  66. D{}(oldptr);
  67. }
  68. void store(unique_ptr_t&& ptr, std::memory_order m=std::memory_order_seq_cst) noexcept
  69. {
  70. if(auto oldptr = mPointer.exchange(ptr.release(), m))
  71. D{}(oldptr);
  72. }
  73. [[nodiscard]]
  74. auto exchange(std::nullptr_t, std::memory_order m=std::memory_order_seq_cst) noexcept -> unique_ptr_t
  75. { return unique_ptr_t{mPointer.exchange(nullptr, m)}; }
  76. [[nodiscard]]
  77. auto exchange(gsl::owner<T*> ptr, std::memory_order m=std::memory_order_seq_cst) noexcept -> unique_ptr_t
  78. { return unique_ptr_t{mPointer.exchange(ptr, m)}; }
  79. [[nodiscard]]
  80. auto exchange(std::unique_ptr<T>&& ptr, std::memory_order m=std::memory_order_seq_cst) noexcept -> unique_ptr_t
  81. { return unique_ptr_t{mPointer.exchange(ptr.release(), m)}; }
  82. [[nodiscard]]
  83. auto is_lock_free() const noexcept -> bool { return mPointer.is_lock_free(); }
  84. static constexpr auto is_always_lock_free = std::atomic<gsl::owner<T*>>::is_always_lock_free;
  85. };
  86. } // namespace al
  87. #endif /* AL_ATOMIC_H */