Skip to content

Commit b7010aa

Browse files
committed
Prevent adding or removing processed asset sources after processing has started.
1 parent a7b1ce4 commit b7010aa

File tree

3 files changed

+89
-11
lines changed

3 files changed

+89
-11
lines changed

crates/bevy_asset/src/io/source.rs

Lines changed: 74 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,12 @@ use alloc::{
88
sync::Arc,
99
};
1010
use atomicow::CowArc;
11-
use bevy_platform::collections::{hash_map::Entry, HashMap};
11+
use bevy_platform::collections::{
12+
hash_map::{Entry, EntryRef},
13+
HashMap,
14+
};
1215
use core::{fmt::Display, hash::Hash, time::Duration};
13-
use std::sync::RwLock;
16+
use std::sync::{PoisonError, RwLock};
1417
use thiserror::Error;
1518
use tracing::warn;
1619

@@ -600,18 +603,41 @@ impl AssetSources {
600603
source_builder: &mut AssetSourceBuilder,
601604
) -> Result<(), AddSourceError> {
602605
let name = name.into();
606+
603607
let entry = match self.sources.entry(name) {
604608
Entry::Occupied(entry) => return Err(AddSourceError::NameInUse(entry.key().clone())),
605609
Entry::Vacant(entry) => entry,
606610
};
607611

608612
let name = entry.key().clone();
609-
entry.insert(source_builder.build(
613+
// Build the asset source. This does mean that we may build the source and then **not** add
614+
// it (due to checks later on), but we need to build the source to know if it's processed.
615+
let source = source_builder.build(
610616
AssetSourceId::Name(name),
611617
self.watch,
612618
self.watch_processed,
613619
self.processing_state.clone(),
614-
));
620+
);
621+
622+
// Hold the processor started lock until after we insert the source (so that the processor
623+
// doesn't start between when we check and when we insert)
624+
let started_lock;
625+
// If the source wants to be processed, and the processor has already started, return an
626+
// error.
627+
// TODO: Remove this once the processor can handle newly added sources.
628+
if source.should_process()
629+
&& let Some(processing_state) = self.processing_state.as_ref()
630+
{
631+
started_lock = processing_state
632+
.started
633+
.read()
634+
.unwrap_or_else(PoisonError::into_inner);
635+
if *started_lock {
636+
return Err(AddSourceError::SourceIsProcessed);
637+
}
638+
}
639+
640+
entry.insert(source);
615641
Ok(())
616642
}
617643

@@ -621,13 +647,36 @@ impl AssetSources {
621647
pub fn remove(
622648
&mut self,
623649
name: impl Into<CowArc<'static, str>>,
624-
) -> Result<(), MissingAssetSourceError> {
650+
) -> Result<(), RemoveSourceError> {
625651
let name = name.into();
626-
if self.sources.remove(&name).is_none() {
627-
return Err(MissingAssetSourceError(AssetSourceId::Name(
628-
name.clone_owned(),
629-
)));
652+
653+
// Use entry_ref so we don't need to clone the name just to remove the entry.
654+
let entry = match self.sources.entry_ref(&name) {
655+
EntryRef::Vacant(_) => {
656+
return Err(RemoveSourceError::MissingSource(name.clone_owned()))
657+
}
658+
EntryRef::Occupied(entry) => entry,
659+
};
660+
661+
// Hold the processor started lock until after we remove the source (so that the processor
662+
// doesn't start between when we check and when we remove)
663+
let started_lock;
664+
// If the source wants to be processed, and the processor has already started, return an
665+
// error.
666+
// TODO: Remove this once the processor can handle removed sources.
667+
if entry.get().should_process()
668+
&& let Some(processing_state) = self.processing_state.as_ref()
669+
{
670+
started_lock = processing_state
671+
.started
672+
.read()
673+
.unwrap_or_else(PoisonError::into_inner);
674+
if *started_lock {
675+
return Err(RemoveSourceError::SourceIsProcessed);
676+
}
630677
}
678+
679+
entry.remove();
631680
Ok(())
632681
}
633682

@@ -653,11 +702,27 @@ impl AssetSources {
653702
/// An error when attempting to add a new asset source.
654703
#[derive(Error, Debug, Clone, PartialEq, Eq)]
655704
pub enum AddSourceError {
705+
/// The provided asset source is a "processed" source, and processing has already started, where adding is currently unsupported.
706+
// TODO: Remove this once it's supported.
707+
#[error("The provided asset source is processed, but the asset processor has already started. This is currently unsupported - processed sources can only be added at startup")]
708+
SourceIsProcessed,
656709
/// An asset source with the given name already exists.
657710
#[error("Asset Source '{0}' already exists")]
658711
NameInUse(CowArc<'static, str>),
659712
}
660713

714+
/// An error when attempting to remove an existing asset source.
715+
#[derive(Error, Debug, Clone, PartialEq, Eq)]
716+
pub enum RemoveSourceError {
717+
/// The requested asset source is a "processed" source, and processing has already started, where removing is currently unsupported.
718+
// TODO: Remove this once it's supported.
719+
#[error("The asset source being removed is processed, but the asset processor has already started. This is currently unsupported - processed sources can only be removed at startup")]
720+
SourceIsProcessed,
721+
/// The requested source is missing, so it cannot be removed.
722+
#[error("Asset Source '{0}' does not exist")]
723+
MissingSource(CowArc<'static, str>),
724+
}
725+
661726
/// An error returned when an [`AssetSource`] does not exist for a given id.
662727
#[derive(Error, Debug, Clone, PartialEq, Eq)]
663728
#[error("Asset Source '{0}' does not exist")]

crates/bevy_asset/src/processor/mod.rs

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -121,6 +121,12 @@ pub struct AssetProcessorData {
121121

122122
/// The current state of processing, including the overall state and the state of all assets.
123123
pub(crate) struct ProcessingState {
124+
/// A bool indicating whether the processor has started or not.
125+
///
126+
/// This is different from `state` since `state` is async, and we only assign to it once, so we
127+
/// should ~never block on it.
128+
// TODO: Remove this once the processor can process new asset sources.
129+
pub(crate) started: RwLock<bool>,
124130
/// The overall state of processing.
125131
state: async_lock::RwLock<ProcessorState>,
126132
/// The channel to broadcast when the processor has completed initialization.
@@ -237,6 +243,12 @@ impl AssetProcessor {
237243
let start_time = std::time::Instant::now();
238244
debug!("Processing Assets");
239245

246+
*processor
247+
.data
248+
.processing_state
249+
.started
250+
.write()
251+
.unwrap_or_else(PoisonError::into_inner) = true;
240252
let sources = processor
241253
.sources()
242254
.read()
@@ -1303,6 +1315,7 @@ impl ProcessingState {
13031315
finished_sender.set_overflow(true);
13041316

13051317
Self {
1318+
started: Default::default(),
13061319
state: async_lock::RwLock::new(ProcessorState::Initializing),
13071320
initialized_sender,
13081321
initialized_receiver,

crates/bevy_asset/src/server/mod.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ use crate::{
66
io::{
77
AddSourceError, AssetReaderError, AssetSource, AssetSourceBuilder, AssetSourceEvent,
88
AssetSourceId, AssetSources, AssetWriterError, ErasedAssetReader, MissingAssetSourceError,
9-
MissingAssetWriterError, MissingProcessedAssetReaderError, Reader,
9+
MissingAssetWriterError, MissingProcessedAssetReaderError, Reader, RemoveSourceError,
1010
},
1111
loader::{AssetLoader, ErasedAssetLoader, LoadContext, LoadedAsset},
1212
meta::{
@@ -211,7 +211,7 @@ impl AssetServer {
211211
pub fn remove_source(
212212
&self,
213213
name: impl Into<CowArc<'static, str>>,
214-
) -> Result<(), MissingAssetSourceError> {
214+
) -> Result<(), RemoveSourceError> {
215215
self.data
216216
.sources
217217
.write()

0 commit comments

Comments
 (0)