diff options
Diffstat (limited to 'engine')
| -rw-r--r-- | engine/src/windowing.rs | 138 |
1 files changed, 114 insertions, 24 deletions
diff --git a/engine/src/windowing.rs b/engine/src/windowing.rs index ea5b5f7..1b4ed7a 100644 --- a/engine/src/windowing.rs +++ b/engine/src/windowing.rs @@ -3,7 +3,7 @@ use std::env::consts::EXE_SUFFIX; use std::hint::cold_path; use std::panic::catch_unwind; use std::sync::atomic::{AtomicBool, Ordering}; -use std::sync::{Arc, Mutex, Weak}; +use std::sync::{Arc, Condvar, Mutex, Weak}; use std::thread::Builder as ThreadBuilder; use crossbeam_queue::ArrayQueue; @@ -169,6 +169,12 @@ fn update_stuff( entity_obtainer: EntityObtainer, ) -> Result<(), EcsError> { + if context.display_handle.is_none() { + cold_path(); + actions.stop(); + return Ok(()); + } + keyboard.make_key_states_previous(); mouse_buttons.make_states_previous(); @@ -191,19 +197,13 @@ fn update_stuff( keyboard.set_text_keys(iter_array_queue(&context.shared_state.text_keys)); let Context { - ref mut display_handle, - ref mut windows, - ref shared_state, - .. + ref mut windows, ref shared_state, .. } = *context; for message in iter_array_queue(&shared_state.message_from_app_queue) { tracing::trace!(message=?message, "Received message from app"); match message { - MessageFromApp::Init(new_display_handle) => { - *display_handle = Some(new_display_handle); - } MessageFromApp::WindowCreated( window_ent_id, winit_window, @@ -307,9 +307,17 @@ fn update_stuff( if shared_state.has_thread_err.load(Ordering::Relaxed) { cold_path(); - let mut err = shared_state.thread_err.try_lock().expect("Not possible"); + let Ok(mut err) = shared_state.thread_err.try_lock() else { + // The windowing app thread always unlocks the thread_err mutex before + // setting the has_thread_err atomic + unreachable!(); + }; - let err = err.take().expect("Not possible"); + let Some(err) = err.take() else { + // The has_thread_err atomic is set to true so this option is always + // Some + unreachable!(); + }; return Err(err.into()); } @@ -381,9 +389,7 @@ impl Context { pub fn display_handle(&self) -> Option<DisplayHandle<'_>> { - let display_handle = self.display_handle.as_ref()?; - - display_handle.display_handle().ok() + self.display_handle.as_ref()?.display_handle().ok() } /// Returns the specified window as a window handle, if it exists. @@ -443,7 +449,7 @@ impl Context let shared_state = Arc::new(SharedState::default()); let shared_state_b = shared_state.clone(); - ThreadBuilder::new() + if let Err(err) = ThreadBuilder::new() .name("windowing app".to_string()) .spawn(move || { let shared_state_c = shared_state_b.clone(); @@ -471,26 +477,93 @@ impl Context }) { Ok(Ok(())) => {} Ok(Err((app, err))) => { - *app.shared_state - .thread_err - .try_lock() - .expect("Not possible") = Some(err); + { + let Ok(mut thread_err) = + app.shared_state.thread_err.try_lock() + else { + // The thread_err mutex is only locked by the main thread + // when the has_thread_err atomic is set to true, which + // it can not be set to yet + unreachable!(); + }; + + *thread_err = Some(err); + } + app.shared_state .has_thread_err .store(true, Ordering::Relaxed); + + app.shared_state.init_data_cond.notify_one(); } Err(_) => { shared_state_c .thread_panicked .store(true, Ordering::Relaxed); + + shared_state_c.init_data_cond.notify_one(); } }; }) - .expect("Failed to create windowing thread"); + { + tracing::error!("Failed to create windowing app thread: {err}"); + } + + let mut init_data_lock = shared_state.init_data.lock().unwrap_or_else(|err| { + let mut lock = err.into_inner(); + *lock = None; + lock + }); + + while init_data_lock.is_none() + && !shared_state.has_thread_err.load(Ordering::Relaxed) + && !shared_state.thread_panicked.load(Ordering::Relaxed) + { + init_data_lock = shared_state + .init_data_cond + .wait(init_data_lock) + .unwrap_or_else(|err| { + let mut lock = err.into_inner(); + *lock = None; + lock + }); + } + + let init_data = init_data_lock.take(); + + drop(init_data_lock); + + let Some(init_data) = init_data else { + if shared_state.thread_panicked.load(Ordering::Relaxed) { + tracing::error!("Windowing app thread panicked"); + } else if shared_state.has_thread_err.load(Ordering::Relaxed) { + let Ok(mut err) = shared_state.thread_err.try_lock() else { + // The windowing app thread always unlocks the thread_err mutex before + // setting the has_thread_err atomic + unreachable!(); + }; + + let Some(err) = err.take() else { + // The has_thread_err atomic is set to true so this option is always + // Some + unreachable!(); + }; + + tracing::error!("Error in windowing app thread: {err}"); + } else { + tracing::error!("Windowing app initialization aborted unexpectedly"); + } + + return Self { + shared_state, + display_handle: None, + windows: MapVec::default(), + }; + }; Self { shared_state, - display_handle: None, + display_handle: Some(init_data.display), windows: MapVec::default(), } } @@ -540,7 +613,6 @@ enum AppThreadError #[derive(Debug)] enum MessageFromApp { - Init(OwnedDisplayHandle), WindowCreated(Uid, Arc<WinitWindow>, WindowCreationAttributes), WindowResized(WindowId, PhysicalSize<u32>), WindowCloseRequested(WindowId), @@ -568,6 +640,8 @@ struct SharedState thread_panicked: AtomicBool, thread_err: Mutex<Option<AppThreadError>>, has_thread_err: AtomicBool, + init_data: Mutex<Option<InitData>>, + init_data_cond: Condvar, } impl Default for SharedState @@ -584,11 +658,19 @@ impl Default for SharedState thread_panicked: AtomicBool::new(false), thread_err: Mutex::new(None), has_thread_err: AtomicBool::new(false), + init_data: Mutex::new(None), + init_data_cond: Condvar::new(), } } } #[derive(Debug)] +struct InitData +{ + display: OwnedDisplayHandle, +} + +#[derive(Debug)] struct App { shared_state: Arc<SharedState>, @@ -674,9 +756,17 @@ impl ApplicationHandler for App { match cause { StartCause::Init => { - self.send_message(MessageFromApp::Init( - event_loop.owned_display_handle(), - )); + let Ok(mut init_data) = self.shared_state.init_data.lock() else { + tracing::error!("Init data mutex is poisoned, exiting event loop"); + event_loop.exit(); + return; + }; + + *init_data = Some(InitData { + display: event_loop.owned_display_handle(), + }); + + self.shared_state.init_data_cond.notify_one(); } StartCause::Poll => { if self.shared_state.is_dropped.load(Ordering::Relaxed) { |
