Remove a left-over assignment
[openal-soft.git] / common / intrusive_ptr.h
blobcb70b81ed5c2db5e1e52408770bc4430d4fef91f
1 #ifndef INTRUSIVE_PTR_H
2 #define INTRUSIVE_PTR_H
4 #include <atomic>
5 #include <cstddef>
6 #include <utility>
8 #include "atomic.h"
9 #include "opthelpers.h"
12 namespace al {
14 template<typename T>
15 class intrusive_ref {
16 std::atomic<unsigned int> mRef{1u};
18 public:
19 unsigned int add_ref() noexcept { return IncrementRef(mRef); }
20 unsigned int dec_ref() noexcept
22 auto ref = DecrementRef(mRef);
23 if(ref == 0) UNLIKELY
24 delete static_cast<T*>(this);
25 return ref;
28 /**
29 * Release only if doing so would not bring the object to 0 references and
30 * delete it. Returns false if the object could not be released.
32 * NOTE: The caller is responsible for handling a failed release, as it
33 * means the object has no other references and needs to be be deleted
34 * somehow.
36 bool releaseIfNoDelete() noexcept
38 auto val = mRef.load(std::memory_order_acquire);
39 while(val > 1 && !mRef.compare_exchange_strong(val, val-1, std::memory_order_acq_rel))
41 /* val was updated with the current value on failure, so just try
42 * again.
46 return val >= 2;
51 template<typename T> /* NOLINTNEXTLINE(clazy-rule-of-three) False positive */
52 class intrusive_ptr {
53 T *mPtr{nullptr};
55 public:
56 intrusive_ptr() noexcept = default;
57 intrusive_ptr(const intrusive_ptr &rhs) noexcept : mPtr{rhs.mPtr}
58 { if(mPtr) mPtr->add_ref(); }
59 intrusive_ptr(intrusive_ptr&& rhs) noexcept : mPtr{rhs.mPtr}
60 { rhs.mPtr = nullptr; }
61 intrusive_ptr(std::nullptr_t) noexcept { }
62 explicit intrusive_ptr(T *ptr) noexcept : mPtr{ptr} { }
63 ~intrusive_ptr() { if(mPtr) mPtr->dec_ref(); }
65 /* NOLINTBEGIN(bugprone-unhandled-self-assignment)
66 * Self-assignment is handled properly here.
68 intrusive_ptr& operator=(const intrusive_ptr &rhs) noexcept
70 static_assert(noexcept(std::declval<T*>()->dec_ref()), "dec_ref must be noexcept");
72 if(rhs.mPtr) rhs.mPtr->add_ref();
73 if(mPtr) mPtr->dec_ref();
74 mPtr = rhs.mPtr;
75 return *this;
77 /* NOLINTEND(bugprone-unhandled-self-assignment) */
78 intrusive_ptr& operator=(intrusive_ptr&& rhs) noexcept
80 if(&rhs != this) LIKELY
82 if(mPtr) mPtr->dec_ref();
83 mPtr = std::exchange(rhs.mPtr, nullptr);
85 return *this;
88 explicit operator bool() const noexcept { return mPtr != nullptr; }
90 [[nodiscard]] auto operator*() const noexcept -> T& { return *mPtr; }
91 [[nodiscard]] auto operator->() const noexcept -> T* { return mPtr; }
92 [[nodiscard]] auto get() const noexcept -> T* { return mPtr; }
94 void reset(T *ptr=nullptr) noexcept
96 if(mPtr)
97 mPtr->dec_ref();
98 mPtr = ptr;
101 T* release() noexcept { return std::exchange(mPtr, nullptr); }
103 void swap(intrusive_ptr &rhs) noexcept { std::swap(mPtr, rhs.mPtr); }
104 void swap(intrusive_ptr&& rhs) noexcept { std::swap(mPtr, rhs.mPtr); }
107 } // namespace al
109 #endif /* INTRUSIVE_PTR_H */