bevy_ecs/storage/resource.rs
1use crate::archetype::ArchetypeComponentId;
2use crate::change_detection::{MutUntyped, TicksMut};
3use crate::component::{ComponentId, ComponentTicks, Components, Tick, TickCells};
4use crate::storage::{blob_vec::BlobVec, SparseSet};
5use bevy_ptr::{OwningPtr, Ptr, UnsafeCellDeref};
6use std::{cell::UnsafeCell, mem::ManuallyDrop, thread::ThreadId};
7
8/// The type-erased backing storage and metadata for a single resource within a [`World`].
9///
10/// If `SEND` is false, values of this type will panic if dropped from a different thread.
11///
12/// [`World`]: crate::world::World
13pub struct ResourceData<const SEND: bool> {
14 data: ManuallyDrop<BlobVec>,
15 added_ticks: UnsafeCell<Tick>,
16 changed_ticks: UnsafeCell<Tick>,
17 type_name: String,
18 id: ArchetypeComponentId,
19 origin_thread_id: Option<ThreadId>,
20}
21
22impl<const SEND: bool> Drop for ResourceData<SEND> {
23 fn drop(&mut self) {
24 if self.is_present() {
25 // If this thread is already panicking, panicking again will cause
26 // the entire process to abort. In this case we choose to avoid
27 // dropping or checking this altogether and just leak the column.
28 if std::thread::panicking() {
29 return;
30 }
31 self.validate_access();
32 }
33 // SAFETY: Drop is only called once upon dropping the ResourceData
34 // and is inaccessible after this as the parent ResourceData has
35 // been dropped. The validate_access call above will check that the
36 // data is dropped on the thread it was inserted from.
37 unsafe {
38 ManuallyDrop::drop(&mut self.data);
39 }
40 }
41}
42
43impl<const SEND: bool> ResourceData<SEND> {
44 /// The only row in the underlying `BlobVec`.
45 const ROW: usize = 0;
46
47 /// Validates the access to `!Send` resources is only done on the thread they were created from.
48 ///
49 /// # Panics
50 /// If `SEND` is false, this will panic if called from a different thread than the one it was inserted from.
51 #[inline]
52 fn validate_access(&self) {
53 if SEND {
54 return;
55 }
56 if self.origin_thread_id != Some(std::thread::current().id()) {
57 // Panic in tests, as testing for aborting is nearly impossible
58 panic!(
59 "Attempted to access or drop non-send resource {} from thread {:?} on a thread {:?}. This is not allowed. Aborting.",
60 self.type_name,
61 self.origin_thread_id,
62 std::thread::current().id()
63 );
64 }
65 }
66
67 /// Returns true if the resource is populated.
68 #[inline]
69 pub fn is_present(&self) -> bool {
70 !self.data.is_empty()
71 }
72
73 /// Gets the [`ArchetypeComponentId`] for the resource.
74 #[inline]
75 pub fn id(&self) -> ArchetypeComponentId {
76 self.id
77 }
78
79 /// Returns a reference to the resource, if it exists.
80 ///
81 /// # Panics
82 /// If `SEND` is false, this will panic if a value is present and is not accessed from the
83 /// original thread it was inserted from.
84 #[inline]
85 pub fn get_data(&self) -> Option<Ptr<'_>> {
86 self.is_present().then(|| {
87 self.validate_access();
88 // SAFETY: We've already checked if a value is present, and there should only be one.
89 unsafe { self.data.get_unchecked(Self::ROW) }
90 })
91 }
92
93 /// Returns a reference to the resource's change ticks, if it exists.
94 #[inline]
95 pub fn get_ticks(&self) -> Option<ComponentTicks> {
96 // SAFETY: This is being fetched through a read-only reference to Self, so no other mutable references
97 // to the ticks can exist.
98 unsafe {
99 self.is_present().then(|| ComponentTicks {
100 added: self.added_ticks.read(),
101 changed: self.changed_ticks.read(),
102 })
103 }
104 }
105
106 /// Returns references to the resource and its change ticks, if it exists.
107 ///
108 /// # Panics
109 /// If `SEND` is false, this will panic if a value is present and is not accessed from the
110 /// original thread it was inserted in.
111 #[inline]
112 pub(crate) fn get_with_ticks(&self) -> Option<(Ptr<'_>, TickCells<'_>)> {
113 self.is_present().then(|| {
114 self.validate_access();
115 (
116 // SAFETY: We've already checked if a value is present, and there should only be one.
117 unsafe { self.data.get_unchecked(Self::ROW) },
118 TickCells {
119 added: &self.added_ticks,
120 changed: &self.changed_ticks,
121 },
122 )
123 })
124 }
125
126 /// Returns a mutable reference to the resource, if it exists.
127 ///
128 /// # Panics
129 /// If `SEND` is false, this will panic if a value is present and is not accessed from the
130 /// original thread it was inserted in.
131 pub(crate) fn get_mut(&mut self, last_run: Tick, this_run: Tick) -> Option<MutUntyped<'_>> {
132 let (ptr, ticks) = self.get_with_ticks()?;
133 Some(MutUntyped {
134 // SAFETY: We have exclusive access to the underlying storage.
135 value: unsafe { ptr.assert_unique() },
136 // SAFETY: We have exclusive access to the underlying storage.
137 ticks: unsafe { TicksMut::from_tick_cells(ticks, last_run, this_run) },
138 })
139 }
140
141 /// Inserts a value into the resource. If a value is already present
142 /// it will be replaced.
143 ///
144 /// # Panics
145 /// If `SEND` is false, this will panic if a value is present and is not replaced from
146 /// the original thread it was inserted in.
147 ///
148 /// # Safety
149 /// - `value` must be valid for the underlying type for the resource.
150 #[inline]
151 pub(crate) unsafe fn insert(&mut self, value: OwningPtr<'_>, change_tick: Tick) {
152 if self.is_present() {
153 self.validate_access();
154 // SAFETY: The caller ensures that the provided value is valid for the underlying type and
155 // is properly initialized. We've ensured that a value is already present and previously
156 // initialized.
157 unsafe {
158 self.data.replace_unchecked(Self::ROW, value);
159 }
160 } else {
161 if !SEND {
162 self.origin_thread_id = Some(std::thread::current().id());
163 }
164 self.data.push(value);
165 *self.added_ticks.deref_mut() = change_tick;
166 }
167 *self.changed_ticks.deref_mut() = change_tick;
168 }
169
170 /// Inserts a value into the resource with a pre-existing change tick. If a
171 /// value is already present it will be replaced.
172 ///
173 /// # Panics
174 /// If `SEND` is false, this will panic if a value is present and is not replaced from
175 /// the original thread it was inserted in.
176 ///
177 /// # Safety
178 /// - `value` must be valid for the underlying type for the resource.
179 #[inline]
180 pub(crate) unsafe fn insert_with_ticks(
181 &mut self,
182 value: OwningPtr<'_>,
183 change_ticks: ComponentTicks,
184 ) {
185 if self.is_present() {
186 self.validate_access();
187 // SAFETY: The caller ensures that the provided value is valid for the underlying type and
188 // is properly initialized. We've ensured that a value is already present and previously
189 // initialized.
190 unsafe {
191 self.data.replace_unchecked(Self::ROW, value);
192 }
193 } else {
194 if !SEND {
195 self.origin_thread_id = Some(std::thread::current().id());
196 }
197 self.data.push(value);
198 }
199 *self.added_ticks.deref_mut() = change_ticks.added;
200 *self.changed_ticks.deref_mut() = change_ticks.changed;
201 }
202
203 /// Removes a value from the resource, if present.
204 ///
205 /// # Panics
206 /// If `SEND` is false, this will panic if a value is present and is not removed from the
207 /// original thread it was inserted from.
208 #[inline]
209 #[must_use = "The returned pointer to the removed component should be used or dropped"]
210 pub(crate) fn remove(&mut self) -> Option<(OwningPtr<'_>, ComponentTicks)> {
211 if !self.is_present() {
212 return None;
213 }
214 if !SEND {
215 self.validate_access();
216 }
217 // SAFETY: We've already validated that the row is present.
218 let res = unsafe { self.data.swap_remove_and_forget_unchecked(Self::ROW) };
219 // SAFETY: This function is being called through an exclusive mutable reference to Self, which
220 // makes it sound to read these ticks.
221 unsafe {
222 Some((
223 res,
224 ComponentTicks {
225 added: self.added_ticks.read(),
226 changed: self.changed_ticks.read(),
227 },
228 ))
229 }
230 }
231
232 /// Removes a value from the resource, if present, and drops it.
233 ///
234 /// # Panics
235 /// If `SEND` is false, this will panic if a value is present and is not
236 /// accessed from the original thread it was inserted in.
237 #[inline]
238 pub(crate) fn remove_and_drop(&mut self) {
239 if self.is_present() {
240 self.validate_access();
241 self.data.clear();
242 }
243 }
244
245 pub(crate) fn check_change_ticks(&mut self, change_tick: Tick) {
246 self.added_ticks.get_mut().check_tick(change_tick);
247 self.changed_ticks.get_mut().check_tick(change_tick);
248 }
249}
250
251/// The backing store for all [`Resource`]s stored in the [`World`].
252///
253/// [`Resource`]: crate::system::Resource
254/// [`World`]: crate::world::World
255#[derive(Default)]
256pub struct Resources<const SEND: bool> {
257 resources: SparseSet<ComponentId, ResourceData<SEND>>,
258}
259
260impl<const SEND: bool> Resources<SEND> {
261 /// The total number of resources stored in the [`World`]
262 ///
263 /// [`World`]: crate::world::World
264 #[inline]
265 pub fn len(&self) -> usize {
266 self.resources.len()
267 }
268
269 /// Iterate over all resources that have been initialized, i.e. given a [`ComponentId`]
270 pub fn iter(&self) -> impl Iterator<Item = (ComponentId, &ResourceData<SEND>)> {
271 self.resources.iter().map(|(id, data)| (*id, data))
272 }
273
274 /// Returns true if there are no resources stored in the [`World`],
275 /// false otherwise.
276 ///
277 /// [`World`]: crate::world::World
278 #[inline]
279 pub fn is_empty(&self) -> bool {
280 self.resources.is_empty()
281 }
282
283 /// Gets read-only access to a resource, if it exists.
284 #[inline]
285 pub fn get(&self, component_id: ComponentId) -> Option<&ResourceData<SEND>> {
286 self.resources.get(component_id)
287 }
288
289 /// Clears all resources.
290 #[inline]
291 pub fn clear(&mut self) {
292 self.resources.clear();
293 }
294
295 /// Gets mutable access to a resource, if it exists.
296 #[inline]
297 pub(crate) fn get_mut(&mut self, component_id: ComponentId) -> Option<&mut ResourceData<SEND>> {
298 self.resources.get_mut(component_id)
299 }
300
301 /// Fetches or initializes a new resource and returns back its underlying column.
302 ///
303 /// # Panics
304 /// Will panic if `component_id` is not valid for the provided `components`
305 /// If `SEND` is true, this will panic if `component_id`'s `ComponentInfo` is not registered as being `Send` + `Sync`.
306 pub(crate) fn initialize_with(
307 &mut self,
308 component_id: ComponentId,
309 components: &Components,
310 f: impl FnOnce() -> ArchetypeComponentId,
311 ) -> &mut ResourceData<SEND> {
312 self.resources.get_or_insert_with(component_id, || {
313 let component_info = components.get_info(component_id).unwrap();
314 if SEND {
315 assert!(
316 component_info.is_send_and_sync(),
317 "Send + Sync resource {} initialized as non_send. It may have been inserted via World::insert_non_send_resource by accident. Try using World::insert_resource instead.",
318 component_info.name(),
319 );
320 }
321 // SAFETY: component_info.drop() is valid for the types that will be inserted.
322 let data = unsafe {
323 BlobVec::new(
324 component_info.layout(),
325 component_info.drop(),
326 1
327 )
328 };
329 ResourceData {
330 data: ManuallyDrop::new(data),
331 added_ticks: UnsafeCell::new(Tick::new(0)),
332 changed_ticks: UnsafeCell::new(Tick::new(0)),
333 type_name: String::from(component_info.name()),
334 id: f(),
335 origin_thread_id: None,
336 }
337 })
338 }
339
340 pub(crate) fn check_change_ticks(&mut self, change_tick: Tick) {
341 for info in self.resources.values_mut() {
342 info.check_change_ticks(change_tick);
343 }
344 }
345}