From c0e661a7c9fa2ab9a2be3c26a3913523667e408f Mon Sep 17 00:00:00 2001 From: HampusM Date: Mon, 1 Jun 2026 02:20:57 +0200 Subject: fix(engine): stop if asset import work queue thread panics --- engine/src/asset.rs | 13 +++++++++++++ engine/src/work_queue.rs | 36 +++++++++++++++++++++++++++++++++--- 2 files changed, 46 insertions(+), 3 deletions(-) (limited to 'engine/src') 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) } } +fn check_import_wq_thread_not_panicked(assets: Single, 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 @@ -12,6 +15,7 @@ pub struct Work pub struct WorkQueue { work_sender: MpscSender>, + thread_panic: Arc>>, _thread: ThreadJoinHandle<()>, _pd: PhantomData, } @@ -22,15 +26,36 @@ impl WorkQueue { let (work_sender, work_receiver) = mpsc_channel::>(); + 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::() + { + 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 WorkQueue } } + pub fn get_thread_panic(&self) -> Option<&str> + { + self.thread_panic.get().map(|thread_panic| &**thread_panic) + } + pub fn add_work(&self, work: Work) { if self.work_sender.send(work).is_err() { -- cgit v1.2.3-18-g5258