18 { T{std::declval<size_t>()} };
19 { std::declval<T&>().fetch_increment() } -> std::convertible_to<size_t>;
20 { std::declval<T&>().fetch_decrement() } -> std::convertible_to<size_t>;
21 { std::declval<const T&>().count() } -> std::convertible_to<size_t>;
28 std::atomic<size_t> m_count;
33 size_t fetch_increment()
noexcept {
return m_count.fetch_add(1, std::memory_order::acquire); }
34 [[nodiscard]]
size_t fetch_decrement()
noexcept {
return m_count.fetch_sub(1, std::memory_order::release); }
35 [[nodiscard]]
size_t count()
const noexcept {
return m_count.load(std::memory_order::relaxed); }
46 size_t fetch_increment()
noexcept {
return m_count++; }
47 [[nodiscard]]
size_t fetch_decrement()
noexcept {
return m_count--; }
48 [[nodiscard]]
size_t count()
const noexcept {
return m_count; }
54 template<
typename T, RefCount TCounter = thread_safe_refcount>
62 template<
typename T, RefCount TCounter>
73 size_t use_count() const noexcept {
return m_refcount.count(); }
79 static_assert(std::is_base_of_v<intrusive_refcount<T, TCounter>, T>,
80 "T needs to inherit intrusive_refcount<T>");
81 m_refcount.fetch_increment();
90 static_assert(std::is_nothrow_destructible_v<T>,
"Destructor needs to be noexcept!");
91 static_assert(std::is_base_of_v<intrusive_refcount<T, TCounter>, T>,
92 "T needs to inherit intrusive_refcount<T>");
93 auto cnt = m_refcount.fetch_decrement();
94 if (cnt == 1)
delete static_cast<const T*
>(
this);
98 mutable TCounter m_refcount{0};
100 friend inline void refcounted_add_ref(
const intrusive_refcount<T, TCounter>* ptr)
noexcept {
101 if (ptr) ptr->add_ref();
103 friend inline void refcounted_remove_ref(
const intrusive_refcount<T, TCounter>* ptr)
noexcept {
104 if (ptr) ptr->remove_ref();
108 inline constexpr struct {
117 { refcounted_add_ref(obj) };
118 { refcounted_remove_ref(obj) };
133 constexpr ref() noexcept : m_ptr(
nullptr) {
static_assert(
RefCountable<T>,
"T needs to be refcountable"); }
139 ref(T* ptr,
decltype(adopt_ref)) noexcept : m_ptr{ptr} {
147 ref(T* ptr)
noexcept(
noexcept(refcounted_add_ref(std::declval<T*>()))) : m_ptr{ptr} {
149 if (m_ptr) refcounted_add_ref(m_ptr);
158 ref(
const ref& other)
noexcept(
noexcept(refcounted_add_ref(std::declval<T*>()))) : m_ptr{other.m_ptr} {
159 if (m_ptr) refcounted_add_ref(m_ptr);
162 constexpr ref(
ref&& other) noexcept : m_ptr{std::exchange(other.m_ptr,
nullptr)} {}
165 noexcept(refcounted_add_ref(std::declval<T*>())) &&
noexcept(refcounted_remove_ref(std::declval<T*>()))) {
166 if (&other !=
this)
reset(other.m_ptr);
171 ref&
operator=(
ref&& other)
noexcept(
noexcept(refcounted_remove_ref(std::declval<T*>()))) {
172 if (m_ptr) refcounted_remove_ref(m_ptr);
173 m_ptr = std::exchange(other.m_ptr,
nullptr);
182 noexcept(refcounted_add_ref(std::declval<T*>())) &&
noexcept(refcounted_remove_ref(std::declval<T*>()))) {
183 if (m_ptr) refcounted_remove_ref(m_ptr);
185 if (m_ptr) refcounted_add_ref(m_ptr);
192 void reset(T* ptr,
decltype(adopt_ref))
noexcept(
noexcept(refcounted_remove_ref(std::declval<T*>()))) {
193 if (m_ptr) refcounted_remove_ref(m_ptr);
199 void reset() noexcept(noexcept(refcounted_remove_ref(std::declval<T*>()))) {
reset(
nullptr, adopt_ref); }
202 ~ref() noexcept(noexcept(refcounted_remove_ref(std::declval<T*>()))) {
reset(); }
206 constexpr T&
operator*() const noexcept {
return *m_ptr; }
208 constexpr T*
get() const noexcept {
return m_ptr; }
216 explicit constexpr operator bool() const noexcept {
return m_ptr !=
nullptr; }
218 constexpr bool operator!() const noexcept {
return m_ptr ==
nullptr; }
221 template<RefCountable T,
typename... Args>
222 ref<T> make_ref(Args&&... args) {
223 return ref<T>(
new T(std::forward<Args>(args)...));
237 template<RefCountable T>
239 friend struct std::hash<asyncpp::
atomic_ref<T>>;
240 template<RefCountable T2>
241 friend constexpr auto operator<=>(const atomic_ref<T2>& lhs, const atomic_ref<T2>& rhs) noexcept;
242 template<RefCountable T2>
243 friend constexpr auto operator<=>(const atomic_ref<T2>& lhs, const T2* rhs) noexcept;
244 template<RefCountable T2>
245 friend constexpr auto operator<=>(const T2* lhs, const atomic_ref<T2>& rhs) noexcept;
246 mutable std::atomic<uintptr_t> m_ptr;
248 static constexpr uintptr_t lock_mask = uintptr_t{1} << (
sizeof(uintptr_t) * 8 - 1);
254 inline static void cpu_pause()
noexcept {
255#if defined(__i386) || defined(_M_IX86) || defined(_X86_) || defined(__amd64) || defined(_M_AMD64)
259 __builtin_ia32_pause();
261#elif defined(__arm__) || defined(_ARM) || defined(_M_ARM) || defined(__arm)
265 asm volatile(
"yield");
267#elif defined(__riscv)
268 asm volatile(
"pause");
275 uintptr_t lock()
const noexcept {
277 auto val = m_ptr.fetch_or(lock_mask, std::memory_order_acquire);
278 while ((val & lock_mask) == lock_mask) {
280 val = m_ptr.fetch_or(lock_mask, std::memory_order_acquire);
288 void unlock_with(uintptr_t val)
const noexcept {
289 assert((val & lock_mask) == 0);
290 [[maybe_unused]]
auto res = m_ptr.exchange(val, std::memory_order_release);
291 assert((res & lock_mask) == lock_mask);
307 if ((
reinterpret_cast<uintptr_t
>(ptr.
get()) & lock_mask) != 0)
throw std::logic_error(
"invalid pointer");
308 m_ptr =
reinterpret_cast<uintptr_t
>(ptr.
release());
322 noexcept(refcounted_add_ref(std::declval<T*>())) &&
noexcept(refcounted_remove_ref(std::declval<T*>()))) {
323 if (&other !=
this)
exchange(other.load());
336 ref<T> load() const noexcept(noexcept(refcounted_add_ref(std::declval<T*>()))) {
338 if ((m_ptr.load(std::memory_order_relaxed) & ~lock_mask) == 0)
return 0;
343 auto ptr =
reinterpret_cast<T*
>(val);
345 if (ptr) refcounted_add_ref(ptr);
348 return ref<T>(ptr, adopt_ref);
369 if ((
reinterpret_cast<uintptr_t
>(ptr) & lock_mask) != 0)
throw std::logic_error(
"invalid pointer");
374 unlock_with(
reinterpret_cast<uintptr_t
>(ptr));
376 return ref<T>(
reinterpret_cast<T*
>(val), adopt_ref);
389 operator bool() const noexcept {
return (m_ptr.load(std::memory_order_relaxed) & ~lock_mask) != 0; }
391 bool operator!() const noexcept {
return (m_ptr.load(std::memory_order_relaxed) & ~lock_mask) == 0; }
397 inline constexpr auto operator<=>(
const ref<T>& lhs,
const ref<T>& rhs)
noexcept {
398 return lhs.get() <=> rhs.get();
401 inline constexpr auto operator<=>(
const ref<T>& lhs,
const T* rhs)
noexcept {
402 return lhs.get() <=> rhs;
405 inline constexpr auto operator<=>(
const T* lhs,
const ref<T>& rhs)
noexcept {
406 return lhs <=> rhs.get();
410 inline constexpr auto operator==(
const ref<T>& lhs,
const ref<T>& rhs)
noexcept {
411 return (lhs <=> rhs) == std::strong_ordering::equal;
414 inline constexpr auto operator==(
const ref<T>& lhs,
const T* rhs)
noexcept {
415 return (lhs <=> rhs) == std::strong_ordering::equal;
418 inline constexpr auto operator==(
const T* lhs,
const ref<T>& rhs)
noexcept {
419 return (lhs <=> rhs) == std::strong_ordering::equal;
422 inline constexpr auto operator!=(
const ref<T>& lhs,
const ref<T>& rhs)
noexcept {
423 return (lhs <=> rhs) != std::strong_ordering::equal;
426 inline constexpr auto operator!=(
const ref<T>& lhs,
const T* rhs)
noexcept {
427 return (lhs <=> rhs) != std::strong_ordering::equal;
430 inline constexpr auto operator!=(
const T* lhs,
const ref<T>& rhs)
noexcept {
431 return (lhs <=> rhs) != std::strong_ordering::equal;
434 template<RefCountable T>
435 inline constexpr auto operator<=>(
const atomic_ref<T>& lhs,
const atomic_ref<T>& rhs)
noexcept {
436 return (lhs.m_ptr.load(std::memory_order::relaxed) & ~atomic_ref<T>::lock_mask) <=>
437 (rhs.m_ptr.load(std::memory_order::relaxed) & ~atomic_ref<T>::lock_mask);
439 template<RefCountable T>
440 inline constexpr auto operator<=>(
const atomic_ref<T>& lhs,
const T* rhs)
noexcept {
441 return (lhs.m_ptr.load(std::memory_order::relaxed) & ~atomic_ref<T>::lock_mask) <=>
442 reinterpret_cast<uintptr_t
>(rhs);
444 template<RefCountable T>
445 inline constexpr auto operator<=>(
const T* lhs,
const atomic_ref<T>& rhs)
noexcept {
446 return reinterpret_cast<uintptr_t
>(lhs) <=>
447 (rhs.m_ptr.load(std::memory_order::relaxed) & ~atomic_ref<T>::lock_mask);
451 inline constexpr auto operator==(
const atomic_ref<T>& lhs,
const atomic_ref<T>& rhs)
noexcept {
452 return (lhs <=> rhs) == std::strong_ordering::equal;
455 inline constexpr auto operator==(
const atomic_ref<T>& lhs,
const T* rhs)
noexcept {
456 return (lhs <=> rhs) == std::strong_ordering::equal;
459 inline constexpr auto operator==(
const T* lhs,
const atomic_ref<T>& rhs)
noexcept {
460 return (lhs <=> rhs) == std::strong_ordering::equal;
463 inline constexpr auto operator!=(
const atomic_ref<T>& lhs,
const atomic_ref<T>& rhs)
noexcept {
464 return (lhs <=> rhs) != std::strong_ordering::equal;
467 inline constexpr auto operator!=(
const atomic_ref<T>& lhs,
const T* rhs)
noexcept {
468 return (lhs <=> rhs) != std::strong_ordering::equal;
471 inline constexpr auto operator!=(
const T* lhs,
const atomic_ref<T>& rhs)
noexcept {
472 return (lhs <=> rhs) != std::strong_ordering::equal;
475 template<
typename T,
typename U>
476 ref<T> static_ref_cast(
const ref<U>& rhs)
noexcept {
477 return ref<T>(
static_cast<T*
>(rhs.get()));
479 template<
typename T,
typename U>
480 ref<T> static_ref_cast(ref<U>&& rhs)
noexcept {
481 return ref<T>(
static_cast<T*
>(rhs.release()), adopt_ref);
483 template<
typename T,
typename U>
484 ref<T> const_ref_cast(
const ref<U>& rhs)
noexcept {
485 return ref<T>(
const_cast<T*
>(rhs.get()));
487 template<
typename T,
typename U>
488 ref<T> const_ref_cast(ref<U>&& rhs)
noexcept {
489 return ref<T>(
const_cast<T*
>(rhs.release()), adopt_ref);
491 template<
typename T,
typename U>
492 ref<T> dynamic_ref_cast(
const ref<U>& rhs)
noexcept {
493 return ref<T>(
dynamic_cast<T*
>(rhs.get()));
495 template<
typename T,
typename U>
496 ref<T> dynamic_ref_cast(ref<U>&& rhs)
noexcept {
497 auto ptr =
dynamic_cast<T*
>(rhs.get());
498 if (!ptr)
return ref<T>();
502 return ref<T>(ptr, adopt_ref);
509struct std::hash<asyncpp::ref<T>> {
510 constexpr size_t operator()(
const asyncpp::ref<T>& rhs)
const noexcept {
return std::hash<void*>{}(rhs.get()); }
515struct std::hash<asyncpp::atomic_ref<T>> {
517 auto ptr = rhs.m_ptr.load(std::memory_order::relaxed) & ~asyncpp::atomic_ref<T>::lock_mask;
518 return std::hash<uintptr_t>{}(ptr);
Thread safe reference handle.
Definition ref.h:238
ref< T > exchange(ref< T > hdl)
Reset the handle with a new value.
Definition ref.h:367
ref< T > exchange(const atomic_ref< T > &hdl)
Reset the handle with a new value.
Definition ref.h:382
~atomic_ref() noexcept(noexcept(refcounted_remove_ref(std::declval< T * >())))
Destructor.
Definition ref.h:334
atomic_ref(ref< T > ptr)
Construct a new atomic_ref object.
Definition ref.h:306
void reset() noexcept(noexcept(refcounted_remove_ref(std::declval< T * >())))
Reset the pointer to nullptr.
Definition ref.h:386
atomic_ref & operator=(atomic_ref< T > &&other) noexcept(noexcept(refcounted_remove_ref(std::declval< T * >())))
Move assignment operator.
Definition ref.h:328
void store(const atomic_ref< T > &hdl)
Store a new value and destroy the old one.
Definition ref.h:360
atomic_ref & operator=(const atomic_ref< T > &other) noexcept(noexcept(refcounted_add_ref(std::declval< T * >())) &&noexcept(refcounted_remove_ref(std::declval< T * >())))
Assignment operator.
Definition ref.h:321
ref< T > load() const noexcept(noexcept(refcounted_add_ref(std::declval< T * >())))
Get the contained value.
Definition ref.h:336
bool operator!() const noexcept
Check if the handle contains no pointer.
Definition ref.h:391
atomic_ref & operator=(ref< T > &&other)
Move assignment operator.
Definition ref.h:316
atomic_ref & operator=(const ref< T > &other)
Assignment operator.
Definition ref.h:311
T * release() noexcept
Release the contained pointer.
Definition ref.h:384
constexpr atomic_ref() noexcept
Construct an empty atomic_ref.
Definition ref.h:298
void store(ref< T > hdl)
Store a new value and destroy the old one.
Definition ref.h:355
ref< T > operator->() const noexcept(noexcept(refcounted_add_ref(std::declval< T * >())))
Dereference this handle.
Definition ref.h:393
Intrusive refcounting base class.
Definition ref.h:63
void remove_ref() const noexcept
Decrement the reference count and delete the object if the last reference is removed.
Definition ref.h:89
size_t use_count() const noexcept
Get the current use_count of this object.
Definition ref.h:73
void add_ref() const noexcept
Increment the reference count.
Definition ref.h:78
Reference count handle.
Definition ref.h:126
constexpr T * release() noexcept
Release the contained pointer.
Definition ref.h:210
void reset() noexcept(noexcept(refcounted_remove_ref(std::declval< T * >())))
Reset the handle to nullptr.
Definition ref.h:199
ref & operator=(const ref &other) noexcept(noexcept(refcounted_add_ref(std::declval< T * >())) &&noexcept(refcounted_remove_ref(std::declval< T * >())))
Assignment operator.
Definition ref.h:164
void reset(T *ptr, decltype(adopt_ref)) noexcept(noexcept(refcounted_remove_ref(std::declval< T * >())))
Reset the handle with a new value.
Definition ref.h:192
constexpr bool operator!() const noexcept
Check if the handle contains no pointer.
Definition ref.h:218
ref(T *ptr) noexcept(noexcept(refcounted_add_ref(std::declval< T * >())))
Construct a new ref object incrementing the reference count of the passed pointer.
Definition ref.h:147
~ref() noexcept(noexcept(refcounted_remove_ref(std::declval< T * >())))
Destructor.
Definition ref.h:202
constexpr ref() noexcept
Construct an empty ref.
Definition ref.h:133
constexpr T * operator->() const noexcept
Dereference this handle.
Definition ref.h:204
constexpr ref(ref &&other) noexcept
Move constructor.
Definition ref.h:162
constexpr T * get() const noexcept
Get the contained value.
Definition ref.h:208
void reset(T *ptr) noexcept(noexcept(refcounted_add_ref(std::declval< T * >())) &&noexcept(refcounted_remove_ref(std::declval< T * >())))
Reset the handle with a new value.
Definition ref.h:181
ref(T *ptr, decltype(adopt_ref)) noexcept
Construct a new ref object.
Definition ref.h:139
ref & operator=(ref &&other) noexcept(noexcept(refcounted_remove_ref(std::declval< T * >())))
Move assignment operator.
Definition ref.h:171
constexpr T & operator*() const noexcept
Dereference this handle.
Definition ref.h:206
ref(const ref &other) noexcept(noexcept(refcounted_add_ref(std::declval< T * >())))
Construct a new ref object.
Definition ref.h:158
Concept describing a refcount policy for use with intrusive_refcount.
Definition ref.h:17
Concept checking if a type is viable for usage with ref<> (i.e. it provides overloads for refcounted_...
Definition ref.h:116
Threadsafe refcount policy.
Definition ref.h:27
Thread unsafe refcount policy.
Definition ref.h:40