Skip to content

Commit 5475281

Browse files
add InteriorMutableWorld with resource access methods
1 parent 62da22d commit 5475281

File tree

2 files changed

+166
-2
lines changed

2 files changed

+166
-2
lines changed
Lines changed: 157 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,157 @@
1+
#![warn(unsafe_op_in_unsafe_fn)]
2+
3+
use super::{Mut, World};
4+
use crate::{
5+
change_detection::{MutUntyped, Ticks},
6+
component::ComponentId,
7+
system::Resource,
8+
};
9+
use bevy_ptr::Ptr;
10+
use bevy_ptr::UnsafeCellDeref;
11+
use std::any::TypeId;
12+
13+
/// Variant of the [`World`] where resource and component accesses takes a `&World`, and the responsibility to avoid
14+
/// aliasing violations are given to the caller instead of being checked at compile-time by rust's unique XOR shared rule.
15+
///
16+
/// ### Rationale
17+
/// In rust, having a `&mut World` means that there are absolutely no other references to the safe world alive at the same time,
18+
/// without exceptions. Not even unsafe code can change this.
19+
///
20+
/// But there are situations where careful shared mutable access through a type is possible and safe. For this, rust provides the [`UnsafeCell`](std::cell::UnsafeCell)
21+
/// escape hatch, which allows you to get a `*mut T` from a `&UnsafeCell<T>` and around which safe abstractions can be built.
22+
///
23+
/// Access to resources and components can be done uniquely using [`World::resource_mut`] and [`World::entity_mut`], and shared using [`World::resource`] and [`World::entity`].
24+
/// These methods use lifetimes to check at compile time that no aliasing rules are being broken.
25+
///
26+
/// This alone is not enough to implement bevy systems where multiple systems can access *disjoint* parts of the world concurrently. For this, bevy stores all values of
27+
/// resources and components (and [`ComponentTicks`](crate::component::ComponentTicks)) in [`UnsafeCell`](std::cell::UnsafeCell)s, and carefully validates disjoint access patterns using
28+
/// APIs like [`System::component_access`](crate::system::System::component_access).
29+
///
30+
/// A system then can be executed using [`System::run_unsafe`](crate::system::System::run_unsafe) with a `&World` and use methods with interior mutability to access resource values.
31+
/// access resource values.
32+
///
33+
/// ### Example Usage
34+
///
35+
/// [`InteriorMutableWorld`] can be used as a building block for writing APIs that safely allow disjoint access into the world.
36+
/// In the following example, the world is split into a resource access half and a component access half, where each one can
37+
/// safely hand out mutable references.
38+
///
39+
/// ```
40+
/// use bevy_ecs::world::World;
41+
/// use bevy_ecs::change_detection::Mut;
42+
/// use bevy_ecs::system::Resource;
43+
/// use bevy_ecs::world::interior_mutable_world::InteriorMutableWorld;
44+
///
45+
/// // INVARIANT: existance of this struct means that users of it are the only ones being able to access resources in the world
46+
/// struct OnlyResourceAccessWorld<'w>(InteriorMutableWorld<'w>);
47+
/// // INVARIANT: existance of this struct means that users of it are the only ones being able to access components in the world
48+
/// struct OnlyComponentAccessWorld<'w>(InteriorMutableWorld<'w>);
49+
///
50+
/// impl<'w> OnlyResourceAccessWorld<'w> {
51+
/// fn get_resource_mut<T: Resource>(&mut self) -> Option<Mut<'w, T>> {
52+
/// // SAFETY: resource access is allowed through this InteriorMutableWorld
53+
/// unsafe { self.0.get_resource_mut::<T>() }
54+
/// }
55+
/// }
56+
/// // impl<'w> OnlyComponentAccessWorld<'w> {
57+
/// // ...
58+
/// // }
59+
///
60+
/// // the two interior mutable worlds borrow from the `&mut World`, so it cannot be accessed while they are live
61+
/// fn split_world_access(world: &mut World) -> (OnlyResourceAccessWorld<'_>, OnlyComponentAccessWorld<'_>) {
62+
/// let resource_access = OnlyResourceAccessWorld(unsafe { world.as_interior_mutable() });
63+
/// let component_access = OnlyComponentAccessWorld(unsafe { world.as_interior_mutable() });
64+
/// (resource_access, component_access)
65+
/// }
66+
/// ```
67+
#[derive(Copy, Clone)]
68+
pub struct InteriorMutableWorld<'w>(&'w World);
69+
70+
impl<'w> InteriorMutableWorld<'w> {
71+
pub(crate) fn new(world: &'w World) -> Self {
72+
InteriorMutableWorld(world)
73+
}
74+
75+
/// Gets a reference to the [`&World`](crate::world::World) this [`InteriorMutableWorld`] belongs to.
76+
/// This can be used to call methods like [`World::read_change_tick`] which aren't exposed here but don't perform any accesses.
77+
///
78+
/// **Note**: You *must not* hand out a `&World` reference to arbitrary safe code when the [`InteriorMutableWorld`] is currently
79+
/// being used for mutable accesses.
80+
pub unsafe fn world(&self) -> &'w World {
81+
self.0
82+
}
83+
84+
/// Gets a reference to the resource of the given type if it exists
85+
#[inline]
86+
pub unsafe fn get_resource<R: Resource>(&self) -> Option<&'w R> {
87+
self.0.get_resource::<R>()
88+
}
89+
90+
/// Gets a pointer to the resource with the id [`ComponentId`] if it exists.
91+
/// The returned pointer must not be used to modify the resource, and must not be
92+
/// dereferenced after the borrow of the [`World`] ends.
93+
///
94+
/// **You should prefer to use the typed API [`InteriorMutableWorld::get_resource`] where possible and only
95+
/// use this in cases where the actual types are not known at compile time.**
96+
#[inline]
97+
pub unsafe fn get_resource_by_id(&self, component_id: ComponentId) -> Option<Ptr<'w>> {
98+
self.0.get_resource_by_id(component_id)
99+
}
100+
101+
/// Gets a mutable reference to the resource of the given type if it exists
102+
#[inline]
103+
pub unsafe fn get_resource_mut<R: Resource>(&self) -> Option<Mut<'w, R>> {
104+
let component_id = self.0.components.get_resource_id(TypeId::of::<R>())?;
105+
unsafe { self.0.get_resource_unchecked_mut_with_id(component_id) }
106+
}
107+
108+
/// Gets a pointer to the resource with the id [`ComponentId`] if it exists.
109+
/// The returned pointer may be used to modify the resource, as long as the mutable borrow
110+
/// of the [`InteriorMutableWorld`] is still valid.
111+
///
112+
/// **You should prefer to use the typed API [`InteriorMutableWorld::get_resource_mut`] where possible and only
113+
/// use this in cases where the actual types are not known at compile time.**
114+
#[inline]
115+
pub unsafe fn get_resource_mut_by_id(
116+
&self,
117+
component_id: ComponentId,
118+
) -> Option<MutUntyped<'w>> {
119+
let info = self.0.components.get_info(component_id)?;
120+
if !info.is_send_and_sync() {
121+
self.0.validate_non_send_access_untyped(info.name());
122+
}
123+
124+
let (ptr, ticks) = self.0.get_resource_with_ticks(component_id)?;
125+
126+
// SAFE: This function has exclusive access to the world so nothing aliases `ticks`.
127+
let ticks = Ticks {
128+
// SAFETY:
129+
// - index is in-bounds because the column is initialized and non-empty
130+
// - no other reference to the ticks of the same row can exist at the same time
131+
component_ticks: unsafe { ticks.deref_mut() },
132+
last_change_tick: self.0.last_change_tick(),
133+
change_tick: self.0.read_change_tick(),
134+
};
135+
136+
Some(MutUntyped {
137+
// SAFETY: This function has exclusive access to the world so nothing aliases `ptr`.
138+
value: unsafe { ptr.assert_unique() },
139+
ticks,
140+
})
141+
}
142+
143+
/// Gets a reference to the non-send resource of the given type, if it exists.
144+
/// Otherwise returns [None]
145+
#[inline]
146+
pub unsafe fn get_non_send_resource<R: 'static>(&self) -> Option<&R> {
147+
self.0.get_non_send_resource::<R>()
148+
}
149+
/// Gets a mutable reference to the non-send resource of the given type, if it exists.
150+
/// Otherwise returns [None]
151+
#[inline]
152+
pub unsafe fn get_non_send_resource_mut<R: 'static>(&mut self) -> Option<Mut<'w, R>> {
153+
let component_id = self.0.components.get_resource_id(TypeId::of::<R>())?;
154+
// SAFETY: safety requirement is deferred to the caller
155+
unsafe { self.0.get_non_send_unchecked_mut_with_id(component_id) }
156+
}
157+
}

crates/bevy_ecs/src/world/mod.rs

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
mod entity_ref;
2+
pub mod interior_mutable_world;
23
mod spawn_batch;
34
mod world_cell;
45

@@ -31,6 +32,8 @@ mod identifier;
3132

3233
pub use identifier::WorldId;
3334

35+
use self::interior_mutable_world::InteriorMutableWorld;
36+
3437
/// Stores and exposes operations on [entities](Entity), [components](Component), resources,
3538
/// and their associated metadata.
3639
///
@@ -103,6 +106,10 @@ impl World {
103106
self.id
104107
}
105108

109+
pub fn as_interior_mutable(&self) -> InteriorMutableWorld<'_> {
110+
InteriorMutableWorld::new(self)
111+
}
112+
106113
/// Retrieves this world's [Entities] collection
107114
#[inline]
108115
pub fn entities(&self) -> &Entities {
@@ -1429,7 +1436,7 @@ impl World {
14291436
}
14301437

14311438
impl World {
1432-
/// Gets a resource to the resource with the id [`ComponentId`] if it exists.
1439+
/// Gets a pointer to the resource with the id [`ComponentId`] if it exists.
14331440
/// The returned pointer must not be used to modify the resource, and must not be
14341441
/// dereferenced after the immutable borrow of the [`World`] ends.
14351442
///
@@ -1444,7 +1451,7 @@ impl World {
14441451
self.storages.resources.get(component_id)?.get_data()
14451452
}
14461453

1447-
/// Gets a resource to the resource with the id [`ComponentId`] if it exists.
1454+
/// Gets a pointer to the resource with the id [`ComponentId`] if it exists.
14481455
/// The returned pointer may be used to modify the resource, as long as the mutable borrow
14491456
/// of the [`World`] is still valid.
14501457
///

0 commit comments

Comments
 (0)