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}