Skip to content

Commit

Permalink
iOS: Use MainThreadMarker instead of marking functions unsafe
Browse files Browse the repository at this point in the history
  • Loading branch information
madsmtm committed Aug 27, 2023
1 parent e93d5e5 commit c14e670
Show file tree
Hide file tree
Showing 4 changed files with 157 additions and 167 deletions.
100 changes: 43 additions & 57 deletions src/platform_impl/ios/app_state.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,9 @@ use core_foundation::runloop::{
kCFRunLoopCommonModes, CFRunLoopAddTimer, CFRunLoopGetMain, CFRunLoopRef, CFRunLoopTimerCreate,
CFRunLoopTimerInvalidate, CFRunLoopTimerRef, CFRunLoopTimerSetNextFireDate,
};
use icrate::Foundation::{CGRect, CGSize, NSInteger, NSOperatingSystemVersion, NSProcessInfo};
use icrate::Foundation::{
CGRect, CGSize, MainThreadMarker, NSInteger, NSOperatingSystemVersion, NSProcessInfo,
};
use objc2::rc::Id;
use objc2::runtime::AnyObject;
use objc2::{msg_send, sel};
Expand Down Expand Up @@ -127,24 +129,18 @@ struct AppState {
}

impl AppState {
// requires main thread
unsafe fn get_mut() -> RefMut<'static, AppState> {
fn get_mut(_mtm: MainThreadMarker) -> RefMut<'static, AppState> {
// basically everything in UIKit requires the main thread, so it's pointless to use the
// std::sync APIs.
// must be mut because plain `static` requires `Sync`
static mut APP_STATE: RefCell<Option<AppState>> = RefCell::new(None);

if cfg!(debug_assertions) {
assert_main_thread!(
"bug in winit: `AppState::get_mut()` can only be called on the main thread"
);
}
let mut guard = APP_STATE.borrow_mut();
let mut guard = unsafe { APP_STATE.borrow_mut() };
if guard.is_none() {
#[inline(never)]
#[cold]
unsafe fn init_guard(guard: &mut RefMut<'static, Option<AppState>>) {
let waker = EventLoopWaker::new(CFRunLoopGetMain());
fn init_guard(guard: &mut RefMut<'static, Option<AppState>>) {
let waker = EventLoopWaker::new(unsafe { CFRunLoopGetMain() });
**guard = Some(AppState {
app_state: Some(AppStateImpl::NotLaunched {
queued_windows: Vec::new(),
Expand All @@ -155,7 +151,7 @@ impl AppState {
waker,
});
}
init_guard(&mut guard)
init_guard(&mut guard);
}
RefMut::map(guard, |state| state.as_mut().unwrap())
}
Expand Down Expand Up @@ -464,10 +460,8 @@ impl AppState {
}
}

// requires main thread and window is a UIWindow
// retains window
pub(crate) unsafe fn set_key_window(window: &Id<WinitUIWindow>) {
let mut this = AppState::get_mut();
pub(crate) fn set_key_window(mtm: MainThreadMarker, window: &Id<WinitUIWindow>) {
let mut this = AppState::get_mut(mtm);
match this.state_mut() {
&mut AppStateImpl::NotLaunched {
ref mut queued_windows,
Expand All @@ -487,10 +481,8 @@ pub(crate) unsafe fn set_key_window(window: &Id<WinitUIWindow>) {
window.makeKeyAndVisible();
}

// requires main thread and window is a UIWindow
// retains window
pub(crate) unsafe fn queue_gl_or_metal_redraw(window: Id<WinitUIWindow>) {
let mut this = AppState::get_mut();
pub(crate) fn queue_gl_or_metal_redraw(mtm: MainThreadMarker, window: Id<WinitUIWindow>) {
let mut this = AppState::get_mut(mtm);
match this.state_mut() {
&mut AppStateImpl::NotLaunched {
ref mut queued_gpu_redraws,
Expand Down Expand Up @@ -519,14 +511,12 @@ pub(crate) unsafe fn queue_gl_or_metal_redraw(window: Id<WinitUIWindow>) {
}
}

// requires main thread
pub unsafe fn will_launch(queued_event_handler: Box<dyn EventHandler>) {
AppState::get_mut().will_launch_transition(queued_event_handler)
pub fn will_launch(mtm: MainThreadMarker, queued_event_handler: Box<dyn EventHandler>) {
AppState::get_mut(mtm).will_launch_transition(queued_event_handler)
}

// requires main thread
pub unsafe fn did_finish_launching() {
let mut this = AppState::get_mut();
pub fn did_finish_launching(mtm: MainThreadMarker) {
let mut this = AppState::get_mut(mtm);
let windows = match this.state_mut() {
AppStateImpl::Launching { queued_windows, .. } => mem::take(queued_windows),
s => bug!("unexpected state {:?}", s),
Expand Down Expand Up @@ -554,7 +544,7 @@ pub unsafe fn did_finish_launching() {
// completed. This may result in incorrect visual appearance.
// ```
let screen = window.screen();
let _: () = msg_send![&window, setScreen: ptr::null::<AnyObject>()];
let _: () = unsafe { msg_send![&window, setScreen: ptr::null::<AnyObject>()] };
window.setScreen(&screen);

let controller = window.rootViewController();
Expand All @@ -564,13 +554,13 @@ pub unsafe fn did_finish_launching() {
window.makeKeyAndVisible();
}

let (windows, events) = AppState::get_mut().did_finish_launching_transition();
let (windows, events) = AppState::get_mut(mtm).did_finish_launching_transition();

let events = std::iter::once(EventWrapper::StaticEvent(Event::NewEvents(
StartCause::Init,
)))
.chain(events);
handle_nonuser_events(events);
handle_nonuser_events(mtm, events);

// the above window dance hack, could possibly trigger new windows to be created.
// we can just set those windows up normally, as they were created after didFinishLaunching
Expand All @@ -579,27 +569,27 @@ pub unsafe fn did_finish_launching() {
}
}

// requires main thread
// AppState::did_finish_launching handles the special transition `Init`
pub unsafe fn handle_wakeup_transition() {
let mut this = AppState::get_mut();
pub fn handle_wakeup_transition(mtm: MainThreadMarker) {
let mut this = AppState::get_mut(mtm);
let wakeup_event = match this.wakeup_transition() {
None => return,
Some(wakeup_event) => wakeup_event,
};
drop(this);

handle_nonuser_event(wakeup_event)
handle_nonuser_event(mtm, wakeup_event)
}

// requires main thread
pub(crate) unsafe fn handle_nonuser_event(event: EventWrapper) {
handle_nonuser_events(std::iter::once(event))
pub(crate) fn handle_nonuser_event(mtm: MainThreadMarker, event: EventWrapper) {
handle_nonuser_events(mtm, std::iter::once(event))
}

// requires main thread
pub(crate) unsafe fn handle_nonuser_events<I: IntoIterator<Item = EventWrapper>>(events: I) {
let mut this = AppState::get_mut();
pub(crate) fn handle_nonuser_events<I: IntoIterator<Item = EventWrapper>>(
mtm: MainThreadMarker,
events: I,
) {
let mut this = AppState::get_mut(mtm);
let (mut event_handler, active_control_flow, processing_redraws) =
match this.try_user_callback_transition() {
UserCallbackTransitionResult::ReentrancyPrevented { queued_events } => {
Expand Down Expand Up @@ -635,7 +625,7 @@ pub(crate) unsafe fn handle_nonuser_events<I: IntoIterator<Item = EventWrapper>>
}

loop {
let mut this = AppState::get_mut();
let mut this = AppState::get_mut(mtm);
let queued_events = match this.state_mut() {
&mut AppStateImpl::InUserCallback {
ref mut queued_events,
Expand Down Expand Up @@ -693,9 +683,8 @@ pub(crate) unsafe fn handle_nonuser_events<I: IntoIterator<Item = EventWrapper>>
}
}

// requires main thread
unsafe fn handle_user_events() {
let mut this = AppState::get_mut();
fn handle_user_events(mtm: MainThreadMarker) {
let mut this = AppState::get_mut(mtm);
let mut control_flow = this.control_flow;
let (mut event_handler, active_control_flow, processing_redraws) =
match this.try_user_callback_transition() {
Expand All @@ -716,7 +705,7 @@ unsafe fn handle_user_events() {
event_handler.handle_user_events(&mut control_flow);

loop {
let mut this = AppState::get_mut();
let mut this = AppState::get_mut(mtm);
let queued_events = match this.state_mut() {
&mut AppStateImpl::InUserCallback {
ref mut queued_events,
Expand Down Expand Up @@ -756,9 +745,8 @@ unsafe fn handle_user_events() {
}
}

// requires main thread
pub unsafe fn handle_main_events_cleared() {
let mut this = AppState::get_mut();
pub fn handle_main_events_cleared(mtm: MainThreadMarker) {
let mut this = AppState::get_mut(mtm);
if !this.has_launched() {
return;
}
Expand All @@ -768,9 +756,9 @@ pub unsafe fn handle_main_events_cleared() {
};
drop(this);

handle_user_events();
handle_user_events(mtm);

let mut this = AppState::get_mut();
let mut this = AppState::get_mut(mtm);
let redraw_events: Vec<EventWrapper> = this
.main_events_cleared_transition()
.into_iter()
Expand All @@ -783,18 +771,16 @@ pub unsafe fn handle_main_events_cleared() {
.collect();
drop(this);

handle_nonuser_events(redraw_events);
handle_nonuser_event(EventWrapper::StaticEvent(Event::AboutToWait));
handle_nonuser_events(mtm, redraw_events);
handle_nonuser_event(mtm, EventWrapper::StaticEvent(Event::AboutToWait));
}

// requires main thread
pub unsafe fn handle_events_cleared() {
AppState::get_mut().events_cleared_transition();
pub fn handle_events_cleared(mtm: MainThreadMarker) {
AppState::get_mut(mtm).events_cleared_transition();
}

// requires main thread
pub unsafe fn terminated() {
let mut this = AppState::get_mut();
pub fn terminated(mtm: MainThreadMarker) {
let mut this = AppState::get_mut(mtm);
let mut event_handler = this.terminated_transition();
let mut control_flow = this.control_flow;
drop(this);
Expand Down
62 changes: 31 additions & 31 deletions src/platform_impl/ios/event_loop.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,18 +32,17 @@ use super::{app_state, monitor, view, MonitorHandle};

#[derive(Debug)]
pub struct EventLoopWindowTarget<T: 'static> {
pub(super) mtm: MainThreadMarker,
p: PhantomData<T>,
}

impl<T: 'static> EventLoopWindowTarget<T> {
pub fn available_monitors(&self) -> VecDeque<MonitorHandle> {
monitor::uiscreens(MainThreadMarker::new().unwrap())
monitor::uiscreens(self.mtm)
}

pub fn primary_monitor(&self) -> Option<MonitorHandle> {
Some(MonitorHandle::new(UIScreen::main(
MainThreadMarker::new().unwrap(),
)))
Some(MonitorHandle::new(UIScreen::main(self.mtm)))
}

pub fn raw_display_handle(&self) -> RawDisplayHandle {
Expand All @@ -52,6 +51,7 @@ impl<T: 'static> EventLoopWindowTarget<T> {
}

pub struct EventLoop<T: 'static> {
mtm: MainThreadMarker,
sender: Sender<T>,
receiver: Receiver<T>,
window_target: RootEventLoopWindowTarget<T>,
Expand All @@ -64,7 +64,8 @@ impl<T: 'static> EventLoop<T> {
pub(crate) fn new(
_: &PlatformSpecificEventLoopAttributes,
) -> Result<EventLoop<T>, EventLoopError> {
assert_main_thread!("`EventLoop` can only be created on the main thread on iOS");
let mtm = MainThreadMarker::new()
.expect("On iOS, `EventLoop` must be created on the main thread");

static mut SINGLETON_INIT: bool = false;
unsafe {
Expand All @@ -82,10 +83,14 @@ impl<T: 'static> EventLoop<T> {
setup_control_flow_observers();

Ok(EventLoop {
mtm,
sender,
receiver,
window_target: RootEventLoopWindowTarget {
p: EventLoopWindowTarget { p: PhantomData },
p: EventLoopWindowTarget {
mtm,
p: PhantomData,
},
_marker: PhantomData,
},
})
Expand All @@ -96,7 +101,7 @@ impl<T: 'static> EventLoop<T> {
F: FnMut(Event<T>, &RootEventLoopWindowTarget<T>, &mut ControlFlow),
{
unsafe {
let application = UIApplication::shared(MainThreadMarker::new().unwrap());
let application = UIApplication::shared(self.mtm);
assert!(
application.is_none(),
"\
Expand All @@ -115,7 +120,7 @@ impl<T: 'static> EventLoop<T> {
event_loop: self.window_target,
};

app_state::will_launch(Box::new(handler));
app_state::will_launch(self.mtm, Box::new(handler));

// Ensure application delegate is initialized
view::WinitApplicationDelegate::class();
Expand All @@ -142,9 +147,7 @@ impl<T: 'static> EventLoop<T> {
// EventLoopExtIOS
impl<T: 'static> EventLoop<T> {
pub fn idiom(&self) -> Idiom {
UIDevice::current(MainThreadMarker::new().unwrap())
.userInterfaceIdiom()
.into()
UIDevice::current(self.mtm).userInterfaceIdiom().into()
}
}

Expand Down Expand Up @@ -222,12 +225,11 @@ fn setup_control_flow_observers() {
activity: CFRunLoopActivity,
_: *mut c_void,
) {
unsafe {
#[allow(non_upper_case_globals)]
match activity {
kCFRunLoopAfterWaiting => app_state::handle_wakeup_transition(),
_ => unreachable!(),
}
let mtm = MainThreadMarker::new().unwrap();
#[allow(non_upper_case_globals)]
match activity {
kCFRunLoopAfterWaiting => app_state::handle_wakeup_transition(mtm),
_ => unreachable!(),
}
}

Expand All @@ -247,13 +249,12 @@ fn setup_control_flow_observers() {
activity: CFRunLoopActivity,
_: *mut c_void,
) {
unsafe {
#[allow(non_upper_case_globals)]
match activity {
kCFRunLoopBeforeWaiting => app_state::handle_main_events_cleared(),
kCFRunLoopExit => unimplemented!(), // not expected to ever happen
_ => unreachable!(),
}
let mtm = MainThreadMarker::new().unwrap();
#[allow(non_upper_case_globals)]
match activity {
kCFRunLoopBeforeWaiting => app_state::handle_main_events_cleared(mtm),
kCFRunLoopExit => unimplemented!(), // not expected to ever happen
_ => unreachable!(),
}
}

Expand All @@ -263,13 +264,12 @@ fn setup_control_flow_observers() {
activity: CFRunLoopActivity,
_: *mut c_void,
) {
unsafe {
#[allow(non_upper_case_globals)]
match activity {
kCFRunLoopBeforeWaiting => app_state::handle_events_cleared(),
kCFRunLoopExit => unimplemented!(), // not expected to ever happen
_ => unreachable!(),
}
let mtm = MainThreadMarker::new().unwrap();
#[allow(non_upper_case_globals)]
match activity {
kCFRunLoopBeforeWaiting => app_state::handle_events_cleared(mtm),
kCFRunLoopExit => unimplemented!(), // not expected to ever happen
_ => unreachable!(),
}
}

Expand Down
Loading

0 comments on commit c14e670

Please sign in to comment.