Skip to content

Commit

Permalink
Setup World to be optionally !Send
Browse files Browse the repository at this point in the history
  • Loading branch information
joshua-holmes committed Jan 25, 2025
1 parent 94e0e1f commit 439a04a
Show file tree
Hide file tree
Showing 6 changed files with 106 additions and 59 deletions.
7 changes: 3 additions & 4 deletions crates/bevy_ecs/src/query/fetch.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,7 @@ use crate::{
query::{Access, DebugCheckedUnwrap, FilteredAccess, WorldQuery},
storage::{ComponentSparseSet, Table, TableRow},
world::{
unsafe_world_cell::UnsafeWorldCell, EntityMut, EntityMutExcept, EntityRef, EntityRefExcept,
FilteredEntityMut, FilteredEntityRef, Mut, Ref, World,
unsafe_world_cell::UnsafeWorldCell, EntityMut, EntityMutExcept, EntityRef, EntityRefExcept, FilteredEntityMut, FilteredEntityRef, Mut, Ref, SendMarker, Sendability, World
},
};
use bevy_ptr::{ThinSlicePtr, UnsafeCellDeref};
Expand Down Expand Up @@ -275,9 +274,9 @@ use variadics_please::all_tuples;
label = "invalid `Query` data",
note = "if `{Self}` is a component type, try using `&{Self}` or `&mut {Self}`"
)]
pub unsafe trait QueryData: WorldQuery {
pub unsafe trait QueryData<S: Sendability = SendMarker>: WorldQuery<S> {
/// The read-only variant of this [`QueryData`], which satisfies the [`ReadOnlyQueryData`] trait.
type ReadOnly: ReadOnlyQueryData<State = <Self as WorldQuery>::State>;
type ReadOnly: ReadOnlyQueryData<State = <Self as WorldQuery<S>>::State>;
}

/// A [`QueryData`] that is read only.
Expand Down
24 changes: 12 additions & 12 deletions crates/bevy_ecs/src/query/filter.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ use crate::{
entity::Entity,
query::{DebugCheckedUnwrap, FilteredAccess, StorageSwitch, WorldQuery},
storage::{ComponentSparseSet, Table, TableRow},
world::{unsafe_world_cell::UnsafeWorldCell, World},
world::{unsafe_world_cell::UnsafeWorldCell, SendMarker, Sendability, World},
};
use bevy_ptr::{ThinSlicePtr, UnsafeCellDeref};
use core::{cell::UnsafeCell, marker::PhantomData};
Expand Down Expand Up @@ -80,7 +80,7 @@ use variadics_please::all_tuples;
label = "invalid `Query` filter",
note = "a `QueryFilter` typically uses a combination of `With<T>` and `Without<T>` statements"
)]
pub unsafe trait QueryFilter: WorldQuery {
pub unsafe trait QueryFilter<S: Sendability = SendMarker>: WorldQuery<S> {
/// Returns true if (and only if) this Filter relies strictly on archetypes to limit which
/// components are accessed by the Query.
///
Expand Down Expand Up @@ -139,7 +139,7 @@ pub struct With<T>(PhantomData<T>);
/// This is sound because `fetch` does not access any components.
/// `update_component_access` adds a `With` filter for `T`.
/// This is sound because `matches_component_set` returns whether the set contains the component.
unsafe impl<T: Component> WorldQuery for With<T> {
unsafe impl<T: Component, S: Sendability> WorldQuery<S> for With<T> {
type Item<'w> = ();
type Fetch<'w> = ();
type State = ComponentId;
Expand All @@ -150,7 +150,7 @@ unsafe impl<T: Component> WorldQuery for With<T> {

#[inline]
unsafe fn init_fetch(
_world: UnsafeWorldCell,
_world: UnsafeWorldCell<S>,
_state: &ComponentId,
_last_run: Tick,
_this_run: Tick,
Expand Down Expand Up @@ -189,7 +189,7 @@ unsafe impl<T: Component> WorldQuery for With<T> {
access.and_with(id);
}

fn init_state(world: &mut World) -> ComponentId {
fn init_state(world: &mut World<S>) -> ComponentId {
world.register_component::<T>()
}

Expand All @@ -206,7 +206,7 @@ unsafe impl<T: Component> WorldQuery for With<T> {
}

// SAFETY: WorldQuery impl performs no access at all
unsafe impl<T: Component> QueryFilter for With<T> {
unsafe impl<T: Component, S: Sendability> QueryFilter<S> for With<T> {
const IS_ARCHETYPAL: bool = true;

#[inline(always)]
Expand Down Expand Up @@ -250,7 +250,7 @@ pub struct Without<T>(PhantomData<T>);
/// This is sound because `fetch` does not access any components.
/// `update_component_access` adds a `Without` filter for `T`.
/// This is sound because `matches_component_set` returns whether the set does not contain the component.
unsafe impl<T: Component> WorldQuery for Without<T> {
unsafe impl<T: Component, S: Sendability> WorldQuery<S> for Without<T> {
type Item<'w> = ();
type Fetch<'w> = ();
type State = ComponentId;
Expand All @@ -261,7 +261,7 @@ unsafe impl<T: Component> WorldQuery for Without<T> {

#[inline]
unsafe fn init_fetch(
_world: UnsafeWorldCell,
_world: UnsafeWorldCell<S>,
_state: &ComponentId,
_last_run: Tick,
_this_run: Tick,
Expand Down Expand Up @@ -300,7 +300,7 @@ unsafe impl<T: Component> WorldQuery for Without<T> {
access.and_without(id);
}

fn init_state(world: &mut World) -> ComponentId {
fn init_state(world: &mut World<S>) -> ComponentId {
world.register_component::<T>()
}

Expand All @@ -317,7 +317,7 @@ unsafe impl<T: Component> WorldQuery for Without<T> {
}

// SAFETY: WorldQuery impl performs no access at all
unsafe impl<T: Component> QueryFilter for Without<T> {
unsafe impl<T: Component, S: Sendability> QueryFilter<S> for Without<T> {
const IS_ARCHETYPAL: bool = true;

#[inline(always)]
Expand Down Expand Up @@ -905,7 +905,7 @@ impl<T: Component> Clone for ChangedFetch<'_, T> {
/// This is sound because `update_component_access` add read access for that component and panics when appropriate.
/// `update_component_access` adds a `With` filter for a component.
/// This is sound because `matches_component_set` returns whether the set contains that component.
unsafe impl<T: Component> WorldQuery for Changed<T> {
unsafe impl<T: Component, S: Sendability> WorldQuery<S> for Changed<T> {
type Item<'w> = bool;
type Fetch<'w> = ChangedFetch<'w, T>;
type State = ComponentId;
Expand All @@ -920,7 +920,7 @@ unsafe impl<T: Component> WorldQuery for Changed<T> {

#[inline]
unsafe fn init_fetch<'w>(
world: UnsafeWorldCell<'w>,
world: UnsafeWorldCell<'w, S>,
&id: &ComponentId,
last_run: Tick,
this_run: Tick,
Expand Down
31 changes: 16 additions & 15 deletions crates/bevy_ecs/src/query/state.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,11 @@ use crate::{
WorldQuery,
},
storage::{SparseSetIndex, TableId},
world::{unsafe_world_cell::UnsafeWorldCell, World, WorldId},
world::{unsafe_world_cell::UnsafeWorldCell, SendMarker, Sendability, World, WorldId},
};

use alloc::vec::Vec;
use core::{fmt, mem::MaybeUninit, ptr};
use core::{fmt, marker::PhantomData, mem::MaybeUninit, ptr};
use fixedbitset::FixedBitSet;
use log::warn;
#[cfg(feature = "trace")]
Expand Down Expand Up @@ -63,7 +63,7 @@ pub(super) union StorageId {
// SAFETY NOTE:
// Do not add any new fields that use the `D` or `F` generic parameters as this may
// make `QueryState::as_transmuted_state` unsound if not done with care.
pub struct QueryState<D: QueryData, F: QueryFilter = ()> {
pub struct QueryState<D: QueryData<S>, F: QueryFilter<S> = (), S: Sendability = SendMarker> {
world_id: WorldId,
pub(crate) archetype_generation: ArchetypeGeneration,
/// Metadata about the [`Table`](crate::storage::Table)s matched by this query.
Expand All @@ -82,9 +82,10 @@ pub struct QueryState<D: QueryData, F: QueryFilter = ()> {
pub(crate) filter_state: F::State,
#[cfg(feature = "trace")]
par_iter_span: Span,
_send_marker: PhantomData<S>,
}

impl<D: QueryData, F: QueryFilter> fmt::Debug for QueryState<D, F> {
impl<D: QueryData<S>, F: QueryFilter<S>, S: Sendability> fmt::Debug for QueryState<D, F, S> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("QueryState")
.field("world_id", &self.world_id)
Expand All @@ -97,8 +98,8 @@ impl<D: QueryData, F: QueryFilter> fmt::Debug for QueryState<D, F> {
}
}

impl<D: QueryData, F: QueryFilter> FromWorld for QueryState<D, F> {
fn from_world(world: &mut World) -> Self {
impl<D: QueryData<S>, F: QueryFilter<S>, S: Sendability> FromWorld<S> for QueryState<D, F, S> {
fn from_world(world: &mut World<S>) -> Self {
world.query_filtered()
}
}
Expand Down Expand Up @@ -156,9 +157,9 @@ impl<D: QueryData, F: QueryFilter> QueryState<D, F> {
}
}

impl<D: QueryData, F: QueryFilter> QueryState<D, F> {
impl<D: QueryData<S>, F: QueryFilter<S>, S: Sendability> QueryState<D, F, S> {
/// Creates a new [`QueryState`] from a given [`World`] and inherits the result of `world.id()`.
pub fn new(world: &mut World) -> Self {
pub fn new(world: &mut World<S>) -> Self {
let mut state = Self::new_uninitialized(world);
state.update_archetypes(world);
state
Expand All @@ -168,15 +169,15 @@ impl<D: QueryData, F: QueryFilter> QueryState<D, F> {
///
/// This function may fail if, for example,
/// the components that make up this query have not been registered into the world.
pub fn try_new(world: &World) -> Option<Self> {
pub fn try_new(world: &World<S>) -> Option<Self> {
let mut state = Self::try_new_uninitialized(world)?;
state.update_archetypes(world);
Some(state)
}

/// Identical to `new`, but it populates the provided `access` with the matched results.
pub(crate) fn new_with_access(
world: &mut World,
world: &mut World<S>,
access: &mut Access<ArchetypeComponentId>,
) -> Self {
let mut state = Self::new_uninitialized(world);
Expand Down Expand Up @@ -212,7 +213,7 @@ impl<D: QueryData, F: QueryFilter> QueryState<D, F> {
///
/// `new_archetype` and its variants must be called on all of the World's archetypes before the
/// state can return valid query results.
fn new_uninitialized(world: &mut World) -> Self {
fn new_uninitialized(world: &mut World<S>) -> Self {
let fetch_state = D::init_state(world);
let filter_state = F::init_state(world);
Self::from_states_uninitialized(world, fetch_state, filter_state)
Expand All @@ -222,7 +223,7 @@ impl<D: QueryData, F: QueryFilter> QueryState<D, F> {
///
/// `new_archetype` and its variants must be called on all of the World's archetypes before the
/// state can return valid query results.
fn try_new_uninitialized(world: &World) -> Option<Self> {
fn try_new_uninitialized(world: &World<S>) -> Option<Self> {
let fetch_state = D::get_state(world.components())?;
let filter_state = F::get_state(world.components())?;
Some(Self::from_states_uninitialized(
Expand All @@ -237,7 +238,7 @@ impl<D: QueryData, F: QueryFilter> QueryState<D, F> {
/// `new_archetype` and its variants must be called on all of the World's archetypes before the
/// state can return valid query results.
fn from_states_uninitialized(
world: &World,
world: &World<S>,
fetch_state: <D as WorldQuery>::State,
filter_state: <F as WorldQuery>::State,
) -> Self {
Expand Down Expand Up @@ -402,7 +403,7 @@ impl<D: QueryData, F: QueryFilter> QueryState<D, F> {
///
/// If `world` does not match the one used to call `QueryState::new` for this instance.
#[inline]
pub fn update_archetypes(&mut self, world: &World) {
pub fn update_archetypes(&mut self, world: &World<S>) {
self.update_archetypes_unsafe_world_cell(world.as_unsafe_world_cell_readonly());
}

Expand All @@ -420,7 +421,7 @@ impl<D: QueryData, F: QueryFilter> QueryState<D, F> {
/// # Panics
///
/// If `world` does not match the one used to call `QueryState::new` for this instance.
pub fn update_archetypes_unsafe_world_cell(&mut self, world: UnsafeWorldCell) {
pub fn update_archetypes_unsafe_world_cell(&mut self, world: UnsafeWorldCell<S>) {
self.validate_world(world.id());
if self.component_access.required.is_empty() {
let archetypes = world.archetypes();
Expand Down
8 changes: 4 additions & 4 deletions crates/bevy_ecs/src/query/world_query.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ use crate::{
entity::Entity,
query::FilteredAccess,
storage::{Table, TableRow},
world::{unsafe_world_cell::UnsafeWorldCell, World},
world::{unsafe_world_cell::UnsafeWorldCell, SendMarker, Sendability, World},
};
use variadics_please::all_tuples;

Expand Down Expand Up @@ -38,7 +38,7 @@ use variadics_please::all_tuples;
/// [`update_component_access`]: Self::update_component_access
/// [`QueryData`]: crate::query::QueryData
/// [`QueryFilter`]: crate::query::QueryFilter
pub unsafe trait WorldQuery {
pub unsafe trait WorldQuery<S: Sendability = SendMarker> {
/// The item returned by this [`WorldQuery`]
/// For `QueryData` this will be the item returned by the query.
/// For `QueryFilter` this will be either `()`, or a `bool` indicating whether the entity should be included
Expand Down Expand Up @@ -69,7 +69,7 @@ pub unsafe trait WorldQuery {
/// - `world` must have the **right** to access any access registered in `update_component_access`.
/// - There must not be simultaneous resource access conflicting with readonly resource access registered in [`WorldQuery::update_component_access`].
unsafe fn init_fetch<'w>(
world: UnsafeWorldCell<'w>,
world: UnsafeWorldCell<'w, S>,
state: &Self::State,
last_run: Tick,
this_run: Tick,
Expand Down Expand Up @@ -138,7 +138,7 @@ pub unsafe trait WorldQuery {
fn update_component_access(state: &Self::State, access: &mut FilteredAccess<ComponentId>);

/// Creates and initializes a [`State`](WorldQuery::State) for this [`WorldQuery`] type.
fn init_state(world: &mut World) -> Self::State;
fn init_state(world: &mut World<S>) -> Self::State;

/// Attempts to initialize a [`State`](WorldQuery::State) for this [`WorldQuery`] type using read-only
/// access to [`Components`].
Expand Down
Loading

0 comments on commit 439a04a

Please sign in to comment.