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}