catch asset loader panics (#14809)

# Objective

currently if an asset loader panics, the asset is left in a perpetual
`Loading` state. this can occur with external crates (eg the image crate
panics on bad data). catch this panic and resolve the asset to `Failed`

## Solution

`AssertUnwindSafe(loader.load).catch_unwind()` and map the panic to an
`AssetLoadError`

separated out from #13170
This commit is contained in:
robtfm 2024-08-19 22:50:39 +01:00 committed by GitHub
parent 80028d1323
commit 75738ed80f
No known key found for this signature in database
GPG key ID: B5690EEEBB952194

View file

@ -22,13 +22,13 @@ use bevy_tasks::IoTaskPool;
use bevy_utils::tracing::{error, info};
use bevy_utils::{CowArc, HashSet};
use crossbeam_channel::{Receiver, Sender};
use futures_lite::StreamExt;
use futures_lite::{FutureExt, StreamExt};
use info::*;
use loaders::*;
use parking_lot::RwLock;
use std::future::Future;
use std::{any::Any, path::PathBuf};
use std::{any::TypeId, path::Path, sync::Arc};
use std::{future::Future, panic::AssertUnwindSafe};
use thiserror::Error;
// Needed for doc string
@ -1176,13 +1176,20 @@ impl AssetServer {
let asset_path = asset_path.clone_owned();
let load_context =
LoadContext::new(self, asset_path.clone(), load_dependencies, populate_hashes);
loader.load(reader, meta, load_context).await.map_err(|e| {
AssetLoadError::AssetLoaderError(AssetLoaderError {
AssertUnwindSafe(loader.load(reader, meta, load_context))
.catch_unwind()
.await
.map_err(|_| AssetLoadError::AssetLoaderPanic {
path: asset_path.clone_owned(),
loader_name: loader.type_name(),
error: e.into(),
})?
.map_err(|e| {
AssetLoadError::AssetLoaderError(AssetLoaderError {
path: asset_path.clone_owned(),
loader_name: loader.type_name(),
error: e.into(),
})
})
})
}
}
@ -1404,6 +1411,11 @@ pub enum AssetLoadError {
CannotLoadProcessedAsset { path: AssetPath<'static> },
#[error("Asset '{path}' is configured to be ignored. It cannot be loaded.")]
CannotLoadIgnoredAsset { path: AssetPath<'static> },
#[error("Failed to load asset '{path}', asset loader '{loader_name}' panicked")]
AssetLoaderPanic {
path: AssetPath<'static>,
loader_name: &'static str,
},
#[error(transparent)]
AssetLoaderError(#[from] AssetLoaderError),
#[error(transparent)]