Skip to content

Commit 3e50911

Browse files
authored
macOS: Remove panic wrapper (#4147)
This is unnecessary nowadays, unwinding in CF observer callbacks is safe (and is safe in Rust after the introduction of `extern "C-unwind"`). Panicking elsewhere (such as in NSNotificationCenter callbacks or delegate methods) _may_ still lead to an abort, if AppKit tries to catch it with libc++, since Rust panics are not compatible with those. That's "just" a quality-of-implementation detail of current Rust though, not an inherent limitation, and should really be solved in rustc.
1 parent f51a470 commit 3e50911

File tree

3 files changed

+23
-156
lines changed

3 files changed

+23
-156
lines changed

src/platform_impl/apple/appkit/app_state.rs

Lines changed: 6 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
use std::cell::{Cell, OnceCell, RefCell};
22
use std::mem;
3-
use std::rc::{Rc, Weak};
3+
use std::rc::Rc;
44
use std::sync::Arc;
55
use std::time::Instant;
66

@@ -15,7 +15,7 @@ use winit_core::window::WindowId;
1515

1616
use super::super::event_handler::EventHandler;
1717
use super::super::event_loop_proxy::EventLoopProxy;
18-
use super::event_loop::{notify_windows_of_exit, stop_app_immediately, ActiveEventLoop, PanicInfo};
18+
use super::event_loop::{notify_windows_of_exit, stop_app_immediately, ActiveEventLoop};
1919
use super::menu;
2020
use super::observer::{EventLoopWaker, RunLoop};
2121

@@ -309,13 +309,9 @@ impl AppState {
309309
}
310310

311311
// Called by RunLoopObserver after finishing waiting for new events
312-
pub fn wakeup(self: &Rc<Self>, panic_info: Weak<PanicInfo>) {
313-
let panic_info = panic_info
314-
.upgrade()
315-
.expect("The panic info must exist here. This failure indicates a developer error.");
316-
312+
pub fn wakeup(self: &Rc<Self>) {
317313
// Return when in event handler due to https://github.com/rust-windowing/winit/issues/1779
318-
if panic_info.is_panicking() || !self.event_handler.ready() || !self.is_running() {
314+
if !self.event_handler.ready() || !self.is_running() {
319315
return;
320316
}
321317

@@ -341,15 +337,11 @@ impl AppState {
341337
}
342338

343339
// Called by RunLoopObserver before waiting for new events
344-
pub fn cleared(self: &Rc<Self>, panic_info: Weak<PanicInfo>) {
345-
let panic_info = panic_info
346-
.upgrade()
347-
.expect("The panic info must exist here. This failure indicates a developer error.");
348-
340+
pub fn cleared(self: &Rc<Self>) {
349341
// Return when in event handler due to https://github.com/rust-windowing/winit/issues/1779
350342
// XXX: how does it make sense that `event_handler.ready()` can ever return `false` here if
351343
// we're about to return to the `CFRunLoop` to poll for new events?
352-
if panic_info.is_panicking() || !self.event_handler.ready() || !self.is_running() {
344+
if !self.event_handler.ready() || !self.is_running() {
353345
return;
354346
}
355347

src/platform_impl/apple/appkit/event_loop.rs

Lines changed: 2 additions & 89 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,4 @@
1-
use std::any::Any;
2-
use std::cell::Cell;
3-
use std::fmt;
4-
use std::panic::{catch_unwind, resume_unwind, RefUnwindSafe, UnwindSafe};
5-
use std::rc::{Rc, Weak};
1+
use std::rc::Rc;
62
use std::sync::Arc;
73
use std::time::{Duration, Instant};
84

@@ -36,42 +32,6 @@ use super::observer::setup_control_flow_observers;
3632
use crate::platform::macos::ActivationPolicy;
3733
use crate::platform_impl::Window;
3834

39-
#[derive(Default)]
40-
pub struct PanicInfo {
41-
inner: Cell<Option<Box<dyn Any + Send + 'static>>>,
42-
}
43-
44-
impl fmt::Debug for PanicInfo {
45-
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
46-
f.debug_struct("PanicInfo").finish_non_exhaustive()
47-
}
48-
}
49-
50-
// WARNING:
51-
// As long as this struct is used through its `impl`, it is UnwindSafe.
52-
// (If `get_mut` is called on `inner`, unwind safety may get broken.)
53-
impl UnwindSafe for PanicInfo {}
54-
impl RefUnwindSafe for PanicInfo {}
55-
impl PanicInfo {
56-
pub fn is_panicking(&self) -> bool {
57-
let inner = self.inner.take();
58-
let result = inner.is_some();
59-
self.inner.set(inner);
60-
result
61-
}
62-
63-
/// Overwrites the current state if the current state is not panicking
64-
pub fn set_panic(&self, p: Box<dyn Any + Send + 'static>) {
65-
if !self.is_panicking() {
66-
self.inner.set(Some(p));
67-
}
68-
}
69-
70-
pub fn take(&self) -> Option<Box<dyn Any + Send + 'static>> {
71-
self.inner.take()
72-
}
73-
}
74-
7535
#[derive(Debug)]
7636
pub struct ActiveEventLoop {
7737
pub(super) app_state: Rc<AppState>,
@@ -183,7 +143,6 @@ pub struct EventLoop {
183143
app_state: Rc<AppState>,
184144

185145
window_target: ActiveEventLoop,
186-
panic_info: Rc<PanicInfo>,
187146

188147
// Since macOS 10.11, we no longer need to remove the observers before they are deallocated;
189148
// the system instead cleans it up next time it would have posted a notification to it.
@@ -259,14 +218,12 @@ impl EventLoop {
259218
},
260219
);
261220

262-
let panic_info: Rc<PanicInfo> = Default::default();
263-
setup_control_flow_observers(mtm, Rc::downgrade(&panic_info));
221+
setup_control_flow_observers(mtm);
264222

265223
Ok(EventLoop {
266224
app,
267225
app_state: app_state.clone(),
268226
window_target: ActiveEventLoop { app_state, mtm },
269-
panic_info,
270227
_did_finish_launching_observer,
271228
_will_terminate_observer,
272229
})
@@ -306,15 +263,6 @@ impl EventLoop {
306263
// NOTE: Make sure to not run the application re-entrantly, as that'd be confusing.
307264
self.app.run();
308265

309-
// While the app is running it's possible that we catch a panic
310-
// to avoid unwinding across an objective-c ffi boundary, which
311-
// will lead to us stopping the `NSApplication` and saving the
312-
// `PanicInfo` so that we can resume the unwind at a controlled,
313-
// safe point in time.
314-
if let Some(panic) = self.panic_info.take() {
315-
resume_unwind(panic);
316-
}
317-
318266
self.app_state.internal_exit()
319267
})
320268
});
@@ -370,15 +318,6 @@ impl EventLoop {
370318
self.app.run();
371319
}
372320

373-
// While the app is running it's possible that we catch a panic
374-
// to avoid unwinding across an objective-c ffi boundary, which
375-
// will lead to us stopping the application and saving the
376-
// `PanicInfo` so that we can resume the unwind at a controlled,
377-
// safe point in time.
378-
if let Some(panic) = self.panic_info.take() {
379-
resume_unwind(panic);
380-
}
381-
382321
if self.app_state.exiting() {
383322
self.app_state.internal_exit();
384323
PumpStatus::Exit(0)
@@ -423,29 +362,3 @@ pub(super) fn notify_windows_of_exit(app: &NSApplication) {
423362
window.close();
424363
}
425364
}
426-
427-
/// Catches panics that happen inside `f` and when a panic
428-
/// happens, stops the `sharedApplication`
429-
#[inline]
430-
pub fn stop_app_on_panic<F: FnOnce() -> R + UnwindSafe, R>(
431-
mtm: MainThreadMarker,
432-
panic_info: Weak<PanicInfo>,
433-
f: F,
434-
) -> Option<R> {
435-
match catch_unwind(f) {
436-
Ok(r) => Some(r),
437-
Err(e) => {
438-
// It's important that we set the panic before requesting a `stop`
439-
// because some callback are still called during the `stop` message
440-
// and we need to know in those callbacks if the application is currently
441-
// panicking
442-
{
443-
let panic_info = panic_info.upgrade().unwrap();
444-
panic_info.set_panic(e);
445-
}
446-
let app = NSApplication::sharedApplication(mtm);
447-
stop_app_immediately(&app);
448-
None
449-
},
450-
}
451-
}

src/platform_impl/apple/appkit/observer.rs

Lines changed: 15 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,7 @@
44
//! <https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/Multithreading/RunLoopManagement/RunLoopManagement.html>
55
use std::cell::Cell;
66
use std::ffi::c_void;
7-
use std::panic::{AssertUnwindSafe, UnwindSafe};
87
use std::ptr;
9-
use std::rc::Weak;
108
use std::time::Instant;
119

1210
use objc2::MainThreadMarker;
@@ -18,47 +16,18 @@ use objc2_core_foundation::{
1816
use tracing::error;
1917

2018
use super::app_state::AppState;
21-
use super::event_loop::{stop_app_on_panic, PanicInfo};
22-
23-
unsafe fn control_flow_handler<F>(panic_info: *mut c_void, f: F)
24-
where
25-
F: FnOnce(Weak<PanicInfo>) + UnwindSafe,
26-
{
27-
let info_from_raw = unsafe { Weak::from_raw(panic_info as *mut PanicInfo) };
28-
// Asserting unwind safety on this type should be fine because `PanicInfo` is
29-
// `RefUnwindSafe` and `Rc<T>` is `UnwindSafe` if `T` is `RefUnwindSafe`.
30-
let panic_info = AssertUnwindSafe(Weak::clone(&info_from_raw));
31-
// `from_raw` takes ownership of the data behind the pointer.
32-
// But if this scope takes ownership of the weak pointer, then
33-
// the weak pointer will get free'd at the end of the scope.
34-
// However we want to keep that weak reference around after the function.
35-
std::mem::forget(info_from_raw);
36-
37-
let mtm = MainThreadMarker::new().unwrap();
38-
stop_app_on_panic(mtm, Weak::clone(&panic_info), move || {
39-
let _ = &panic_info;
40-
f(panic_info.0)
41-
});
42-
}
4319

4420
// begin is queued with the highest priority to ensure it is processed before other observers
4521
extern "C-unwind" fn control_flow_begin_handler(
4622
_: *mut CFRunLoopObserver,
4723
activity: CFRunLoopActivity,
48-
panic_info: *mut c_void,
24+
_info: *mut c_void,
4925
) {
50-
unsafe {
51-
control_flow_handler(panic_info, |panic_info| {
52-
#[allow(non_upper_case_globals)]
53-
match activity {
54-
CFRunLoopActivity::AfterWaiting => {
55-
// trace!("Triggered `CFRunLoopAfterWaiting`");
56-
AppState::get(MainThreadMarker::new().unwrap()).wakeup(panic_info);
57-
// trace!("Completed `CFRunLoopAfterWaiting`");
58-
},
59-
_ => unreachable!(),
60-
}
61-
});
26+
match activity {
27+
CFRunLoopActivity::AfterWaiting => {
28+
AppState::get(MainThreadMarker::new().unwrap()).wakeup();
29+
},
30+
_ => unreachable!(),
6231
}
6332
}
6433

@@ -67,21 +36,14 @@ extern "C-unwind" fn control_flow_begin_handler(
6736
extern "C-unwind" fn control_flow_end_handler(
6837
_: *mut CFRunLoopObserver,
6938
activity: CFRunLoopActivity,
70-
panic_info: *mut c_void,
39+
_info: *mut c_void,
7140
) {
72-
unsafe {
73-
control_flow_handler(panic_info, |panic_info| {
74-
#[allow(non_upper_case_globals)]
75-
match activity {
76-
CFRunLoopActivity::BeforeWaiting => {
77-
// trace!("Triggered `CFRunLoopBeforeWaiting`");
78-
AppState::get(MainThreadMarker::new().unwrap()).cleared(panic_info);
79-
// trace!("Completed `CFRunLoopBeforeWaiting`");
80-
},
81-
CFRunLoopActivity::Exit => (), /* unimplemented!(), // not expected to ever happen */
82-
_ => unreachable!(),
83-
}
84-
});
41+
match activity {
42+
CFRunLoopActivity::BeforeWaiting => {
43+
AppState::get(MainThreadMarker::new().unwrap()).cleared();
44+
},
45+
CFRunLoopActivity::Exit => (), // unimplemented!(), // not expected to ever happen
46+
_ => unreachable!(),
8547
}
8648
}
8749

@@ -180,11 +142,11 @@ impl RunLoop {
180142
}
181143
}
182144

183-
pub fn setup_control_flow_observers(mtm: MainThreadMarker, panic_info: Weak<PanicInfo>) {
145+
pub fn setup_control_flow_observers(mtm: MainThreadMarker) {
184146
let run_loop = RunLoop::main(mtm);
185147
unsafe {
186148
let mut context = CFRunLoopObserverContext {
187-
info: Weak::into_raw(panic_info) as *mut _,
149+
info: ptr::null_mut(),
188150
version: 0,
189151
retain: None,
190152
release: None,

0 commit comments

Comments
 (0)