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 {
245 template<RefCountable T2>
246 friend constexpr auto operator<=>(
const atomic_ref<T2>& lhs,
const T2* rhs)
noexcept {
248 reinterpret_cast<uintptr_t
>(rhs);
250 template<RefCountable T2>
251 friend constexpr auto operator<=>(
const T2* lhs,
const atomic_ref<T2>& rhs)
noexcept {
252 return reinterpret_cast<uintptr_t
>(lhs) <=>
255 mutable std::atomic<uintptr_t> m_ptr;
257 static constexpr uintptr_t lock_mask = uintptr_t{1} << (
sizeof(uintptr_t) * 8 - 1);
263 inline static void cpu_pause()
noexcept {
264#if defined(__i386) || defined(_M_IX86) || defined(_X86_) || defined(__amd64) || defined(_M_AMD64)
268 __builtin_ia32_pause();
270#elif defined(__arm__) || defined(_ARM) || defined(_M_ARM) || defined(__arm)
274 asm volatile(
"yield");
276#elif defined(__riscv)
277 asm volatile(
"pause");
284 uintptr_t lock()
const noexcept {
286 auto val = m_ptr.fetch_or(lock_mask, std::memory_order_acquire);
287 while ((val & lock_mask) == lock_mask) {
289 val = m_ptr.fetch_or(lock_mask, std::memory_order_acquire);
297 void unlock_with(uintptr_t val)
const noexcept {
298 assert((val & lock_mask) == 0);
299 [[maybe_unused]]
auto res = m_ptr.exchange(val, std::memory_order_release);
300 assert((res & lock_mask) == lock_mask);
316 if ((
reinterpret_cast<uintptr_t
>(ptr.
get()) & lock_mask) != 0)
throw std::logic_error(
"invalid pointer");
317 m_ptr =
reinterpret_cast<uintptr_t
>(ptr.
release());
331 noexcept(refcounted_add_ref(std::declval<T*>())) &&
noexcept(refcounted_remove_ref(std::declval<T*>()))) {
332 if (&other !=
this)
exchange(other.load());
345 ref<T> load() const noexcept(noexcept(refcounted_add_ref(std::declval<T*>()))) {
347 if ((m_ptr.load(std::memory_order_relaxed) & ~lock_mask) == 0)
return 0;
352 auto ptr =
reinterpret_cast<T*
>(val);
354 if (ptr) refcounted_add_ref(ptr);
357 return ref<T>(ptr, adopt_ref);
378 if ((
reinterpret_cast<uintptr_t
>(ptr) & lock_mask) != 0)
throw std::logic_error(
"invalid pointer");
383 unlock_with(
reinterpret_cast<uintptr_t
>(ptr));
385 return ref<T>(
reinterpret_cast<T*
>(val), adopt_ref);
398 operator bool() const noexcept {
return (m_ptr.load(std::memory_order_relaxed) & ~lock_mask) != 0; }
400 bool operator!() const noexcept {
return (m_ptr.load(std::memory_order_relaxed) & ~lock_mask) == 0; }
406 inline constexpr auto operator<=>(
const ref<T>& lhs,
const ref<T>& rhs)
noexcept {
407 return lhs.get() <=> rhs.get();
410 inline constexpr auto operator<=>(
const ref<T>& lhs,
const T* rhs)
noexcept {
411 return lhs.get() <=> rhs;
414 inline constexpr auto operator<=>(
const T* lhs,
const ref<T>& rhs)
noexcept {
415 return lhs <=> rhs.get();
419 inline constexpr auto operator==(
const ref<T>& lhs,
const ref<T>& rhs)
noexcept {
420 return (lhs <=> rhs) == std::strong_ordering::equal;
423 inline constexpr auto operator==(
const ref<T>& lhs,
const T* rhs)
noexcept {
424 return (lhs <=> rhs) == std::strong_ordering::equal;
427 inline constexpr auto operator==(
const T* lhs,
const ref<T>& rhs)
noexcept {
428 return (lhs <=> rhs) == std::strong_ordering::equal;
431 inline constexpr auto operator!=(
const ref<T>& lhs,
const ref<T>& rhs)
noexcept {
432 return (lhs <=> rhs) != std::strong_ordering::equal;
435 inline constexpr auto operator!=(
const ref<T>& lhs,
const T* rhs)
noexcept {
436 return (lhs <=> rhs) != std::strong_ordering::equal;
439 inline constexpr auto operator!=(
const T* lhs,
const ref<T>& rhs)
noexcept {
440 return (lhs <=> rhs) != std::strong_ordering::equal;
444 inline constexpr auto operator==(
const atomic_ref<T>& lhs,
const atomic_ref<T>& rhs)
noexcept {
445 return (lhs <=> rhs) == std::strong_ordering::equal;
448 inline constexpr auto operator==(
const atomic_ref<T>& lhs,
const T* rhs)
noexcept {
449 return (lhs <=> rhs) == std::strong_ordering::equal;
452 inline constexpr auto operator==(
const T* lhs,
const atomic_ref<T>& rhs)
noexcept {
453 return (lhs <=> rhs) == std::strong_ordering::equal;
456 inline constexpr auto operator!=(
const atomic_ref<T>& lhs,
const atomic_ref<T>& rhs)
noexcept {
457 return (lhs <=> rhs) != std::strong_ordering::equal;
460 inline constexpr auto operator!=(
const atomic_ref<T>& lhs,
const T* rhs)
noexcept {
461 return (lhs <=> rhs) != std::strong_ordering::equal;
464 inline constexpr auto operator!=(
const T* lhs,
const atomic_ref<T>& rhs)
noexcept {
465 return (lhs <=> rhs) != std::strong_ordering::equal;
468 template<
typename T,
typename U>
469 ref<T> static_ref_cast(
const ref<U>& rhs)
noexcept {
470 return ref<T>(
static_cast<T*
>(rhs.get()));
472 template<
typename T,
typename U>
473 ref<T> static_ref_cast(ref<U>&& rhs)
noexcept {
474 return ref<T>(
static_cast<T*
>(rhs.release()), adopt_ref);
476 template<
typename T,
typename U>
477 ref<T> const_ref_cast(
const ref<U>& rhs)
noexcept {
478 return ref<T>(
const_cast<T*
>(rhs.get()));
480 template<
typename T,
typename U>
481 ref<T> const_ref_cast(ref<U>&& rhs)
noexcept {
482 return ref<T>(
const_cast<T*
>(rhs.release()), adopt_ref);
484 template<
typename T,
typename U>
485 ref<T> dynamic_ref_cast(
const ref<U>& rhs)
noexcept {
486 return ref<T>(
dynamic_cast<T*
>(rhs.get()));
488 template<
typename T,
typename U>
489 ref<T> dynamic_ref_cast(ref<U>&& rhs)
noexcept {
490 auto ptr =
dynamic_cast<T*
>(rhs.get());
491 if (!ptr)
return ref<T>();
495 return ref<T>(ptr, adopt_ref);
502struct std::hash<asyncpp::ref<T>> {
503 constexpr size_t operator()(
const asyncpp::ref<T>& rhs)
const noexcept {
return std::hash<void*>{}(rhs.get()); }
508struct std::hash<asyncpp::atomic_ref<T>> {
510 auto ptr = rhs.m_ptr.load(std::memory_order::relaxed) & ~asyncpp::atomic_ref<T>::lock_mask;
511 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:376
ref< T > exchange(const atomic_ref< T > &hdl)
Reset the handle with a new value.
Definition ref.h:391
~atomic_ref() noexcept(noexcept(refcounted_remove_ref(std::declval< T * >())))
Destructor.
Definition ref.h:343
atomic_ref(ref< T > ptr)
Construct a new atomic_ref object.
Definition ref.h:315
void reset() noexcept(noexcept(refcounted_remove_ref(std::declval< T * >())))
Reset the pointer to nullptr.
Definition ref.h:395
atomic_ref & operator=(atomic_ref< T > &&other) noexcept(noexcept(refcounted_remove_ref(std::declval< T * >())))
Move assignment operator.
Definition ref.h:337
void store(const atomic_ref< T > &hdl)
Store a new value and destroy the old one.
Definition ref.h:369
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:330
ref< T > load() const noexcept(noexcept(refcounted_add_ref(std::declval< T * >())))
Get the contained value.
Definition ref.h:345
bool operator!() const noexcept
Check if the handle contains no pointer.
Definition ref.h:400
atomic_ref & operator=(ref< T > &&other)
Move assignment operator.
Definition ref.h:325
atomic_ref & operator=(const ref< T > &other)
Assignment operator.
Definition ref.h:320
T * release() noexcept
Release the contained pointer.
Definition ref.h:393
constexpr atomic_ref() noexcept
Construct an empty atomic_ref.
Definition ref.h:307
void store(ref< T > hdl)
Store a new value and destroy the old one.
Definition ref.h:364
ref< T > operator->() const noexcept(noexcept(refcounted_add_ref(std::declval< T * >())))
Dereference this handle.
Definition ref.h:402
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