🛠️🐜 Antkeeper superbuild with dependencies included https://antkeeper.com
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

120 lines
3.3 KiB

  1. #ifndef INTRUSIVE_PTR_H
  2. #define INTRUSIVE_PTR_H
  3. #include <utility>
  4. #include "atomic.h"
  5. #include "opthelpers.h"
  6. namespace al {
  7. template<typename T>
  8. class intrusive_ref {
  9. RefCount mRef{1u};
  10. public:
  11. unsigned int add_ref() noexcept { return IncrementRef(mRef); }
  12. unsigned int release() noexcept
  13. {
  14. auto ref = DecrementRef(mRef);
  15. if UNLIKELY(ref == 0)
  16. delete static_cast<T*>(this);
  17. return ref;
  18. }
  19. /**
  20. * Release only if doing so would not bring the object to 0 references and
  21. * delete it. Returns false if the object could not be released.
  22. *
  23. * NOTE: The caller is responsible for handling a failed release, as it
  24. * means the object has no other references and needs to be be deleted
  25. * somehow.
  26. */
  27. bool releaseIfNoDelete() noexcept
  28. {
  29. auto val = mRef.load(std::memory_order_acquire);
  30. while(val > 1 && !mRef.compare_exchange_strong(val, val-1, std::memory_order_acq_rel))
  31. {
  32. /* val was updated with the current value on failure, so just try
  33. * again.
  34. */
  35. }
  36. return val >= 2;
  37. }
  38. };
  39. template<typename T>
  40. class intrusive_ptr {
  41. T *mPtr{nullptr};
  42. public:
  43. intrusive_ptr() noexcept = default;
  44. intrusive_ptr(const intrusive_ptr &rhs) noexcept : mPtr{rhs.mPtr}
  45. { if(mPtr) mPtr->add_ref(); }
  46. intrusive_ptr(intrusive_ptr&& rhs) noexcept : mPtr{rhs.mPtr}
  47. { rhs.mPtr = nullptr; }
  48. intrusive_ptr(std::nullptr_t) noexcept { }
  49. explicit intrusive_ptr(T *ptr) noexcept : mPtr{ptr} { }
  50. ~intrusive_ptr() { if(mPtr) mPtr->release(); }
  51. intrusive_ptr& operator=(const intrusive_ptr &rhs) noexcept
  52. {
  53. static_assert(noexcept(std::declval<T*>()->release()), "release must be noexcept");
  54. if(rhs.mPtr) rhs.mPtr->add_ref();
  55. if(mPtr) mPtr->release();
  56. mPtr = rhs.mPtr;
  57. return *this;
  58. }
  59. intrusive_ptr& operator=(intrusive_ptr&& rhs) noexcept
  60. {
  61. if(likely(&rhs != this))
  62. {
  63. if(mPtr) mPtr->release();
  64. mPtr = std::exchange(rhs.mPtr, nullptr);
  65. }
  66. return *this;
  67. }
  68. explicit operator bool() const noexcept { return mPtr != nullptr; }
  69. T& operator*() const noexcept { return *mPtr; }
  70. T* operator->() const noexcept { return mPtr; }
  71. T* get() const noexcept { return mPtr; }
  72. void reset(T *ptr=nullptr) noexcept
  73. {
  74. if(mPtr)
  75. mPtr->release();
  76. mPtr = ptr;
  77. }
  78. T* release() noexcept { return std::exchange(mPtr, nullptr); }
  79. void swap(intrusive_ptr &rhs) noexcept { std::swap(mPtr, rhs.mPtr); }
  80. void swap(intrusive_ptr&& rhs) noexcept { std::swap(mPtr, rhs.mPtr); }
  81. };
  82. #define AL_DECL_OP(op) \
  83. template<typename T> \
  84. inline bool operator op(const intrusive_ptr<T> &lhs, const T *rhs) noexcept \
  85. { return lhs.get() op rhs; } \
  86. template<typename T> \
  87. inline bool operator op(const T *lhs, const intrusive_ptr<T> &rhs) noexcept \
  88. { return lhs op rhs.get(); }
  89. AL_DECL_OP(==)
  90. AL_DECL_OP(!=)
  91. AL_DECL_OP(<=)
  92. AL_DECL_OP(>=)
  93. AL_DECL_OP(<)
  94. AL_DECL_OP(>)
  95. #undef AL_DECL_OP
  96. } // namespace al
  97. #endif /* INTRUSIVE_PTR_H */