summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--engine/src/windowing.rs144
1 files changed, 120 insertions, 24 deletions
diff --git a/engine/src/windowing.rs b/engine/src/windowing.rs
index ea5b5f7..75d0c18 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,99 @@ 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}");
+
+ return Self {
+ shared_state,
+ display_handle: None,
+ windows: MapVec::default(),
+ };
+ }
+
+ 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 +619,6 @@ enum AppThreadError
#[derive(Debug)]
enum MessageFromApp
{
- Init(OwnedDisplayHandle),
WindowCreated(Uid, Arc<WinitWindow>, WindowCreationAttributes),
WindowResized(WindowId, PhysicalSize<u32>),
WindowCloseRequested(WindowId),
@@ -568,6 +646,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 +664,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 +762,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) {