Make iOS fully thread safe (#3045)
* macOS & iOS: Refactor EventWrapper * macOS & iOS: Make EventLoopWindowTarget independent of the user event * iOS: Use MainThreadMarker instead of marking functions unsafe * Make iOS thread safe
This commit is contained in:
parent
d9f04780cc
commit
86baa1c99a
14 changed files with 457 additions and 445 deletions
|
|
@ -16,19 +16,21 @@ 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};
|
||||
use once_cell::sync::Lazy;
|
||||
|
||||
use super::event_loop::{EventHandler, Never};
|
||||
use super::uikit::UIView;
|
||||
use super::view::WinitUIWindow;
|
||||
use crate::{
|
||||
dpi::LogicalSize,
|
||||
dpi::PhysicalSize,
|
||||
event::{Event, InnerSizeWriter, StartCause, WindowEvent},
|
||||
event_loop::ControlFlow,
|
||||
platform_impl::platform::event_loop::{EventHandler, EventProxy, EventWrapper, Never},
|
||||
window::WindowId as RootWindowId,
|
||||
};
|
||||
|
||||
|
|
@ -44,6 +46,19 @@ macro_rules! bug_assert {
|
|||
};
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum EventWrapper {
|
||||
StaticEvent(Event<Never>),
|
||||
ScaleFactorChanged(ScaleFactorChanged),
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct ScaleFactorChanged {
|
||||
pub(super) window: Id<WinitUIWindow>,
|
||||
pub(super) suggested_size: PhysicalSize<u32>,
|
||||
pub(super) scale_factor: f64,
|
||||
}
|
||||
|
||||
enum UserCallbackTransitionResult<'a> {
|
||||
Success {
|
||||
event_handler: Box<dyn EventHandler>,
|
||||
|
|
@ -114,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(),
|
||||
|
|
@ -142,7 +151,7 @@ impl AppState {
|
|||
waker,
|
||||
});
|
||||
}
|
||||
init_guard(&mut guard)
|
||||
init_guard(&mut guard);
|
||||
}
|
||||
RefMut::map(guard, |state| state.as_mut().unwrap())
|
||||
}
|
||||
|
|
@ -451,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,
|
||||
|
|
@ -474,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,
|
||||
|
|
@ -506,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),
|
||||
|
|
@ -541,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();
|
||||
|
|
@ -551,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
|
||||
|
|
@ -566,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 } => {
|
||||
|
|
@ -615,14 +618,14 @@ pub(crate) unsafe fn handle_nonuser_events<I: IntoIterator<Item = EventWrapper>>
|
|||
}
|
||||
event_handler.handle_nonuser_event(event, &mut control_flow)
|
||||
}
|
||||
EventWrapper::EventProxy(proxy) => {
|
||||
handle_event_proxy(&mut event_handler, control_flow, proxy)
|
||||
EventWrapper::ScaleFactorChanged(event) => {
|
||||
handle_hidpi_proxy(&mut event_handler, control_flow, event)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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,
|
||||
|
|
@ -672,17 +675,16 @@ pub(crate) unsafe fn handle_nonuser_events<I: IntoIterator<Item = EventWrapper>>
|
|||
}
|
||||
event_handler.handle_nonuser_event(event, &mut control_flow)
|
||||
}
|
||||
EventWrapper::EventProxy(proxy) => {
|
||||
handle_event_proxy(&mut event_handler, control_flow, proxy)
|
||||
EventWrapper::ScaleFactorChanged(event) => {
|
||||
handle_hidpi_proxy(&mut event_handler, control_flow, event)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 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() {
|
||||
|
|
@ -703,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,
|
||||
|
|
@ -734,8 +736,8 @@ unsafe fn handle_user_events() {
|
|||
EventWrapper::StaticEvent(event) => {
|
||||
event_handler.handle_nonuser_event(event, &mut control_flow)
|
||||
}
|
||||
EventWrapper::EventProxy(proxy) => {
|
||||
handle_event_proxy(&mut event_handler, control_flow, proxy)
|
||||
EventWrapper::ScaleFactorChanged(event) => {
|
||||
handle_hidpi_proxy(&mut event_handler, control_flow, event)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -743,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;
|
||||
}
|
||||
|
|
@ -755,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()
|
||||
|
|
@ -770,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);
|
||||
|
|
@ -789,34 +788,17 @@ pub unsafe fn terminated() {
|
|||
event_handler.handle_nonuser_event(Event::LoopExiting, &mut control_flow)
|
||||
}
|
||||
|
||||
fn handle_event_proxy(
|
||||
event_handler: &mut Box<dyn EventHandler>,
|
||||
control_flow: ControlFlow,
|
||||
proxy: EventProxy,
|
||||
) {
|
||||
match proxy {
|
||||
EventProxy::DpiChangedProxy {
|
||||
suggested_size,
|
||||
scale_factor,
|
||||
window,
|
||||
} => handle_hidpi_proxy(
|
||||
event_handler,
|
||||
control_flow,
|
||||
suggested_size,
|
||||
scale_factor,
|
||||
window,
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
fn handle_hidpi_proxy(
|
||||
event_handler: &mut Box<dyn EventHandler>,
|
||||
mut control_flow: ControlFlow,
|
||||
suggested_size: LogicalSize<f64>,
|
||||
scale_factor: f64,
|
||||
window: Id<WinitUIWindow>,
|
||||
event: ScaleFactorChanged,
|
||||
) {
|
||||
let new_inner_size = Arc::new(Mutex::new(suggested_size.to_physical(scale_factor)));
|
||||
let ScaleFactorChanged {
|
||||
suggested_size,
|
||||
scale_factor,
|
||||
window,
|
||||
} = event;
|
||||
let new_inner_size = Arc::new(Mutex::new(suggested_size));
|
||||
let event = Event::WindowEvent {
|
||||
window_id: RootWindowId(window.id()),
|
||||
event: WindowEvent::ScaleFactorChanged {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue