use crate::object::{Object, ObjectIntf, ObjectPtr};
use crate::prelude::GcPtrEq;
use crate::{GcMemberPtr, Metadata};
use std::fmt;
use std::mem;
use std::ops::Deref;
use std::pin::Pin;
use std::ptr;
#[cfg(feature = "multi_thread")]
use super::SingleOrMultiThreadPtr;
#[cfg(all(feature = "multi_thread", feature = "weak_pointer"))]
use super::sync_weak;
#[cfg(feature = "weak_pointer")]
use super::weak::Weak;
#[cfg(feature = "multi_thread")]
use crate::object::{MTObjectIntf, MTObjectPtr};
#[cfg(feature = "multi_thread")]
use crate::sync::{GcMtMemberPtr, GcMtPtr};
#[cfg(feature = "weak_pointer")]
use crate::errors::Error;
pub struct GcPtr<T>
where
T: 'static,
{
#[cfg(not(feature = "multi_thread"))]
pub(super) ptr: Pin<ObjectPtr<T>>,
#[cfg(feature = "multi_thread")]
pub(super) ptr: SingleOrMultiThreadPtr<T>,
}
#[cfg(feature = "multi_thread")]
impl<T> From<GcMtPtr<T>> for GcPtr<T>
where
T: 'static + Send + Sync,
{
#[inline]
fn from(sp: GcMtPtr<T>) -> GcPtr<T> {
let sp_ptr = unsafe {
let sp = mem::ManuallyDrop::new(sp);
let mut new_ptr = mem::MaybeUninit::<Pin<MTObjectPtr<T>>>::zeroed();
ptr::copy_nonoverlapping(&sp.ptr, new_ptr.as_mut_ptr(), 1);
new_ptr.assume_init()
};
GcPtr { ptr: sp_ptr.into() }
}
}
impl<T> Clone for GcPtr<T>
where
T: 'static,
{
#[inline]
fn clone(&self) -> Self {
let ptr = self.ptr.clone();
#[cfg(not(feature = "multi_thread"))]
ptr.get_control_block().refcount_inc();
#[cfg(feature = "multi_thread")]
match &ptr {
SingleOrMultiThreadPtr::SingleThread(ptr) => ptr.get_control_block().refcount_inc(),
SingleOrMultiThreadPtr::MultiThread(ptr) => ptr.get_control_block().refcount_inc(),
};
GcPtr { ptr }
}
}
impl<T> Drop for GcPtr<T>
where
T: 'static,
{
#[inline]
fn drop(&mut self) {
#[cfg(not(feature = "multi_thread"))]
self.ptr.get_control_block().refcount_dec();
#[cfg(feature = "multi_thread")]
match &self.ptr {
SingleOrMultiThreadPtr::SingleThread(ptr) => ptr.get_control_block().refcount_dec(),
SingleOrMultiThreadPtr::MultiThread(ptr) => ptr.get_control_block().refcount_dec(),
};
}
}
impl<T> Deref for GcPtr<T>
where
T: 'static,
{
type Target = T;
#[inline]
fn deref(&self) -> &T {
#[cfg(not(feature = "multi_thread"))]
return self.ptr.get_data();
#[cfg(feature = "multi_thread")]
return match &self.ptr {
SingleOrMultiThreadPtr::SingleThread(ptr) => ptr.get_data(),
SingleOrMultiThreadPtr::MultiThread(ptr) => ptr.get_data(),
};
}
}
impl<T> fmt::Debug for GcPtr<T>
where
T: 'static + fmt::Debug,
{
#[inline]
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
fmt::Debug::fmt(&**self, f)
}
}
impl<T> GcPtr<T>
where
T: 'static,
{
#[inline]
pub fn new<Factory>(factory: Factory) -> GcPtr<T>
where
Factory: FnOnce(Metadata) -> T,
{
unsafe { GcPtr::new_from_raw(Object::new_ptr(1, factory, None)) }
}
#[cfg(feature = "weak_pointer")]
#[cfg_attr(docsrs, doc(cfg(feature = "weak_pointer")))]
#[inline]
pub fn new_cyclic<Factory>(factory: Factory) -> GcPtr<T>
where
Factory: FnOnce(Metadata, Weak<T>) -> T,
{
unsafe { GcPtr::new_from_raw(Object::new_cyclic_ptr(1, factory, None)) }
}
#[cfg(feature = "weak_pointer")]
#[cfg_attr(docsrs, doc(cfg(feature = "weak_pointer")))]
#[inline]
pub fn downgrade(this: &Self) -> Weak<T> {
Weak::new_ptr(this.ptr.clone())
}
#[cfg(all(not(feature = "multi_thread"), feature = "weak_pointer"))]
pub(super) fn new_from_weak(ptr: &Pin<ObjectPtr<T>>) -> Result<Self, Error> {
ptr.get_control_block()
.try_refcount_inc()
.map(|_| GcPtr { ptr: ptr.clone() })
}
#[cfg(all(feature = "multi_thread", feature = "weak_pointer"))]
pub(super) fn new_from_weak(ptr: &SingleOrMultiThreadPtr<T>) -> Result<Self, Error> {
match ptr {
SingleOrMultiThreadPtr::SingleThread(ptr) => {
ptr.get_control_block().try_refcount_inc().map(|_| GcPtr {
ptr: SingleOrMultiThreadPtr::SingleThread(ptr.clone()),
})
}
SingleOrMultiThreadPtr::MultiThread(ptr) => {
ptr.get_control_block().try_refcount_inc().map(|_| GcPtr {
ptr: SingleOrMultiThreadPtr::MultiThread(ptr.clone()),
})
}
}
}
#[cfg(not(feature = "multi_thread"))]
#[inline]
pub(crate) const unsafe fn new_from_raw(ptr: Pin<ObjectPtr<T>>) -> Self {
GcPtr { ptr }
}
#[cfg(feature = "multi_thread")]
#[inline]
pub(crate) const unsafe fn new_from_raw(ptr: Pin<ObjectPtr<T>>) -> Self {
GcPtr {
ptr: SingleOrMultiThreadPtr::SingleThread(ptr),
}
}
#[cfg(feature = "multi_thread")]
#[inline]
pub(crate) const unsafe fn new_from_raw_mt(ptr: Pin<MTObjectPtr<T>>) -> Self {
GcPtr {
ptr: SingleOrMultiThreadPtr::MultiThread(ptr),
}
}
#[cfg(not(feature = "multi_thread"))]
#[inline]
pub(super) unsafe fn release(this: Self) -> Pin<ObjectPtr<T>> {
unsafe {
let this = mem::ManuallyDrop::new(this);
let mut new_ptr = mem::MaybeUninit::<Pin<ObjectPtr<T>>>::zeroed();
ptr::copy_nonoverlapping(&this.ptr, new_ptr.as_mut_ptr(), 1);
new_ptr.assume_init()
}
}
#[cfg(feature = "multi_thread")]
#[inline]
pub(super) unsafe fn release(this: Self) -> SingleOrMultiThreadPtr<T> {
unsafe {
let this = mem::ManuallyDrop::new(this);
let mut new_ptr = mem::MaybeUninit::<SingleOrMultiThreadPtr<T>>::zeroed();
ptr::copy_nonoverlapping(&this.ptr, new_ptr.as_mut_ptr(), 1);
new_ptr.assume_init()
}
}
}
impl<T> GcPtrEq<GcPtr<T>> for GcPtr<T>
where
T: 'static,
{
#[inline]
fn ptr_eq(this: &Self, other: &GcPtr<T>) -> bool {
#[cfg(not(feature = "multi_thread"))]
return ptr::eq(&*this.ptr, &*other.ptr);
#[cfg(feature = "multi_thread")]
return match (&this.ptr, &other.ptr) {
(
SingleOrMultiThreadPtr::SingleThread(this_ptr),
SingleOrMultiThreadPtr::SingleThread(other_ptr),
) => ptr::eq(&**this_ptr, &**other_ptr),
(SingleOrMultiThreadPtr::SingleThread(_), SingleOrMultiThreadPtr::MultiThread(_)) => {
false
}
(SingleOrMultiThreadPtr::MultiThread(_), SingleOrMultiThreadPtr::SingleThread(_)) => {
false
}
(
SingleOrMultiThreadPtr::MultiThread(this_ptr),
SingleOrMultiThreadPtr::MultiThread(other_ptr),
) => ptr::eq(&**this_ptr, &**other_ptr),
};
}
}
#[cfg(feature = "multi_thread")]
impl<T> GcPtrEq<GcMtPtr<T>> for GcPtr<T>
where
T: 'static + Send + Sync,
{
#[inline]
fn ptr_eq(this: &Self, other: &GcMtPtr<T>) -> bool {
match &this.ptr {
SingleOrMultiThreadPtr::MultiThread(this_ptr) => ptr::eq(&**this_ptr, &*other.ptr),
_ => false,
}
}
}
impl<T> GcPtrEq<GcMemberPtr<T>> for GcPtr<T>
where
T: 'static,
{
#[inline]
fn ptr_eq(this: &Self, other: &GcMemberPtr<T>) -> bool {
#[cfg(not(feature = "multi_thread"))]
return ptr::eq(&*this.ptr, &*other.ptr);
#[cfg(feature = "multi_thread")]
return match (&this.ptr, &other.ptr) {
(
SingleOrMultiThreadPtr::SingleThread(this_ptr),
SingleOrMultiThreadPtr::SingleThread(other_ptr),
) => ptr::eq(&**this_ptr, &**other_ptr),
(SingleOrMultiThreadPtr::SingleThread(_), SingleOrMultiThreadPtr::MultiThread(_)) => {
false
}
(SingleOrMultiThreadPtr::MultiThread(_), SingleOrMultiThreadPtr::SingleThread(_)) => {
false
}
(
SingleOrMultiThreadPtr::MultiThread(this_ptr),
SingleOrMultiThreadPtr::MultiThread(other_ptr),
) => ptr::eq(&**this_ptr, &**other_ptr),
};
}
}
#[cfg(feature = "multi_thread")]
impl<T> GcPtrEq<GcMtMemberPtr<T>> for GcPtr<T>
where
T: 'static + Send + Sync,
{
#[inline]
fn ptr_eq(this: &Self, other: &GcMtMemberPtr<T>) -> bool {
match &this.ptr {
SingleOrMultiThreadPtr::MultiThread(this_ptr) => ptr::eq(&**this_ptr, &*other.ptr),
_ => false,
}
}
}
#[cfg(feature = "weak_pointer")]
impl<T> GcPtrEq<Weak<T>> for GcPtr<T>
where
T: 'static,
{
#[inline]
fn ptr_eq(this: &Self, other: &Weak<T>) -> bool {
#[cfg(not(feature = "multi_thread"))]
return other
.ptr
.as_ref()
.map(|other_ptr| ptr::eq(this.ptr.as_ref().get_ref(), other_ptr.as_ref().get_ref()))
.unwrap_or(false);
#[cfg(feature = "multi_thread")]
return other
.ptr
.as_ref()
.map(|other_ptr| match (&this.ptr, other_ptr) {
(
SingleOrMultiThreadPtr::SingleThread(this_ptr),
SingleOrMultiThreadPtr::SingleThread(other_ptr),
) => ptr::eq(this_ptr.as_ref().get_ref(), other_ptr.as_ref().get_ref()),
(
SingleOrMultiThreadPtr::SingleThread(_),
SingleOrMultiThreadPtr::MultiThread(_),
) => false,
(
SingleOrMultiThreadPtr::MultiThread(_),
SingleOrMultiThreadPtr::SingleThread(_),
) => false,
(
SingleOrMultiThreadPtr::MultiThread(this_ptr),
SingleOrMultiThreadPtr::MultiThread(other_ptr),
) => ptr::eq(this_ptr.as_ref().get_ref(), other_ptr.as_ref().get_ref()),
})
.unwrap_or(false);
}
}
#[cfg(all(feature = "multi_thread", feature = "weak_pointer"))]
impl<T> GcPtrEq<sync_weak::Weak<T>> for GcPtr<T>
where
T: 'static + Send + Sync,
{
#[inline]
fn ptr_eq(this: &Self, other: &sync_weak::Weak<T>) -> bool {
other
.ptr
.as_ref()
.map(|other_ptr| match &this.ptr {
SingleOrMultiThreadPtr::SingleThread(_) => false,
SingleOrMultiThreadPtr::MultiThread(this_ptr) => ptr::eq(&**this_ptr, &**other_ptr),
})
.unwrap_or(false)
}
}
#[cfg(test)]
mod tests {
use crate::GcPtr;
use crate::prelude::GcPtrEq;
use std::sync::{Arc, Mutex};
#[derive(Debug)]
struct Bla {
n: Arc<Mutex<i32>>,
}
impl Bla {
fn new(n: Arc<Mutex<i32>>) -> Bla {
*n.lock().unwrap() += 1;
Bla { n }
}
}
impl Drop for Bla {
fn drop(&mut self) {
*self.n.lock().unwrap() -= 1;
}
}
#[test]
fn create_pointer() {
let n = Arc::new(Mutex::new(0));
let p = GcPtr::new(|_| Bla::new(n.clone()));
assert_eq!(*n.lock().unwrap(), 1);
drop(p);
assert_eq!(*n.lock().unwrap(), 0);
}
#[test]
fn clone_pointer() {
let n = Arc::new(Mutex::new(0));
let p = GcPtr::new(|_| Bla::new(n.clone()));
let q = p.clone();
assert_eq!(*n.lock().unwrap(), 1);
drop(p);
assert_eq!(*n.lock().unwrap(), 1);
drop(q);
assert_eq!(*n.lock().unwrap(), 0);
}
#[test]
fn equality() {
let n = Arc::new(Mutex::new(0));
let p = GcPtr::new(|_| Bla::new(n.clone()));
let q = p.clone();
assert!(GcPtr::ptr_eq(&p, &q));
}
#[cfg(feature = "multi_thread")]
#[test]
#[cfg_attr(
feature = "single_generation_mt",
ignore = "In single-generation, any of the other test threads may be running the GC task, making it not run in this function, and thus fail the n=0 check at the end."
)]
fn from_sync_pointer() {
let n = Arc::new(Mutex::new(0));
let p = super::GcMtPtr::new(|_| Bla::new(n.clone()));
let q = GcPtr::<Bla>::from(p);
assert_eq!(*n.lock().unwrap(), 1);
drop(q);
assert_eq!(*n.lock().unwrap(), 0);
}
#[cfg(feature = "multi_thread")]
#[test]
fn equality_against_sync() {
let n = Arc::new(Mutex::new(0));
let p = super::GcMtPtr::new(|_| Bla::new(n.clone()));
let q = GcPtr::from(p.clone());
assert!(GcPtr::ptr_eq(&q, &p));
}
}