use core::sync::atomic::{Ordering, fence};
use osom_lib_alloc::traits::Allocator;
use osom_lib_reprc::traits::ReprC;
use osom_lib_try_clone::TryClone;
use crate::{
consts::MAX_REFERENCES,
errors::{MaxReferencesExceededError, WeakUpgradeError},
};
use super::{carc::CArc, internal::InternalArc};
#[repr(transparent)]
#[must_use]
#[derive(Debug)]
pub struct CWeak<T, TAllocator: Allocator> {
internal: InternalArc<T, TAllocator>,
}
unsafe impl<T: ReprC, TAllocator: Allocator> ReprC for CWeak<T, TAllocator> {
const CHECK: () = const {
osom_lib_reprc::hidden::is_reprc::<T>();
osom_lib_reprc::hidden::is_reprc::<InternalArc<T, TAllocator>>();
};
}
impl<T, TAllocator: Allocator> CWeak<T, TAllocator> {
#[inline(always)]
#[must_use]
pub fn strong_count(&self) -> u32 {
self.internal.strong().load(Ordering::Relaxed)
}
#[inline(always)]
#[must_use]
pub fn weak_count(&self) -> u32 {
self.internal.weak().load(Ordering::Relaxed)
}
#[inline]
#[must_use]
pub fn data(&self) -> &T
where
T: Copy,
{
self.internal.data()
}
pub fn upgrade(&self) -> Result<CArc<T, TAllocator>, WeakUpgradeError> {
let strong = self.internal.strong();
let mut current = strong.load(Ordering::Relaxed);
loop {
if current == 0 {
return Err(WeakUpgradeError::NoStrongReferencesAlive);
}
if current >= MAX_REFERENCES {
return Err(WeakUpgradeError::MaxReferencesExceeded);
}
match strong.compare_exchange_weak(current, current + 1, Ordering::Acquire, Ordering::Relaxed) {
Ok(_) => return Ok(CArc::from_internal(self.internal.clone())),
Err(new) => current = new,
}
}
}
#[inline(always)]
#[must_use]
pub fn abandon(mut self) -> bool {
let result = self.internal_abandon();
core::mem::forget(self);
result
}
#[inline]
pub(super) fn from_internal(internal: InternalArc<T, TAllocator>) -> Self {
Self { internal }
}
fn internal_abandon(&mut self) -> bool {
let internal = unsafe { core::ptr::read(&raw const self.internal) };
let prev = internal.weak().fetch_sub(1, Ordering::Release);
if prev > 1 {
return false;
}
fence(Ordering::Acquire);
unsafe { internal.deallocate_memory() };
true
}
}
impl<T, TAllocator: Allocator> Drop for CWeak<T, TAllocator> {
fn drop(&mut self) {
let _ = self.internal_abandon();
}
}
impl<T, TAllocator: Allocator> Clone for CWeak<T, TAllocator> {
fn clone(&self) -> Self {
self.try_clone()
.expect("CWeak weak reference count is too high. Cannot exceed {MAX_REFERENCES}")
}
}
impl<T, TAllocator: Allocator> TryClone for CWeak<T, TAllocator> {
type Error = MaxReferencesExceededError;
fn try_clone(&self) -> Result<Self, Self::Error> {
let internal_clone = self.internal.clone();
let prev_value = internal_clone.weak().fetch_add(1, Ordering::Relaxed);
if prev_value >= MAX_REFERENCES {
internal_clone.weak().fetch_sub(1, Ordering::Relaxed);
return Err(MaxReferencesExceededError);
}
Ok(Self {
internal: internal_clone,
})
}
}