use super::ptr::GcPtr;
use crate::GcMemberPtr;
use crate::errors::{Error, ErrorEnum};
use crate::object::ObjectPtr;
use crate::prelude::GcPtrEq;
use std::fmt;
use std::pin::Pin;
use std::ptr;
#[cfg(feature = "multi_thread")]
use super::SingleOrMultiThreadPtr;
#[cfg(feature = "multi_thread")]
use super::sync_ptr::GcMtPtr;
#[cfg(feature = "multi_thread")]
use crate::sync::GcMtMemberPtr;
#[cfg_attr(docsrs, doc(cfg(feature = "weak_pointer")))]
pub struct Weak<T>
where
T: 'static,
{
#[cfg(not(feature = "multi_thread"))]
pub(super) ptr: Option<Pin<ObjectPtr<T>>>,
#[cfg(feature = "multi_thread")]
pub(super) ptr: Option<SingleOrMultiThreadPtr<T>>,
}
impl<T> Weak<T>
where
T: 'static,
{
#[inline]
pub const fn new() -> Self {
Weak { ptr: None }
}
#[cfg(not(feature = "multi_thread"))]
#[inline]
pub(super) const fn new_ptr(ptr: Pin<ObjectPtr<T>>) -> Self {
Weak { ptr: Some(ptr) }
}
#[cfg(feature = "multi_thread")]
#[inline]
pub(super) const fn new_ptr(ptr: SingleOrMultiThreadPtr<T>) -> Self {
Weak { ptr: Some(ptr) }
}
#[inline]
pub(crate) const fn new_from_raw(ptr: Pin<ObjectPtr<T>>) -> Self {
#[cfg(not(feature = "multi_thread"))]
return Self::new_ptr(ptr);
#[cfg(feature = "multi_thread")]
return Self::new_ptr(SingleOrMultiThreadPtr::SingleThread(ptr));
}
#[inline]
pub fn upgrade(&self) -> Result<GcPtr<T>, Error> {
self.ptr
.as_ref()
.ok_or(Error::new(ErrorEnum::UnvaluedWeak))
.and_then(GcPtr::new_from_weak)
}
}
impl<T> GcPtrEq<GcPtr<T>> for Weak<T>
where
T: 'static,
{
#[inline]
fn ptr_eq(this: &Self, other: &GcPtr<T>) -> bool {
GcPtr::ptr_eq(other, this)
}
}
#[cfg(feature = "multi_thread")]
impl<T> GcPtrEq<GcMtPtr<T>> for Weak<T>
where
T: 'static + Send + Sync,
{
#[inline]
fn ptr_eq(this: &Self, other: &GcMtPtr<T>) -> bool {
GcMtPtr::ptr_eq(other, this)
}
}
impl<T> GcPtrEq<GcMemberPtr<T>> for Weak<T>
where
T: 'static,
{
#[inline]
fn ptr_eq(this: &Self, other: &GcMemberPtr<T>) -> bool {
GcMemberPtr::ptr_eq(other, this)
}
}
#[cfg(feature = "multi_thread")]
impl<T> GcPtrEq<GcMtMemberPtr<T>> for Weak<T>
where
T: 'static + Send + Sync,
{
#[inline]
fn ptr_eq(this: &Self, other: &GcMtMemberPtr<T>) -> bool {
GcMtMemberPtr::ptr_eq(other, this)
}
}
impl<T> GcPtrEq<Weak<T>> for Weak<T>
where
T: 'static,
{
#[inline]
fn ptr_eq(this: &Self, other: &Weak<T>) -> bool {
#[cfg(not(feature = "multi_thread"))]
return match (&this.ptr, &other.ptr) {
(Some(this_ptr), Some(other_ptr)) => ptr::eq(&**this_ptr, &**other_ptr),
_ => false,
};
#[cfg(feature = "multi_thread")]
return match (&this.ptr, &other.ptr) {
(
Some(SingleOrMultiThreadPtr::SingleThread(this_ptr)),
Some(SingleOrMultiThreadPtr::SingleThread(other_ptr)),
) => ptr::eq(&**this_ptr, &**other_ptr),
(
Some(SingleOrMultiThreadPtr::MultiThread(this_ptr)),
Some(SingleOrMultiThreadPtr::MultiThread(other_ptr)),
) => ptr::eq(&**this_ptr, &**other_ptr),
_ => false,
};
}
}
#[cfg(feature = "multi_thread")]
impl<T> GcPtrEq<crate::sync::Weak<T>> for Weak<T>
where
T: 'static + Send + Sync,
{
#[inline]
fn ptr_eq(this: &Self, other: &crate::sync::Weak<T>) -> bool {
match (&this.ptr, &other.ptr) {
(Some(SingleOrMultiThreadPtr::MultiThread(this_ptr)), Some(other_ptr)) => {
ptr::eq(&**this_ptr, &**other_ptr)
}
_ => false,
}
}
}
impl<T> Default for Weak<T>
where
T: 'static,
{
#[inline]
fn default() -> Self {
Weak::new()
}
}
impl<T> Clone for Weak<T>
where
T: 'static,
{
#[inline]
fn clone(&self) -> Self {
Weak {
ptr: self.ptr.clone(),
}
}
}
#[cfg(feature = "multi_thread")]
impl<T> From<super::sync_weak::Weak<T>> for Weak<T>
where
T: 'static + Send + Sync,
{
#[inline]
fn from(v: super::sync_weak::Weak<T>) -> Weak<T> {
Weak {
ptr: v.ptr.map(|ptr| SingleOrMultiThreadPtr::MultiThread(ptr)),
}
}
}
impl<T> fmt::Debug for Weak<T>
where
T: 'static + fmt::Debug,
{
#[cfg(not(feature = "multi_thread"))]
#[inline(never)]
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
match &self.ptr {
None => f.write_str("Weak(None)"),
Some(ptr) => ptr.weak_debug_fmt("Weak", f),
}
}
#[cfg(feature = "multi_thread")]
#[inline(never)]
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
match &self.ptr {
None => f.write_str("Weak(None)"),
Some(SingleOrMultiThreadPtr::SingleThread(ptr)) => ptr.weak_debug_fmt("Weak", f),
Some(SingleOrMultiThreadPtr::MultiThread(ptr)) => ptr.weak_debug_fmt("Weak", f),
}
}
}
#[cfg(test)]
mod tests {
use super::Weak;
use crate::errors::{Error, ErrorEnum};
use crate::pointer::GcPtr;
use crate::prelude::GcPtrEq;
#[test]
fn weak_can_promote() {
let ptr = GcPtr::new(|_| "bla".to_owned());
let weak_ptr: Weak<String> = GcPtr::downgrade(&ptr);
let q = Weak::upgrade(&weak_ptr);
assert!(q.is_ok());
assert!(GcPtr::ptr_eq(&ptr, &q.unwrap()));
}
#[test]
#[cfg_attr(
feature = "single_generation",
ignore = "Single generation GC runs at unspecified time"
)]
fn expired_weak_cant_promote() {
let ptr = GcPtr::new(|_| "bla".to_owned());
let weak_ptr: Weak<String> = GcPtr::downgrade(&ptr);
drop(ptr);
let q = Weak::upgrade(&weak_ptr);
assert!(q.is_err());
assert_eq!(q.err().unwrap(), Error::new(ErrorEnum::Expired(None)));
}
#[test]
fn empty_weak_cant_promote() {
let weak_ptr: Weak<String> = Weak::default();
let q = Weak::upgrade(&weak_ptr);
assert!(q.is_err());
assert_eq!(q.err().unwrap(), Error::new(ErrorEnum::UnvaluedWeak));
}
}