diff options
| -rw-r--r-- | engine/src/asset.rs | 13 | ||||
| -rw-r--r-- | engine/src/work_queue.rs | 36 |
2 files changed, 46 insertions, 3 deletions
diff --git a/engine/src/asset.rs b/engine/src/asset.rs index 95d9dc3..d22b3f1 100644 --- a/engine/src/asset.rs +++ b/engine/src/asset.rs @@ -6,6 +6,7 @@ use std::convert::Infallible; use std::ffi::{OsStr, OsString}; use std::fmt::{Debug, Display}; use std::hash::{DefaultHasher, Hash, Hasher}; +use std::hint::cold_path; use std::marker::PhantomData; use std::path::{Path, PathBuf}; use std::sync::mpsc::{ @@ -15,6 +16,8 @@ use std::sync::mpsc::{ }; use std::sync::Arc; +use ecs::actions::Actions; + use crate::ecs::pair::{ChildOf, Pair}; use crate::ecs::phase::{Phase, PRE_UPDATE as PRE_UPDATE_PHASE}; use crate::ecs::sole::Single; @@ -851,6 +854,7 @@ impl crate::ecs::extension::Extension for Extension collector.add_declared_entity(&HANDLE_ASSETS_PHASE); collector.add_system(*HANDLE_ASSETS_PHASE, add_received_assets); + collector.add_system(*HANDLE_ASSETS_PHASE, check_import_wq_thread_not_panicked); } } @@ -881,6 +885,15 @@ fn add_received_assets(mut assets: Single<Assets>) } } +fn check_import_wq_thread_not_panicked(assets: Single<Assets>, mut actions: Actions<'_>) +{ + if assets.import_work_queue.get_thread_panic().is_some() { + cold_path(); + + actions.stop(); + } +} + #[derive(Debug)] struct ImportWorkUserData { diff --git a/engine/src/work_queue.rs b/engine/src/work_queue.rs index a2b7db9..494d2b5 100644 --- a/engine/src/work_queue.rs +++ b/engine/src/work_queue.rs @@ -1,5 +1,8 @@ +use std::borrow::Cow; use std::marker::PhantomData; +use std::panic::catch_unwind; use std::sync::mpsc::{channel as mpsc_channel, Sender as MpscSender}; +use std::sync::{Arc, OnceLock}; use std::thread::{Builder as ThreadBuilder, JoinHandle as ThreadJoinHandle}; pub struct Work<UserData: Send + Sync + 'static> @@ -12,6 +15,7 @@ pub struct Work<UserData: Send + Sync + 'static> pub struct WorkQueue<UserData: Send + Sync + 'static> { work_sender: MpscSender<Work<UserData>>, + thread_panic: Arc<OnceLock<Box<str>>>, _thread: ThreadJoinHandle<()>, _pd: PhantomData<UserData>, } @@ -22,15 +26,36 @@ impl<UserData: Send + Sync + 'static> WorkQueue<UserData> { let (work_sender, work_receiver) = mpsc_channel::<Work<UserData>>(); + let thread_panic = Arc::new(OnceLock::new()); + + let thread_panic_b = thread_panic.clone(); + Self { work_sender, + thread_panic: thread_panic, _thread: ThreadBuilder::new() .name(name.to_string()) .spawn(move || { - let work_receiver = work_receiver; + if let Err(panic_err) = catch_unwind(|| { + while let Ok(work) = work_receiver.recv() { + (work.func)(work.user_data); + } + }) { + let panic_message: Cow<'static, str> = + if let Some(panic_message) = + panic_err.downcast_ref::<&'static str>() + { + (*panic_message).into() + } else if let Some(panic_message) = + panic_err.downcast_ref::<String>() + { + panic_message.clone().into() + } else { + "(unknown panic payload type)".into() + }; - while let Ok(work) = work_receiver.recv() { - (work.func)(work.user_data); + let _ = thread_panic_b + .set(panic_message.into_owned().into_boxed_str()); } }) .expect("Failed to create work queue thread"), @@ -38,6 +63,11 @@ impl<UserData: Send + Sync + 'static> WorkQueue<UserData> } } + pub fn get_thread_panic(&self) -> Option<&str> + { + self.thread_panic.get().map(|thread_panic| &**thread_panic) + } + pub fn add_work(&self, work: Work<UserData>) { if self.work_sender.send(work).is_err() { |
