Skip to content

Commit

Permalink
Wrap Apple disk refreshing in autorelease pool to avoid system caching
Browse files Browse the repository at this point in the history
  • Loading branch information
complexspaces committed Aug 28, 2024
1 parent d0122ae commit 5f78295
Show file tree
Hide file tree
Showing 2 changed files with 44 additions and 1 deletion.
38 changes: 37 additions & 1 deletion src/unix/apple/disk.rs
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,11 @@ impl crate::DisksInner {

pub(crate) fn refresh_list(&mut self) {
unsafe {
get_list(&mut self.disks);
// SAFETY: We don't keep any Objective-C objects around because we
// don't make any direct Objective-C calls in this code.
with_autorelease(|| {
get_list(&mut self.disks);
})
}
}

Expand Down Expand Up @@ -431,3 +435,35 @@ unsafe fn new_disk(
},
})
}


/// Calls the provided closure in the context of a new autorelease pool that is drained
/// before returning.
///
/// ## SAFETY:
/// You must not return an Objective-C object that is autoreleased from this function since it
/// will be freed before usable.
unsafe fn with_autorelease<T, F: FnOnce() -> T>(call: F) -> T {
// NB: This struct exists to help prevent memory leaking if `call` were to panic.
// Otherwise, the call to `objc_autoreleasePoolPop` would never be made as the stack unwinds.
// `Drop` destructors for existing types on the stack are run during unwinding, so we can
// ensure the autorelease pool is drained by using a RAII pattern here.
struct DrainPool {
ctx: *mut c_void,
}

impl Drop for DrainPool {
fn drop(&mut self) {
// SAFETY: We have not manipulated `pool_ctx` since it was received from a corresponding
// pool push call.
unsafe { ffi::objc_autoreleasePoolPop(self.ctx) }
}
}

// SAFETY: Creating a new pool is safe in any context. They can be arbitrarily nested
// as long as pool objects are not used in deeper layers, but we only have one and don't
// allow it to leave this scope.
let _pool_ctx = DrainPool { ctx: unsafe { ffi::objc_autoreleasePoolPush() } };
call()
// Pool is drained here before returning
}
7 changes: 7 additions & 0 deletions src/unix/apple/ffi.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ cfg_if! {
array::CFArrayRef, dictionary::CFDictionaryRef, error::CFErrorRef, string::CFStringRef,
url::CFURLRef,
};
use std::ffi::c_void;

#[link(name = "CoreFoundation", kind = "framework")]
extern "C" {
Expand All @@ -32,6 +33,12 @@ cfg_if! {
pub static kCFURLVolumeIsInternalKey: CFStringRef;
pub static kCFURLVolumeIsBrowsableKey: CFStringRef;
}

#[link(name = "objc", kind = "dylib")]
extern "C" {
pub fn objc_autoreleasePoolPop(pool: *mut c_void);
pub fn objc_autoreleasePoolPush() -> *mut c_void;
}
}
}

Expand Down

0 comments on commit 5f78295

Please sign in to comment.