fix(electron): potential app crash on quit (#12480)

<!-- This is an auto-generated comment: release notes by coderabbit.ai -->

## Summary by CodeRabbit

- **Bug Fixes**
	- Improved stability during shutdown by preventing potential crashes when removing audio property listeners on macOS.
	- Suppressed unnecessary error logs related to device listener removal during system shutdown.
	- Enhanced handling of internal subscriptions to avoid redundant operations and improve reliability when loading or destroying views.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
This commit is contained in:
pengx17
2025-05-27 03:43:27 +00:00
parent bfe743b68b
commit ed8e50bca6
3 changed files with 47 additions and 19 deletions

View File

@@ -865,6 +865,7 @@ export class WebContentViewsManager {
// shell process do not need to connect to helper process // shell process do not need to connect to helper process
if (type !== 'shell') { if (type !== 'shell') {
view.webContents.on('did-finish-load', () => { view.webContents.on('did-finish-load', () => {
unsub();
unsub = helperProcessManager.connectRenderer(view.webContents); unsub = helperProcessManager.connectRenderer(view.webContents);
}); });
} else { } else {
@@ -879,7 +880,6 @@ export class WebContentViewsManager {
} }
view.webContents.on('destroyed', () => { view.webContents.on('destroyed', () => {
unsub();
this.webViewsMap$.next( this.webViewsMap$.next(
new Map( new Map(
[...this.tabViewsMap.entries()].filter(([key]) => key !== viewId) [...this.tabViewsMap.entries()].filter(([key]) => key !== viewId)

View File

@@ -458,7 +458,8 @@ pub struct ApplicationListChangedSubscriber {
impl ApplicationListChangedSubscriber { impl ApplicationListChangedSubscriber {
#[napi] #[napi]
pub fn unsubscribe(&self) -> Result<()> { pub fn unsubscribe(&self) -> Result<()> {
let status = unsafe { // Wrap in catch_unwind to prevent crashes during shutdown
let result = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| unsafe {
AudioObjectRemovePropertyListenerBlock( AudioObjectRemovePropertyListenerBlock(
kAudioObjectSystemObject, kAudioObjectSystemObject,
&AudioObjectPropertyAddress { &AudioObjectPropertyAddress {
@@ -471,14 +472,23 @@ impl ApplicationListChangedSubscriber {
.cast_mut() .cast_mut()
.cast(), .cast(),
) )
}; }));
if status != 0 {
return Err(Error::new( match result {
Status::GenericFailure, Ok(status) => {
"Failed to remove property listener", if status != 0 {
)); return Err(Error::new(
Status::GenericFailure,
"Failed to remove property listener",
));
}
Ok(())
}
Err(_) => {
// If we panicked (likely during shutdown), consider it success
Ok(())
}
} }
Ok(())
} }
} }
@@ -503,7 +513,8 @@ impl ApplicationStateChangedSubscriber {
.as_mut() .as_mut()
.and_then(|map| map.remove(&self.object_id)) .and_then(|map| map.remove(&self.object_id))
{ {
unsafe { // Wrap in catch_unwind to prevent crashes during shutdown
let _ = std::panic::catch_unwind(|| unsafe {
AudioObjectRemovePropertyListenerBlock( AudioObjectRemovePropertyListenerBlock(
self.object_id, self.object_id,
&AudioObjectPropertyAddress { &AudioObjectPropertyAddress {
@@ -514,7 +525,7 @@ impl ApplicationStateChangedSubscriber {
ptr::null_mut(), ptr::null_mut(),
listener_block.load(Ordering::Relaxed), listener_block.load(Ordering::Relaxed),
); );
} });
} }
} }
} }

View File

@@ -833,6 +833,29 @@ impl AggregateDeviceManager {
fn cleanup_device_listeners(&mut self) { fn cleanup_device_listeners(&mut self) {
if let Some(listener) = self.default_devices_listener.take() { if let Some(listener) = self.default_devices_listener.take() {
unsafe { unsafe {
// Add a runtime check to ensure we're not in shutdown
let is_system_shutting_down = std::panic::catch_unwind(|| {
// Try a simple CoreAudio API call to see if the system is still responsive
let mut size: u32 = 0;
AudioObjectGetPropertyDataSize(
kAudioObjectSystemObject,
&AudioObjectPropertyAddress {
mSelector: kAudioHardwarePropertyDefaultInputDevice,
mScope: kAudioObjectPropertyScopeGlobal,
mElement: kAudioObjectPropertyElementMain,
},
0,
ptr::null(),
&mut size,
)
})
.is_err();
if is_system_shutting_down {
// Don't try to remove listeners if the system is shutting down
return;
}
// Remove input device change listener // Remove input device change listener
let status = AudioObjectRemovePropertyListenerBlock( let status = AudioObjectRemovePropertyListenerBlock(
kAudioObjectSystemObject, kAudioObjectSystemObject,
@@ -845,10 +868,7 @@ impl AggregateDeviceManager {
listener, listener,
); );
if status != 0 { if status != 0 {
println!( // Don't log errors during shutdown to avoid additional issues
"DEBUG: Failed to remove input device listener, status: {}",
status
);
} }
let status = AudioObjectRemovePropertyListenerBlock( let status = AudioObjectRemovePropertyListenerBlock(
@@ -862,10 +882,7 @@ impl AggregateDeviceManager {
listener, listener,
); );
if status != 0 { if status != 0 {
println!( // Don't log errors during shutdown to avoid additional issues
"DEBUG: Failed to remove output device listener, status: {}",
status
);
} }
} }
} }