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