From 5f78295b50c3e604d71f18fb45b5013b7b0d9f5f Mon Sep 17 00:00:00 2001 From: ComplexSpaces Date: Wed, 28 Aug 2024 12:44:22 -0500 Subject: [PATCH] Wrap Apple disk refreshing in autorelease pool to avoid system caching --- src/unix/apple/disk.rs | 38 +++++++++++++++++++++++++++++++++++++- src/unix/apple/ffi.rs | 7 +++++++ 2 files changed, 44 insertions(+), 1 deletion(-) diff --git a/src/unix/apple/disk.rs b/src/unix/apple/disk.rs index 048c85268..f16f52842 100644 --- a/src/unix/apple/disk.rs +++ b/src/unix/apple/disk.rs @@ -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); + }) } } @@ -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>(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 +} diff --git a/src/unix/apple/ffi.rs b/src/unix/apple/ffi.rs index fb6dc0460..75ae0e1ec 100644 --- a/src/unix/apple/ffi.rs +++ b/src/unix/apple/ffi.rs @@ -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" { @@ -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; + } } }