use std::cell::Cell;
use std::fmt::{Debug, Formatter, Result as FmtResult};
use std::marker::PhantomData;
use std::mem;
use std::ptr::{self, NonNull};
use std::sync::Weak as WeakArc;
use drc::{Drc, DrcInner};
pub struct Weak<T>
where
T: ?Sized,
{
pub(crate) ptr: NonNull<DrcInner<T>>,
}
impl<T> Weak<T> {
pub fn new() -> Weak<T> {
Weak::with_weak_arc(WeakArc::new())
}
}
impl<T> Weak<T>
where
T: ?Sized,
{
pub fn with_weak_arc(weak_arc: WeakArc<T>) -> Weak<T> {
unsafe {
Weak {
ptr: NonNull::new_unchecked(Box::into_raw(Box::new(DrcInner {
strong: Cell::new(0),
weak: Cell::new(1),
strong_ref: None,
weak_ref: Some(weak_arc),
}))),
}
}
}
pub fn upgrade(&self) -> Option<Drc<T>> {
if unsafe { self.ptr.as_ref() }.strong_ref.is_none() {
debug_assert_eq!(0, self.strong().get());
if let Some(arc) = WeakArc::upgrade(self.weak_arc()) {
self.strong().set(1);
self.weak().set(self.weak().get() + 1);
*unsafe { &mut (*self.ptr.as_ptr()).strong_ref } = Some(arc);
Some(Drc {
ptr: self.ptr,
phantom: PhantomData,
})
} else {
None
}
} else {
self.strong().set(self.strong().get() + 1);
Some(Drc {
ptr: self.ptr,
phantom: PhantomData,
})
}
}
pub fn detach(&self) -> WeakArc<T> {
WeakArc::clone(self.weak_arc())
}
fn weak_arc(&self) -> &WeakArc<T> {
assert_eq!(
mem::size_of::<WeakArc<T>>(),
mem::size_of::<Option<WeakArc<T>>>(),
"Error within drc::Weak<T>: Null pointer optimization does not apply to WeakArc<T>! \
If you see this panic, please report it to the maintainer(s) of the \"drc\" crate."
);
unsafe { &*(&self.ptr.as_ref().weak_ref as *const _ as *const WeakArc<T>) }
}
unsafe fn take_weak_arc(&self) -> WeakArc<T> {
assert_eq!(
mem::size_of::<WeakArc<T>>(),
mem::size_of::<Option<WeakArc<T>>>(),
"Error within drc::Weak<T>: Null pointer optimization does not apply to WeakArc<T>! \
If you see this panic, please report it to the maintainer(s) of the \"drc\" crate."
);
let storage = self.ptr.as_ptr();
let weak_arc = ptr::read(&(*storage).weak_ref as *const _ as *const WeakArc<T>);
ptr::write(&mut (*storage).weak_ref, None);
weak_arc
}
fn weak(&self) -> &Cell<usize> {
unsafe { &self.ptr.as_ref().weak }
}
fn strong(&self) -> &Cell<usize> {
unsafe { &self.ptr.as_ref().strong }
}
}
impl<T> Clone for Weak<T>
where
T: ?Sized,
{
fn clone(&self) -> Weak<T> {
self.weak().set(self.weak().get() + 1);
Weak { ptr: self.ptr }
}
}
impl<T> Drop for Weak<T>
where
T: ?Sized,
{
fn drop(&mut self) {
unsafe {
let weak = self.weak().get();
self.weak().set(weak - 1);
if self.strong().get() == 0 {
if weak == 1
{
Box::from_raw(self.ptr.as_ptr());
}
} else {
if weak == 2
{
drop(self.take_weak_arc());
}
}
}
}
}
impl<T> Default for Weak<T> {
fn default() -> Weak<T> {
Weak::new()
}
}
impl<T> Debug for Weak<T> {
fn fmt(&self, f: &mut Formatter) -> FmtResult {
write!(f, "(Weak)")
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn new_and_default() {
for i in 0usize..2usize {
let weak: Weak<usize> = match i {
0 => Weak::new(),
1 => Default::default(),
_ => unreachable!(),
};
let storage = unsafe { weak.ptr.as_ref() };
assert_eq!(
storage.strong.get(),
0,
"Freshly created weak pointer had nonzero strong count."
);
assert_eq!(
storage.weak.get(),
1,
"Freshly created weak pointer had a weak count other than one."
);
assert!(
storage.strong_ref.is_none(),
"Freshly created weak pointer had a strong Arc."
);
assert!(
storage.weak_ref.is_some(),
"Freshly created weak pointer was missing its weak Arc."
);
assert!(
weak.upgrade().is_none(),
"Freshly created weak pointer upgraded into a strong Drc."
);
}
}
#[test]
fn with_weak_arc() {
use std::sync::Arc;
{
let weak: Weak<usize> = Weak::with_weak_arc(WeakArc::new());
let storage = unsafe { weak.ptr.as_ref() };
assert_eq!(
storage.strong.get(),
0,
"Weak pointer from freshly created weak Arc had nonzero strong count."
);
assert_eq!(
storage.weak.get(),
1,
"Weak pointer from freshly created weak Arc had a weak count other than one."
);
assert!(
storage.strong_ref.is_none(),
"Weak pointer from freshly created weak Arc had a strong Arc."
);
assert!(
storage.weak_ref.is_some(),
"Weak pointer from freshly created weak Arc was missing its weak Arc."
);
assert!(
weak.upgrade().is_none(),
"Weak pointer from freshly created weak Arc upgraded into a strong Drc."
);
}
{
let arc = Arc::new(17usize);
let drc = {
let weak = Weak::with_weak_arc(Arc::downgrade(&arc));
let storage = unsafe { weak.ptr.as_ref() };
assert_eq!(
storage.strong.get(),
0,
"Weak pointer with freshly made DrcInner had nonzero strong count."
);
assert_eq!(
storage.weak.get(),
1,
"Weak pointer with freshly made DrcInner had a weak count other than one."
);
assert!(
storage.strong_ref.is_none(),
"Weak pointer with freshly made DrcInner had a strong Arc."
);
assert!(
storage.weak_ref.is_some(),
"Weak pointer with freshly made DrcInner was missing its weak Arc."
);
let maybe_drc = weak.upgrade();
assert!(
maybe_drc.is_some(),
"Weak pointer with associated weak Arc that should have been upgradeable did \
not upgrade."
);
maybe_drc.unwrap()
};
assert_eq!(
*drc, 17usize,
"Upgraded Drc does not have the proper value."
);
assert!(
Drc::arc_ptr_eq(&drc, &arc),
"Upgraded Drc did not point to the same value as the Arc."
);
}
}
#[test]
fn upgrade() {
let five = Drc::new(5);
let detached_five = Drc::detach(&five);
let weak_five = Drc::downgrade(&five);
let strong_five: Option<Drc<_>> = weak_five.upgrade();
assert!(strong_five.is_some());
mem::drop(strong_five);
mem::drop(five);
let strong_five: Option<Drc<_>> = weak_five.upgrade();
assert!(strong_five.is_some());
mem::drop(strong_five);
mem::drop(detached_five);
assert!(weak_five.upgrade().is_none());
}
#[test]
fn detach() {
let five = Drc::new(5);
let weak_five = Drc::downgrade(&five);
let detached_weak_five: WeakArc<_> = weak_five.detach();
assert!(Drc::arc_ptr_eq(
&weak_five.upgrade().unwrap(),
&detached_weak_five.upgrade().unwrap()
));
}
#[test]
fn clone() {
let five = Drc::new(5);
let weak_five = Drc::downgrade(&five);
let same_weak_five = Weak::clone(&weak_five);
assert!(Drc::linked(&five, &same_weak_five.upgrade().unwrap()));
assert_eq!(2, Drc::weak_count(&five, true));
assert_eq!(1, Drc::weak_count(&five, false));
}
#[test]
fn drop() {
struct Foo<'a>(&'a Cell<bool>);
impl<'a> Drop for Foo<'a> {
fn drop(&mut self) {
self.0.set(true);
}
}
let cell = Cell::new(false);
let foo = Drc::new(Foo(&cell));
let weak_foo = Drc::downgrade(&foo);
let other_weak_foo = Weak::clone(&weak_foo);
assert_eq!(cell.get(), false);
mem::drop(weak_foo);
assert_eq!(cell.get(), false);
mem::drop(foo);
assert_eq!(cell.get(), true);
assert!(other_weak_foo.upgrade().is_none());
}
#[test]
fn debug() {
let weak: Weak<usize> = Weak::new();
assert_eq!(
format!("{:?}", weak),
"(Weak)",
"Weak pointers should debug-format to the string \"(Weak)\""
);
}
}