summaryrefslogtreecommitdiff
path: root/engine/src
diff options
context:
space:
mode:
authorHampusM <hampus@hampusmat.com>2026-06-01 02:20:57 +0200
committerHampusM <hampus@hampusmat.com>2026-06-01 02:20:57 +0200
commitc0e661a7c9fa2ab9a2be3c26a3913523667e408f (patch)
tree66d147c1e2a5b63b563350976a2df1b9abe800f0 /engine/src
parentc85300fca2c9ece5d3ecd76f0b156b0b53baf6ea (diff)
fix(engine): stop if asset import work queue thread panics
Diffstat (limited to 'engine/src')
-rw-r--r--engine/src/asset.rs13
-rw-r--r--engine/src/work_queue.rs36
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() {