use super::sync_ptr::GcMtPtr;
use crate::GcMemberPtr;
use crate::GcPtr;
use crate::errors::{Error, ErrorEnum};
use crate::object::MTObjectPtr;
use crate::pointer::SingleOrMultiThreadPtr;
use crate::prelude::GcPtrEq;
use crate::sync::GcMtMemberPtr;
use std::fmt;
use std::pin::Pin;
use std::ptr;
#[cfg_attr(
docsrs,
doc(cfg(all(feature = "multi_thread", feature = "weak_pointer")))
)]
pub struct Weak<T>
where
T: 'static + Send + Sync,
{
pub(super) ptr: Option<Pin<MTObjectPtr<T>>>,
}
impl<T> Weak<T>
where
T: 'static + Send + Sync,
{
#[inline]
pub const fn new() -> Self {
Weak { ptr: None }
}
#[inline]
pub(super) const fn new_ptr(ptr: Pin<MTObjectPtr<T>>) -> Self {
Weak { ptr: Some(ptr) }
}
#[inline]
pub(crate) const fn new_from_raw(ptr: Pin<MTObjectPtr<T>>) -> Self {
Self::new_ptr(ptr)
}
#[inline]
pub fn upgrade(&self) -> Result<GcMtPtr<T>, Error> {
self.ptr
.as_ref()
.ok_or(Error::new(ErrorEnum::UnvaluedWeak))
.and_then(GcMtPtr::new_from_weak)
}
}
impl<T> GcPtrEq<GcPtr<T>> for Weak<T>
where
T: 'static + Send + Sync,
{
#[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 + Send + Sync,
{
#[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<crate::Weak<T>> for Weak<T>
where
T: 'static + Send + Sync,
{
#[inline]
fn ptr_eq(this: &Self, other: &crate::Weak<T>) -> bool {
match (&this.ptr, &other.ptr) {
(Some(this_ptr), Some(SingleOrMultiThreadPtr::MultiThread(other_ptr))) => {
ptr::eq(&**this_ptr, &**other_ptr)
}
_ => false,
}
}
}
#[cfg(feature = "multi_thread")]
impl<T> GcPtrEq<Weak<T>> for Weak<T>
where
T: 'static + Send + Sync,
{
#[inline]
fn ptr_eq(this: &Self, other: &Weak<T>) -> bool {
match (&this.ptr, &other.ptr) {
(Some(this_ptr), Some(other_ptr)) => ptr::eq(&**this_ptr, &**other_ptr),
_ => false,
}
}
}
impl<T> Default for Weak<T>
where
T: 'static + Send + Sync,
{
#[inline]
fn default() -> Self {
Weak::new()
}
}
impl<T> Clone for Weak<T>
where
T: 'static + Send + Sync,
{
#[inline]
fn clone(&self) -> Self {
Weak {
ptr: self.ptr.clone(),
}
}
}
impl<T> fmt::Debug for Weak<T>
where
T: 'static + Send + Sync + fmt::Debug,
{
#[allow(
clippy::missing_inline_in_public_items,
reason = "This will make a call to some complicated debug functions. Might as well make it non-inline."
)]
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(test)]
mod tests {
use super::Weak;
use crate::errors::{Error, ErrorEnum};
use crate::pointer::sync::GcMtPtr;
use crate::prelude::GcPtrEq;
#[test]
fn weak_can_promote() {
let ptr = GcMtPtr::new(|_| "bla".to_owned());
let weak_ptr: Weak<String> = GcMtPtr::downgrade(&ptr);
let q = Weak::upgrade(&weak_ptr);
assert!(q.is_ok());
assert!(GcMtPtr::ptr_eq(&ptr, &q.unwrap()));
}
#[test]
#[cfg_attr(
feature = "single_generation_mt",
ignore = "Single generation GC runs at unspecified time"
)]
fn expired_weak_cant_promote() {
let ptr = GcMtPtr::new(|_| "bla".to_owned());
let weak_ptr: Weak<String> = GcMtPtr::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));
}
}