diff options
Diffstat (limited to 'third_party/rust/objc/src/rc')
-rw-r--r-- | third_party/rust/objc/src/rc/autorelease.rs | 30 | ||||
-rw-r--r-- | third_party/rust/objc/src/rc/mod.rs | 123 | ||||
-rw-r--r-- | third_party/rust/objc/src/rc/strong.rs | 73 | ||||
-rw-r--r-- | third_party/rust/objc/src/rc/weak.rs | 50 |
4 files changed, 276 insertions, 0 deletions
diff --git a/third_party/rust/objc/src/rc/autorelease.rs b/third_party/rust/objc/src/rc/autorelease.rs new file mode 100644 index 0000000000..ad7a5e2847 --- /dev/null +++ b/third_party/rust/objc/src/rc/autorelease.rs @@ -0,0 +1,30 @@ +use std::os::raw::c_void; +use runtime::{objc_autoreleasePoolPush, objc_autoreleasePoolPop}; + +// we use a struct to ensure that objc_autoreleasePoolPop during unwinding. +struct AutoReleaseHelper { + context: *mut c_void, +} + +impl AutoReleaseHelper { + unsafe fn new() -> Self { + AutoReleaseHelper { context: objc_autoreleasePoolPush() } + } +} + +impl Drop for AutoReleaseHelper { + fn drop(&mut self) { + unsafe { objc_autoreleasePoolPop(self.context) } + } +} + +/** +Execute `f` in the context of a new autorelease pool. The pool is drained +after the execution of `f` completes. + +This corresponds to `@autoreleasepool` blocks in Objective-C and Swift. +*/ +pub fn autoreleasepool<T, F: FnOnce() -> T>(f: F) -> T { + let _context = unsafe { AutoReleaseHelper::new() }; + f() +} diff --git a/third_party/rust/objc/src/rc/mod.rs b/third_party/rust/objc/src/rc/mod.rs new file mode 100644 index 0000000000..f6d26f7a1b --- /dev/null +++ b/third_party/rust/objc/src/rc/mod.rs @@ -0,0 +1,123 @@ +/*! +Utilities for reference counting Objective-C objects. + +The utilities of the `rc` module provide ARC-like semantics for working with +Objective-C's reference counted objects in Rust. +A `StrongPtr` retains an object and releases the object when dropped. +A `WeakPtr` will not retain the object, but can be upgraded to a `StrongPtr` +and safely fails if the object has been deallocated. + +These utilities are not intended to provide a fully safe interface, but can be +useful when writing higher-level Rust wrappers for Objective-C code. + +For more information on Objective-C's reference counting, see Apple's documentation: +<https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/MemoryMgmt/Articles/MemoryMgmt.html> + +# Example + +``` no_run +# #[macro_use] extern crate objc; +# use objc::rc::{autoreleasepool, StrongPtr}; +# fn main() { +// StrongPtr will release the object when dropped +let obj = unsafe { + StrongPtr::new(msg_send![class!(NSObject), new]) +}; + +// Cloning retains the object an additional time +let cloned = obj.clone(); +autoreleasepool(|| { + // Autorelease consumes the StrongPtr, but won't + // actually release until the end of an autoreleasepool + cloned.autorelease(); +}); + +// Weak references won't retain the object +let weak = obj.weak(); +drop(obj); +assert!(weak.load().is_null()); +# } +``` +*/ + +mod strong; +mod weak; +mod autorelease; + +pub use self::strong::StrongPtr; +pub use self::weak::WeakPtr; +pub use self::autorelease::autoreleasepool; + +// These tests use NSObject, which isn't present for GNUstep +#[cfg(all(test, any(target_os = "macos", target_os = "ios")))] +mod tests { + use runtime::Object; + use super::StrongPtr; + use super::autoreleasepool; + + #[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]) + }; + let weak = obj.weak(); + + let strong = weak.load(); + assert!(*strong == *obj); + drop(strong); + + drop(obj); + assert!(weak.load().is_null()); + } + + #[test] + fn test_weak_copy() { + let obj = unsafe { + StrongPtr::new(msg_send![class!(NSObject), new]) + }; + let weak = obj.weak(); + + let weak2 = weak.clone(); + let strong = weak2.load(); + assert!(*strong == *obj); + } + + #[test] + fn test_autorelease() { + let obj = unsafe { + StrongPtr::new(msg_send![class!(NSObject), new]) + }; + + fn retain_count(obj: *mut Object) -> usize { + unsafe { msg_send![obj, retainCount] } + } + let cloned = obj.clone(); + + autoreleasepool(|| { + obj.autorelease(); + assert!(retain_count(*cloned) == 2); + }); + + // make sure that the autoreleased value has been released + assert!(retain_count(*cloned) == 1); + } +} diff --git a/third_party/rust/objc/src/rc/strong.rs b/third_party/rust/objc/src/rc/strong.rs new file mode 100644 index 0000000000..a61881b6ff --- /dev/null +++ b/third_party/rust/objc/src/rc/strong.rs @@ -0,0 +1,73 @@ +use std::fmt; +use std::mem; +use std::ops::Deref; + +use runtime::{Object, self}; +use super::WeakPtr; + +/// A pointer that strongly references an object, ensuring it won't be deallocated. +pub struct StrongPtr(*mut Object); + +impl StrongPtr { + /// Constructs a `StrongPtr` to a newly created object that already has a + /// +1 retain count. This will not retain the object. + /// When dropped, the object will be released. + /// Unsafe because the caller must ensure the given object pointer is valid. + pub unsafe fn new(ptr: *mut Object) -> Self { + StrongPtr(ptr) + } + + /// Retains the given object and constructs a `StrongPtr` to it. + /// When dropped, the object will be released. + /// Unsafe because the caller must ensure the given object pointer is valid. + pub unsafe fn retain(ptr: *mut Object) -> Self { + StrongPtr(runtime::objc_retain(ptr)) + } + + /// Autoreleases self, meaning that the object is not immediately released, + /// but will be when the autorelease pool is drained. A pointer to the + /// object is returned, but its validity is no longer ensured. + pub fn autorelease(self) -> *mut Object { + let ptr = self.0; + mem::forget(self); + unsafe { + runtime::objc_autorelease(ptr); + } + ptr + } + + /// Returns a `WeakPtr` to self. + pub fn weak(&self) -> WeakPtr { + unsafe { WeakPtr::new(self.0) } + } +} + +impl Drop for StrongPtr { + fn drop(&mut self) { + unsafe { + runtime::objc_release(self.0); + } + } +} + +impl Clone for StrongPtr { + fn clone(&self) -> StrongPtr { + unsafe { + StrongPtr::retain(self.0) + } + } +} + +impl Deref for StrongPtr { + type Target = *mut Object; + + fn deref(&self) -> &*mut Object { + &self.0 + } +} + +impl fmt::Pointer for StrongPtr { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + fmt::Pointer::fmt(&self.0, f) + } +} diff --git a/third_party/rust/objc/src/rc/weak.rs b/third_party/rust/objc/src/rc/weak.rs new file mode 100644 index 0000000000..289bd8dedb --- /dev/null +++ b/third_party/rust/objc/src/rc/weak.rs @@ -0,0 +1,50 @@ +use std::cell::UnsafeCell; +use std::ptr; + +use runtime::{Object, self}; +use super::StrongPtr; + +// Our pointer must have the same address even if we are moved, so Box it. +// Although loading the WeakPtr may modify the pointer, it is thread safe, +// so we must use an UnsafeCell to get a *mut without self being mutable. + +/// A pointer that weakly references an object, allowing to safely check +/// whether it has been deallocated. +pub struct WeakPtr(Box<UnsafeCell<*mut Object>>); + +impl WeakPtr { + /// Constructs a `WeakPtr` to the given object. + /// Unsafe because the caller must ensure the given object pointer is valid. + pub unsafe fn new(obj: *mut Object) -> Self { + let ptr = Box::new(UnsafeCell::new(ptr::null_mut())); + runtime::objc_initWeak(ptr.get(), obj); + WeakPtr(ptr) + } + + /// Loads the object self points to, returning a `StrongPtr`. + /// If the object has been deallocated, the returned pointer will be null. + pub fn load(&self) -> StrongPtr { + unsafe { + let ptr = runtime::objc_loadWeakRetained(self.0.get()); + StrongPtr::new(ptr) + } + } +} + +impl Drop for WeakPtr { + fn drop(&mut self) { + unsafe { + runtime::objc_destroyWeak(self.0.get()); + } + } +} + +impl Clone for WeakPtr { + fn clone(&self) -> Self { + let ptr = Box::new(UnsafeCell::new(ptr::null_mut())); + unsafe { + runtime::objc_copyWeak(ptr.get(), self.0.get()); + } + WeakPtr(ptr) + } +} |