Skip to content

Commit

Permalink
Refactor Ownership and WeakId into their own files
Browse files Browse the repository at this point in the history
  • Loading branch information
madsmtm committed Sep 9, 2021
1 parent 0a6e8b6 commit 470a4b7
Show file tree
Hide file tree
Showing 4 changed files with 209 additions and 187 deletions.
188 changes: 2 additions & 186 deletions objc2/src/rc/id.rs
Original file line number Diff line number Diff line change
@@ -1,43 +1,16 @@
use alloc::borrow;
use alloc::boxed::Box;
use core::cell::UnsafeCell;
use core::fmt;
use core::hash;
use core::iter::FusedIterator;
use core::marker::PhantomData;
use core::mem::ManuallyDrop;
use core::ops::{Deref, DerefMut};
use core::ptr;
use core::ptr::NonNull;

use super::AutoreleasePool;
use super::{Owned, Ownership, Shared};
use crate::Message;

/// A type used to mark that a struct owns the object(s) it contains,
/// so it has the sole references to them.
pub enum Owned {}

/// A type used to mark that the object(s) a struct contains are shared,
/// so there may be other references to them.
pub enum Shared {}

mod private {
pub trait Sealed {}

impl Sealed for super::Owned {}
impl Sealed for super::Shared {}
}

/// A type that marks what type of ownership a struct has over the object(s)
/// it contains; specifically, either [`Owned`] or [`Shared`].
///
/// This trait is sealed and not meant to be implemented outside of the this
/// crate.
pub trait Ownership: private::Sealed + 'static {}

impl Ownership for Owned {}
impl Ownership for Shared {}

/// An pointer for Objective-C reference counted objects.
///
/// [`Id`] strongly references or "retains" the given object `T`, and
Expand Down Expand Up @@ -539,148 +512,18 @@ impl<T, O: Ownership> Unpin for Id<T, O> {}
/// A convenient alias for a shared [`Id`].
pub type ShareId<T> = Id<T, Shared>;

/// A pointer type for a weak reference to an Objective-C reference counted
/// object.
///
/// Allows breaking reference cycles and safely checking whether the object
/// has been deallocated.
#[repr(transparent)]
pub struct WeakId<T> {
/// We give the runtime the address to this box, so that it can modify it
/// even if the `WeakId` is moved.
///
/// Loading may modify the pointer through a shared reference, so we use
/// an UnsafeCell to get a *mut without self being mutable.
inner: Box<UnsafeCell<*mut T>>,
/// TODO: Variance and dropck
item: PhantomData<T>,
}

impl<T: Message> WeakId<T> {
/// Construct a new [`WeakId`] referencing the given shared [`Id`].
#[doc(alias = "objc_initWeak")]
pub fn new(obj: &Id<T, Shared>) -> Self {
// Note that taking `&Id<T, Owned>` would not be safe since that would
// allow loading an `Id<T, Shared>` later on.

// SAFETY: `obj` is valid
unsafe { Self::new_inner(&**obj as *const T as *mut T) }
}

/// # Safety
///
/// The object must be valid or null.
unsafe fn new_inner(obj: *mut T) -> Self {
let inner = Box::new(UnsafeCell::new(ptr::null_mut()));
// SAFETY: `ptr` will never move, and the caller verifies `obj`
objc2_sys::objc_initWeak(inner.get() as _, obj as _);
Self {
inner,
item: PhantomData,
}
}

/// Load a shared (and retained) [`Id`] if the object still exists.
///
/// Returns [`None`] if the object has been deallocated.
#[doc(alias = "upgrade")]
#[doc(alias = "objc_loadWeak")]
#[doc(alias = "objc_loadWeakRetained")]
#[inline]
pub fn load(&self) -> Option<Id<T, Shared>> {
let ptr: *mut *mut objc2_sys::objc_object = self.inner.get() as _;
let obj = unsafe { objc2_sys::objc_loadWeakRetained(ptr) } as *mut T;
NonNull::new(obj).map(|obj| unsafe { Id::new(obj) })
}
}

impl<T> Drop for WeakId<T> {
/// Drops the `WeakId` pointer.
#[doc(alias = "objc_destroyWeak")]
fn drop(&mut self) {
unsafe {
objc2_sys::objc_destroyWeak(self.inner.get() as _);
}
}
}

impl<T> Clone for WeakId<T> {
/// Makes a clone of the `WeakId` that points to the same object.
#[doc(alias = "objc_copyWeak")]
fn clone(&self) -> Self {
let ptr = Box::new(UnsafeCell::new(ptr::null_mut()));
unsafe {
objc2_sys::objc_copyWeak(ptr.get() as _, self.inner.get() as _);
}
Self {
inner: ptr,
item: PhantomData,
}
}
}

impl<T: Message> Default for WeakId<T> {
/// Constructs a new `WeakId<T>` that doesn't reference any object.
///
/// Calling [`Self::load`] on the return value always gives [`None`].
fn default() -> Self {
// SAFETY: The pointer is null
unsafe { Self::new_inner(ptr::null_mut()) }
}
}

/// This implementation follows the same reasoning as `Id<T, Shared>`.
unsafe impl<T: Sync + Send> Sync for WeakId<T> {}

/// This implementation follows the same reasoning as `Id<T, Shared>`.
unsafe impl<T: Sync + Send> Send for WeakId<T> {}

// Unsure about the Debug bound on T, see std::sync::Weak
impl<T: fmt::Debug> fmt::Debug for WeakId<T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "(WeakId)")
}
}

// Underneath this is just a `Box`
impl<T> Unpin for WeakId<T> {}

#[cfg(test)]
mod tests {
use core::mem::size_of;
use core::ptr::NonNull;

use super::{Id, Owned, ShareId, Shared, WeakId};
use super::{Id, Shared};
use crate::runtime::Object;
use crate::{class, msg_send};

fn retain_count(obj: &Object) -> usize {
unsafe { msg_send![obj, retainCount] }
}

pub struct TestType {
_data: [u8; 0], // TODO: `UnsafeCell`?
}

#[test]
fn test_size_of() {
assert_eq!(size_of::<Id<TestType, Owned>>(), size_of::<&TestType>());
assert_eq!(size_of::<Id<TestType, Shared>>(), size_of::<&TestType>());
assert_eq!(
size_of::<Option<Id<TestType, Owned>>>(),
size_of::<&TestType>()
);
assert_eq!(
size_of::<Option<Id<TestType, Shared>>>(),
size_of::<&TestType>()
);

assert_eq!(
size_of::<Option<WeakId<TestType>>>(),
size_of::<*const ()>()
);
}

#[test]
fn test_clone() {
let cls = class!(NSObject);
Expand All @@ -701,31 +544,4 @@ mod tests {
drop(obj);
assert!(retain_count(&cloned) == 1);
}

#[test]
fn test_weak() {
let cls = class!(NSObject);
let obj: ShareId<Object> = unsafe {
let obj: *mut Object = msg_send![cls, alloc];
let obj: *mut Object = msg_send![obj, init];
Id::new(NonNull::new_unchecked(obj))
};

let weak = WeakId::new(&obj);
let strong = weak.load().unwrap();
let strong_ptr: *const Object = &*strong;
let obj_ptr: *const Object = &*obj;
assert!(strong_ptr == obj_ptr);
drop(strong);

drop(obj);
assert!(weak.load().is_none());
}

#[test]
fn test_weak_default() {
let weak: WeakId<Object> = WeakId::default();
assert!(weak.load().is_none());
drop(weak);
}
}
32 changes: 31 additions & 1 deletion objc2/src/rc/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -60,21 +60,51 @@
mod autorelease;
mod id;
mod ownership;
mod strong;
mod weak;
mod weak_id;

pub use self::autorelease::{autoreleasepool, AutoreleasePool, AutoreleaseSafe};
pub use self::id::{Id, Owned, Ownership, ShareId, Shared, WeakId};
pub use self::id::{Id, ShareId};
pub use self::ownership::{Owned, Ownership, Shared};
pub use self::strong::StrongPtr;
pub use self::weak::WeakPtr;
pub use self::weak_id::WeakId;

// These tests use NSObject, which isn't present for GNUstep
#[cfg(all(test, target_vendor = "apple"))]
mod tests {
use core::mem::size_of;

use super::autoreleasepool;
use super::StrongPtr;
use super::{Id, Owned, Shared, WeakId};
use crate::runtime::Object;

pub struct TestType {
_data: [u8; 0], // TODO: `UnsafeCell`?
}

#[test]
fn test_size_of() {
assert_eq!(size_of::<Id<TestType, Owned>>(), size_of::<&TestType>());
assert_eq!(size_of::<Id<TestType, Shared>>(), size_of::<&TestType>());
assert_eq!(
size_of::<Option<Id<TestType, Owned>>>(),
size_of::<&TestType>()
);
assert_eq!(
size_of::<Option<Id<TestType, Shared>>>(),
size_of::<&TestType>()
);

assert_eq!(
size_of::<Option<WeakId<TestType>>>(),
size_of::<*const ()>()
);
}

#[test]
fn test_strong_clone() {
fn retain_count(obj: *mut Object) -> usize {
Expand Down
24 changes: 24 additions & 0 deletions objc2/src/rc/ownership.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
/// A type used to mark that a struct owns the object(s) it contains,
/// so it has the sole references to them.
pub enum Owned {}

/// A type used to mark that the object(s) a struct contains are shared,
/// so there may be other references to them.
pub enum Shared {}

mod private {
pub trait Sealed {}

impl Sealed for super::Owned {}
impl Sealed for super::Shared {}
}

/// A type that marks what type of ownership a struct has over the object(s)
/// it contains; specifically, either [`Owned`] or [`Shared`].
///
/// This trait is sealed and not meant to be implemented outside of the this
/// crate.
pub trait Ownership: private::Sealed + 'static {}

impl Ownership for Owned {}
impl Ownership for Shared {}
Loading

0 comments on commit 470a4b7

Please sign in to comment.