Skip to content

Commit

Permalink
Add a method to run read-only systems using &World (#8849)
Browse files Browse the repository at this point in the history
# Objective

Resolves #7558.

Systems that are known to never modify the world implement the trait
`ReadOnlySystem`. This is a perfect place to add a safe API for running
a system with a shared reference to a World.

---

## Changelog

- Added the trait method `ReadOnlySystem::run_readonly`, which allows a
system to be run using `&World`.
  • Loading branch information
JoJoJet authored Jun 15, 2023
1 parent 5291110 commit 8ec8149
Show file tree
Hide file tree
Showing 2 changed files with 33 additions and 3 deletions.
2 changes: 1 addition & 1 deletion crates/bevy_ecs/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ pub mod prelude {
adapter as system_adapter,
adapter::{dbg, error, ignore, info, unwrap, warn},
Commands, Deferred, In, IntoSystem, Local, NonSend, NonSendMut, ParallelCommands,
ParamSet, Query, Res, ResMut, Resource, System, SystemParamFunction,
ParamSet, Query, ReadOnlySystem, Res, ResMut, Resource, System, SystemParamFunction,
},
world::{FromWorld, World},
};
Expand Down
34 changes: 32 additions & 2 deletions crates/bevy_ecs/src/system/system.rs
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,12 @@ pub trait System: Send + Sync + 'static {
/// point before this one, with the same exact [`World`]. If `update_archetype_component_access`
/// panics (or otherwise does not return for any reason), this method must not be called.
unsafe fn run_unsafe(&mut self, input: Self::In, world: UnsafeWorldCell) -> Self::Out;

/// Runs the system with the given input in the world.
///
/// For [read-only](ReadOnlySystem) systems, see [`run_readonly`], which can be called using `&World`.
///
/// [`run_readonly`]: ReadOnlySystem::run_readonly
fn run(&mut self, input: Self::In, world: &mut World) -> Self::Out {
let world = world.as_unsafe_world_cell();
self.update_archetype_component_access(world);
Expand All @@ -61,29 +66,36 @@ pub trait System: Send + Sync + 'static {
// - `update_archetype_component_access` has been called.
unsafe { self.run_unsafe(input, world) }
}

/// Applies any [`Deferred`](crate::system::Deferred) system parameters (or other system buffers) of this system to the world.
///
/// This is where [`Commands`](crate::system::Commands) get applied.
fn apply_deferred(&mut self, world: &mut World);

/// Initialize the system.
fn initialize(&mut self, _world: &mut World);

/// Update the system's archetype component [`Access`].
///
/// ## Note for implementors
/// `world` may only be used to access metadata. This can be done in safe code
/// via functions such as [`UnsafeWorldCell::archetypes`].
fn update_archetype_component_access(&mut self, world: UnsafeWorldCell);

/// Checks any [`Tick`]s stored on this system and wraps their value if they get too old.
///
/// This method must be called periodically to ensure that change detection behaves correctly.
/// When using bevy's default configuration, this will be called for you as needed.
fn check_change_tick(&mut self, change_tick: Tick);

/// Returns the system's default [system sets](crate::schedule::SystemSet).
fn default_system_sets(&self) -> Vec<Box<dyn crate::schedule::SystemSet>> {
Vec::new()
}

/// Gets the tick indicating the last time this system ran.
fn get_last_run(&self) -> Tick;

/// Overwrites the tick indicating the last time this system ran.
///
/// # Warning
Expand All @@ -96,12 +108,30 @@ pub trait System: Send + Sync + 'static {
/// [`System`] types that do not modify the [`World`] when run.
/// This is implemented for any systems whose parameters all implement [`ReadOnlySystemParam`].
///
/// Note that systems which perform [deferred](System::apply_deferred) mutations (such as with [`Commands`])
/// may implement this trait.
///
/// [`ReadOnlySystemParam`]: crate::system::ReadOnlySystemParam
/// [`Commands`]: crate::system::Commands
///
/// # Safety
///
/// This must only be implemented for system types which do not mutate the `World`.
pub unsafe trait ReadOnlySystem: System {}
/// This must only be implemented for system types which do not mutate the `World`
/// when [`System::run_unsafe`] is called.
pub unsafe trait ReadOnlySystem: System {
/// Runs this system with the given input in the world.
///
/// Unlike [`System::run`], this can be called with a shared reference to the world,
/// since this system is known not to modify the world.
fn run_readonly(&mut self, input: Self::In, world: &World) -> Self::Out {
let world = world.as_unsafe_world_cell_readonly();
self.update_archetype_component_access(world);
// SAFETY:
// - We have read-only access to the entire world.
// - `update_archetype_component_access` has been called.
unsafe { self.run_unsafe(input, world) }
}
}

/// A convenience type alias for a boxed [`System`] trait object.
pub type BoxedSystem<In = (), Out = ()> = Box<dyn System<In = In, Out = Out>>;
Expand Down

0 comments on commit 8ec8149

Please sign in to comment.