Skip to content

Commit

Permalink
Merge branch 'smart-references' into id-improvements
Browse files Browse the repository at this point in the history
  • Loading branch information
madsmtm committed Sep 8, 2021
2 parents 39c7ac1 + d067bbd commit 41170ef
Show file tree
Hide file tree
Showing 3 changed files with 582 additions and 17 deletions.
21 changes: 4 additions & 17 deletions objc2/src/rc/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -39,10 +39,14 @@ assert!(weak.load().is_null());
*/

mod autorelease;
mod owned;
mod retained;
mod strong;
mod weak;

pub use self::autorelease::{autoreleasepool, AutoreleasePool, AutoreleaseSafe};
pub use self::owned::Owned;
pub use self::retained::Retained;
pub use self::strong::StrongPtr;
pub use self::weak::WeakPtr;

Expand All @@ -53,23 +57,6 @@ mod tests {
use super::StrongPtr;
use crate::runtime::Object;

#[test]
fn test_strong_clone() {
fn retain_count(obj: *mut Object) -> usize {
unsafe { msg_send![obj, retainCount] }
}

let obj = unsafe { StrongPtr::new(msg_send![class!(NSObject), new]) };
assert!(retain_count(*obj) == 1);

let cloned = obj.clone();
assert!(retain_count(*cloned) == 2);
assert!(retain_count(*obj) == 2);

drop(obj);
assert!(retain_count(*cloned) == 1);
}

#[test]
fn test_weak() {
let obj = unsafe { StrongPtr::new(msg_send![class!(NSObject), new]) };
Expand Down
219 changes: 219 additions & 0 deletions objc2/src/rc/owned.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,219 @@
use core::borrow;
use core::fmt;
use core::hash;
use core::marker::PhantomData;
use core::mem;
use core::ops::{Deref, DerefMut};
use core::ptr::{drop_in_place, NonNull};

use super::AutoreleasePool;
use super::Retained;

/// A smart pointer that strongly references and uniquely owns an Objective-C
/// object.
///
/// The fact that we uniquely own the pointer means that it's safe to mutate
/// it. As such, this implements [`DerefMut`].
///
/// This is guaranteed to have the same size as the underlying pointer.
///
/// # Cloning and [`Retained`]
///
/// This does not implement [`Clone`], but [`Retained`] has a [`From`]
/// implementation to convert from this, so you can easily reliquish ownership
/// and work with a clonable [`Retained`] pointer.
///
/// ```no_run
/// let obj: Owned<T> = ...;
/// let retained: Retained<T> = obj.into();
/// let cloned: Retained<T> = retained.clone();
/// ```
///
/// TODO: Explain similarities to [`Box`].
///
/// TODO: Explain this vs. [`Retained`]
#[repr(transparent)]
pub struct Owned<T> {
/// The pointer is always retained.
ptr: NonNull<T>, // We are the unique owner of T, so covariance is correct
phantom: PhantomData<T>, // Necessary for dropck
}

/// `Owned` pointers are `Send` if `T` is `Send` because they give the same
/// access as having a T directly.
unsafe impl<T: Send> Send for Owned<T> {}

/// `Owned` pointers are `Sync` if `T` is `Sync` because they give the same
/// access as having a `T` directly.
unsafe impl<T: Sync> Sync for Owned<T> {}

// TODO: Unsure how the API should look...
impl<T> Owned<T> {
/// Create a new `Owned` pointer to the object.
///
/// Uses a retain count that has been handed off from somewhere else,
/// usually Objective-C methods like `init`, `alloc`, `new`, or `copy`.
///
/// # Safety
///
/// The caller must ensure that there are no other pointers or references
/// to the same object, and the given pointer is not be used afterwards.
///
/// Additionally, the given object pointer must have +1 retain count.
///
/// And lastly, the object pointer must be valid as a mutable reference
/// (non-null, aligned, dereferencable, initialized and upholds aliasing
/// rules, see the [`std::ptr`] module for more information).
///
/// # Example
///
/// ```rust
/// let obj: &mut Object = unsafe { msg_send![cls, alloc] };
/// let obj: Owned<Object> = unsafe { Owned::new(msg_send![obj, init]) };
/// // Or in this case simply just:
/// let obj: Owned<Object> = unsafe { Owned::new(msg_send![cls, new]) };
/// ```
#[inline]
// Note: We don't take a mutable reference as a parameter since it would
// be too easy to accidentally create two aliasing mutable references.
pub unsafe fn new(ptr: *mut T) -> Self {
Self {
// SAFETY: Upheld by the caller
ptr: NonNull::new_unchecked(ptr),
phantom: PhantomData,
}
}

/// Acquires a `*mut` pointer to the object.
#[inline]
pub fn as_ptr(&self) -> *mut T {
self.ptr.as_ptr()
}

/// Construct an `Owned` pointer from a `Retained` pointer.
///
/// # Safety
///
/// The caller must ensure that there are no other pointers to the same
/// object (which also means that the given [`Retained`] should have a
/// retain count of exactly 1 in almost all cases).
#[inline]
pub unsafe fn from_retained(obj: Retained<T>) -> Self {
// SAFETY: The pointer is guaranteed by `Retained` to be NonNull
let ptr = NonNull::new_unchecked(mem::ManuallyDrop::new(obj).as_ptr() as *mut T);
Self {
ptr,
phantom: PhantomData,
}
}

/// Autoreleases the retained pointer, meaning that the object is not
/// immediately released, but will be when the innermost / current
/// autorelease pool is drained.
#[doc(alias = "objc_autorelease")]
#[must_use = "If you don't intend to use the object any more, just drop it as usual"]
#[inline]
pub fn autorelease<'p>(self, pool: &'p AutoreleasePool) -> &'p mut T {
let retained: Retained<T> = self.into();
let ptr = retained.autorelease(pool) as *const T as *mut T;
// SAFETY: The pointer was previously `Owned`, so is safe to be mutable
unsafe { &mut *ptr }
}
}

/// `#[may_dangle]` (see [this][dropck_eyepatch]) would not be safe here,
/// since we cannot verify that a `dealloc` method doesn't access borrowed
/// data.
///
/// [dropck_eyepatch]: https://doc.rust-lang.org/nightly/nomicon/dropck.html#an-escape-hatch
impl<T> Drop for Owned<T> {
/// Releases the retained object.
///
/// This is guaranteed to be the last destructor that runs, in contrast to
/// [`Retained`], which means that we can run the [`Drop`] implementation
/// on the contained object as well.
#[inline]
fn drop(&mut self) {
let ptr = self.as_ptr();
unsafe {
drop_in_place(ptr);
// Construct a new `Retained`, which will be dropped immediately
Retained::new(ptr);
};
}
}

impl<T> Deref for Owned<T> {
type Target = T;

#[inline]
fn deref(&self) -> &Self::Target {
// SAFETY: TODO
unsafe { self.ptr.as_ref() }
}
}

impl<T> DerefMut for Owned<T> {
#[inline]
fn deref_mut(&mut self) -> &mut Self::Target {
// SAFETY: TODO
unsafe { self.ptr.as_mut() }
}
}

// TODO: impl PartialEq, PartialOrd, Ord and Eq

impl<T: fmt::Display> fmt::Display for Owned<T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt::Display::fmt(&**self, f)
}
}

impl<T: fmt::Debug> fmt::Debug for Owned<T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt::Debug::fmt(&**self, f)
}
}

impl<T> fmt::Pointer for Owned<T> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
fmt::Pointer::fmt(&self.as_ptr(), f)
}
}

impl<T: hash::Hash> hash::Hash for Owned<T> {
fn hash<H: hash::Hasher>(&self, state: &mut H) {
(&**self).hash(state)
}
}

// TODO: impl Fn traits? See `boxed_closure_impls`

// TODO: CoerceUnsized

impl<T> borrow::Borrow<T> for Owned<T> {
fn borrow(&self) -> &T {
&**self
}
}

impl<T> borrow::BorrowMut<T> for Owned<T> {
fn borrow_mut(&mut self) -> &mut T {
&mut **self
}
}

impl<T> AsRef<T> for Owned<T> {
fn as_ref(&self) -> &T {
&**self
}
}

impl<T> AsMut<T> for Owned<T> {
fn as_mut(&mut self) -> &mut T {
&mut **self
}
}

// TODO: Comment on impl Unpin for Box
impl<T> Unpin for Owned<T> {}
Loading

0 comments on commit 41170ef

Please sign in to comment.