bevy_ecs/storage/
resource.rs

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