Skip to content

Commit 260a7ae

Browse files
committed
- resovles #15098
- implements consistently named AssertServer methods for asset, dependency, and recursive dependency load states - adds error to DependencyLoadState::Failed and RecursiveDependencyLoadState::Failed - fixes bug where successful dependency load overwrites previous dependency failed load state
1 parent 5a0c09d commit 260a7ae

File tree

3 files changed

+244
-41
lines changed

3 files changed

+244
-41
lines changed

crates/bevy_asset/src/lib.rs

+129-5
Original file line numberDiff line numberDiff line change
@@ -1095,13 +1095,19 @@ mod tests {
10951095

10961096
assert!(d_text.is_none());
10971097
assert!(matches!(d_load, LoadState::Failed(_)));
1098-
assert_eq!(d_deps, DependencyLoadState::Failed);
1099-
assert_eq!(d_rec_deps, RecursiveDependencyLoadState::Failed);
1098+
assert!(matches!(d_deps, DependencyLoadState::Failed(_)));
1099+
assert!(matches!(
1100+
d_rec_deps,
1101+
RecursiveDependencyLoadState::Failed(_)
1102+
));
11001103

11011104
assert_eq!(a_text.text, "a");
11021105
assert_eq!(a_load, LoadState::Loaded);
11031106
assert_eq!(a_deps, DependencyLoadState::Loaded);
1104-
assert_eq!(a_rec_deps, RecursiveDependencyLoadState::Failed);
1107+
assert!(matches!(
1108+
a_rec_deps,
1109+
RecursiveDependencyLoadState::Failed(_)
1110+
));
11051111

11061112
assert_eq!(b_text.text, "b");
11071113
assert_eq!(b_load, LoadState::Loaded);
@@ -1110,13 +1116,131 @@ mod tests {
11101116

11111117
assert_eq!(c_text.text, "c");
11121118
assert_eq!(c_load, LoadState::Loaded);
1113-
assert_eq!(c_deps, DependencyLoadState::Failed);
1114-
assert_eq!(c_rec_deps, RecursiveDependencyLoadState::Failed);
1119+
assert!(matches!(c_deps, DependencyLoadState::Failed(_)));
1120+
assert!(matches!(
1121+
c_rec_deps,
1122+
RecursiveDependencyLoadState::Failed(_)
1123+
));
1124+
1125+
assert_eq!(asset_server.load_state(a_id), LoadState::Loaded);
1126+
assert_eq!(
1127+
asset_server.dependency_load_state(a_id),
1128+
DependencyLoadState::Loaded
1129+
);
1130+
assert!(matches!(
1131+
asset_server.recursive_dependency_load_state(a_id),
1132+
RecursiveDependencyLoadState::Failed(_)
1133+
));
1134+
1135+
assert!(asset_server.is_loaded(a_id));
1136+
assert!(asset_server.is_loaded_with_dependencies(a_id));
1137+
assert!(!asset_server.is_loaded_with_recursive_dependencies(a_id));
11151138

11161139
Some(())
11171140
});
11181141
}
11191142

1143+
#[test]
1144+
fn dependency_load_states() {
1145+
// The particular usage of GatedReader in this test will cause deadlocking if running single-threaded
1146+
#[cfg(not(feature = "multi_threaded"))]
1147+
panic!("This test requires the \"multi_threaded\" feature, otherwise it will deadlock.\ncargo test --package bevy_asset --features multi_threaded");
1148+
1149+
let a_path = "a.cool.ron";
1150+
let a_ron = r#"
1151+
(
1152+
text: "a",
1153+
dependencies: [
1154+
"b.cool.ron",
1155+
"c.cool.ron",
1156+
],
1157+
embedded_dependencies: [],
1158+
sub_texts: []
1159+
)"#;
1160+
let b_path = "b.cool.ron";
1161+
let b_ron = r#"
1162+
(
1163+
text: "b",
1164+
dependencies: [],
1165+
MALFORMED
1166+
embedded_dependencies: [],
1167+
sub_texts: []
1168+
)"#;
1169+
1170+
let c_path = "c.cool.ron";
1171+
let c_ron = r#"
1172+
(
1173+
text: "c",
1174+
dependencies: [],
1175+
embedded_dependencies: [],
1176+
sub_texts: []
1177+
)"#;
1178+
1179+
let dir = Dir::default();
1180+
dir.insert_asset_text(Path::new(a_path), a_ron);
1181+
dir.insert_asset_text(Path::new(b_path), b_ron);
1182+
dir.insert_asset_text(Path::new(c_path), c_ron);
1183+
1184+
let (mut app, gate_opener) = test_app(dir);
1185+
app.init_asset::<CoolText>()
1186+
.register_asset_loader(CoolTextLoader);
1187+
let asset_server = app.world().resource::<AssetServer>().clone();
1188+
let handle: Handle<CoolText> = asset_server.load(a_path);
1189+
let a_id = handle.id();
1190+
app.world_mut().spawn(handle);
1191+
1192+
gate_opener.open(a_path);
1193+
run_app_until(&mut app, |world| {
1194+
let _a_text = get::<CoolText>(world, a_id)?;
1195+
let (a_load, a_deps, a_rec_deps) = asset_server.get_load_states(a_id).unwrap();
1196+
assert_eq!(a_load, LoadState::Loaded);
1197+
assert_eq!(a_deps, DependencyLoadState::Loading);
1198+
assert_eq!(a_rec_deps, RecursiveDependencyLoadState::Loading);
1199+
Some(())
1200+
});
1201+
1202+
gate_opener.open(b_path);
1203+
run_app_until(&mut app, |world| {
1204+
let a_text = get::<CoolText>(world, a_id)?;
1205+
let b_id = a_text.dependencies[0].id();
1206+
1207+
let (b_load, _b_deps, _b_rec_deps) = asset_server.get_load_states(b_id).unwrap();
1208+
if !matches!(b_load, LoadState::Failed(_)) {
1209+
// wait until b fails
1210+
return None;
1211+
}
1212+
1213+
let (a_load, a_deps, a_rec_deps) = asset_server.get_load_states(a_id).unwrap();
1214+
assert_eq!(a_load, LoadState::Loaded);
1215+
assert!(matches!(a_deps, DependencyLoadState::Failed(_)));
1216+
assert!(matches!(
1217+
a_rec_deps,
1218+
RecursiveDependencyLoadState::Failed(_)
1219+
));
1220+
Some(())
1221+
});
1222+
1223+
gate_opener.open(c_path);
1224+
run_app_until(&mut app, |world| {
1225+
let a_text = get::<CoolText>(world, a_id)?;
1226+
let c_id = a_text.dependencies[1].id();
1227+
// wait until c loads
1228+
let _c_text = get::<CoolText>(world, c_id)?;
1229+
1230+
let (a_load, a_deps, a_rec_deps) = asset_server.get_load_states(a_id).unwrap();
1231+
assert_eq!(a_load, LoadState::Loaded);
1232+
assert!(
1233+
matches!(a_deps, DependencyLoadState::Failed(_)),
1234+
"Successful dependency load should not overwrite a previous failure"
1235+
);
1236+
assert!(
1237+
matches!(a_rec_deps, RecursiveDependencyLoadState::Failed(_)),
1238+
"Successful dependency load should not overwrite a previous failure"
1239+
);
1240+
Some(())
1241+
});
1242+
}
1243+
11201244
const SIMPLE_TEXT: &str = r#"
11211245
(
11221246
text: "dep",

crates/bevy_asset/src/server/info.rs

+31-16
Original file line numberDiff line numberDiff line change
@@ -388,8 +388,10 @@ impl AssetInfos {
388388
loaded_asset.value.insert(loaded_asset_id, world);
389389
let mut loading_deps = loaded_asset.dependencies;
390390
let mut failed_deps = HashSet::new();
391+
let mut dep_error = None;
391392
let mut loading_rec_deps = loading_deps.clone();
392393
let mut failed_rec_deps = HashSet::new();
394+
let mut rec_dep_error = None;
393395
loading_deps.retain(|dep_id| {
394396
if let Some(dep_info) = self.get_mut(*dep_id) {
395397
match dep_info.rec_dep_load_state {
@@ -404,7 +406,10 @@ impl AssetInfos {
404406
// If dependency is loaded, reduce our count by one
405407
loading_rec_deps.remove(dep_id);
406408
}
407-
RecursiveDependencyLoadState::Failed => {
409+
RecursiveDependencyLoadState::Failed(ref error) => {
410+
if rec_dep_error.is_none() {
411+
rec_dep_error = Some(error.clone());
412+
}
408413
failed_rec_deps.insert(*dep_id);
409414
loading_rec_deps.remove(dep_id);
410415
}
@@ -419,7 +424,10 @@ impl AssetInfos {
419424
// If dependency is loaded, reduce our count by one
420425
false
421426
}
422-
LoadState::Failed(_) => {
427+
LoadState::Failed(ref error) => {
428+
if dep_error.is_none() {
429+
dep_error = Some(error.clone());
430+
}
423431
failed_deps.insert(*dep_id);
424432
false
425433
}
@@ -437,7 +445,7 @@ impl AssetInfos {
437445
let dep_load_state = match (loading_deps.len(), failed_deps.len()) {
438446
(0, 0) => DependencyLoadState::Loaded,
439447
(_loading, 0) => DependencyLoadState::Loading,
440-
(_loading, _failed) => DependencyLoadState::Failed,
448+
(_loading, _failed) => DependencyLoadState::Failed(dep_error.unwrap()),
441449
};
442450

443451
let rec_dep_load_state = match (loading_rec_deps.len(), failed_rec_deps.len()) {
@@ -450,7 +458,7 @@ impl AssetInfos {
450458
RecursiveDependencyLoadState::Loaded
451459
}
452460
(_loading, 0) => RecursiveDependencyLoadState::Loading,
453-
(_loading, _failed) => RecursiveDependencyLoadState::Failed,
461+
(_loading, _failed) => RecursiveDependencyLoadState::Failed(rec_dep_error.unwrap()),
454462
};
455463

456464
let (dependants_waiting_on_load, dependants_waiting_on_rec_load) = {
@@ -480,14 +488,14 @@ impl AssetInfos {
480488
info.failed_rec_dependencies = failed_rec_deps;
481489
info.load_state = LoadState::Loaded;
482490
info.dep_load_state = dep_load_state;
483-
info.rec_dep_load_state = rec_dep_load_state;
491+
info.rec_dep_load_state = rec_dep_load_state.clone();
484492
if watching_for_changes {
485493
info.loader_dependencies = loaded_asset.loader_dependencies;
486494
}
487495

488496
let dependants_waiting_on_rec_load = if matches!(
489497
rec_dep_load_state,
490-
RecursiveDependencyLoadState::Loaded | RecursiveDependencyLoadState::Failed
498+
RecursiveDependencyLoadState::Loaded | RecursiveDependencyLoadState::Failed(_)
491499
) {
492500
Some(std::mem::take(
493501
&mut info.dependants_waiting_on_recursive_dep_load,
@@ -505,7 +513,9 @@ impl AssetInfos {
505513
for id in dependants_waiting_on_load {
506514
if let Some(info) = self.get_mut(id) {
507515
info.loading_dependencies.remove(&loaded_asset_id);
508-
if info.loading_dependencies.is_empty() {
516+
if info.loading_dependencies.is_empty()
517+
&& !matches!(info.dep_load_state, DependencyLoadState::Failed(_))
518+
{
509519
// send dependencies loaded event
510520
info.dep_load_state = DependencyLoadState::Loaded;
511521
}
@@ -519,9 +529,9 @@ impl AssetInfos {
519529
Self::propagate_loaded_state(self, loaded_asset_id, dep_id, sender);
520530
}
521531
}
522-
RecursiveDependencyLoadState::Failed => {
532+
RecursiveDependencyLoadState::Failed(ref error) => {
523533
for dep_id in dependants_waiting_on_rec_load {
524-
Self::propagate_failed_state(self, loaded_asset_id, dep_id);
534+
Self::propagate_failed_state(self, loaded_asset_id, dep_id, error);
525535
}
526536
}
527537
RecursiveDependencyLoadState::Loading | RecursiveDependencyLoadState::NotLoaded => {
@@ -570,11 +580,12 @@ impl AssetInfos {
570580
infos: &mut AssetInfos,
571581
failed_id: UntypedAssetId,
572582
waiting_id: UntypedAssetId,
583+
error: &Arc<AssetLoadError>,
573584
) {
574585
let dependants_waiting_on_rec_load = if let Some(info) = infos.get_mut(waiting_id) {
575586
info.loading_rec_dependencies.remove(&failed_id);
576587
info.failed_rec_dependencies.insert(failed_id);
577-
info.rec_dep_load_state = RecursiveDependencyLoadState::Failed;
588+
info.rec_dep_load_state = RecursiveDependencyLoadState::Failed(error.clone());
578589
Some(std::mem::take(
579590
&mut info.dependants_waiting_on_recursive_dep_load,
580591
))
@@ -584,7 +595,7 @@ impl AssetInfos {
584595

585596
if let Some(dependants_waiting_on_rec_load) = dependants_waiting_on_rec_load {
586597
for dep_id in dependants_waiting_on_rec_load {
587-
Self::propagate_failed_state(infos, waiting_id, dep_id);
598+
Self::propagate_failed_state(infos, waiting_id, dep_id, error);
588599
}
589600
}
590601
}
@@ -595,14 +606,15 @@ impl AssetInfos {
595606
return;
596607
}
597608

609+
let error = Arc::new(error);
598610
let (dependants_waiting_on_load, dependants_waiting_on_rec_load) = {
599611
let Some(info) = self.get_mut(failed_id) else {
600612
// The asset was already dropped.
601613
return;
602614
};
603-
info.load_state = LoadState::Failed(Box::new(error));
604-
info.dep_load_state = DependencyLoadState::Failed;
605-
info.rec_dep_load_state = RecursiveDependencyLoadState::Failed;
615+
info.load_state = LoadState::Failed(error.clone());
616+
info.dep_load_state = DependencyLoadState::Failed(error.clone());
617+
info.rec_dep_load_state = RecursiveDependencyLoadState::Failed(error.clone());
606618
(
607619
std::mem::take(&mut info.dependants_waiting_on_load),
608620
std::mem::take(&mut info.dependants_waiting_on_recursive_dep_load),
@@ -613,12 +625,15 @@ impl AssetInfos {
613625
if let Some(info) = self.get_mut(waiting_id) {
614626
info.loading_dependencies.remove(&failed_id);
615627
info.failed_dependencies.insert(failed_id);
616-
info.dep_load_state = DependencyLoadState::Failed;
628+
// don't overwrite DependencyLoadState if already failed to preserve first error
629+
if !(matches!(info.dep_load_state, DependencyLoadState::Failed(_))) {
630+
info.dep_load_state = DependencyLoadState::Failed(error.clone());
631+
}
617632
}
618633
}
619634

620635
for waiting_id in dependants_waiting_on_rec_load {
621-
Self::propagate_failed_state(self, failed_id, waiting_id);
636+
Self::propagate_failed_state(self, failed_id, waiting_id, &error);
622637
}
623638
}
624639

0 commit comments

Comments
 (0)