Skip to content

Commit 9667281

Browse files
committed
replace UnsafeCell with Cell in ComponentTicks and fix missing drop on component overwrite
1 parent 90beff9 commit 9667281

File tree

15 files changed

+221
-161
lines changed

15 files changed

+221
-161
lines changed

crates/bevy_ecs/src/bundle.rs

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -140,20 +140,20 @@ impl BundleInfo {
140140
// bundle_info.component_ids are also in "bundle order"
141141
let mut bundle_component = 0;
142142
bundle.get_components(|component_ptr| {
143-
// SAFE: component_id was initialized by get_dynamic_bundle_info
144143
let component_id = *self.component_ids.get_unchecked(bundle_component);
145-
let component_status = bundle_status.get_unchecked(bundle_component);
146144
match self.storage_types[bundle_component] {
147145
StorageType::Table => {
148146
let column = table.get_column_mut(component_id).unwrap();
149-
column.set_data_unchecked(table_row, component_ptr);
150-
let column_status = column.get_ticks_unchecked_mut(table_row);
151-
match component_status {
147+
match bundle_status.get_unchecked(bundle_component) {
152148
ComponentStatus::Added => {
153-
*column_status = ComponentTicks::new(change_tick);
149+
column.initialize(
150+
table_row,
151+
component_ptr,
152+
ComponentTicks::new(change_tick),
153+
);
154154
}
155155
ComponentStatus::Mutated => {
156-
column_status.set_changed(change_tick);
156+
column.replace(table_row, component_ptr, change_tick);
157157
}
158158
}
159159
}

crates/bevy_ecs/src/component/mod.rs

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ use crate::storage::SparseSetIndex;
66
use std::{
77
alloc::Layout,
88
any::{Any, TypeId},
9+
cell::Cell,
910
collections::hash_map::Entry,
1011
};
1112
use thiserror::Error;
@@ -306,10 +307,10 @@ impl Components {
306307
}
307308
}
308309

309-
#[derive(Copy, Clone, Debug)]
310+
#[derive(Clone, Debug)]
310311
pub struct ComponentTicks {
311312
pub(crate) added: u32,
312-
pub(crate) changed: u32,
313+
pub(crate) changed: Cell<u32>,
313314
}
314315

315316
impl ComponentTicks {
@@ -326,7 +327,7 @@ impl ComponentTicks {
326327

327328
#[inline]
328329
pub fn is_changed(&self, last_change_tick: u32, change_tick: u32) -> bool {
329-
let component_delta = change_tick.wrapping_sub(self.changed);
330+
let component_delta = change_tick.wrapping_sub(self.changed.get());
330331
let system_delta = change_tick.wrapping_sub(last_change_tick);
331332

332333
component_delta < system_delta
@@ -335,13 +336,13 @@ impl ComponentTicks {
335336
pub(crate) fn new(change_tick: u32) -> Self {
336337
Self {
337338
added: change_tick,
338-
changed: change_tick,
339+
changed: Cell::new(change_tick),
339340
}
340341
}
341342

342343
pub(crate) fn check_ticks(&mut self, change_tick: u32) {
343344
check_tick(&mut self.added, change_tick);
344-
check_tick(&mut self.changed, change_tick);
345+
check_tick(self.changed.get_mut(), change_tick);
345346
}
346347

347348
/// Manually sets the change tick.
@@ -357,8 +358,8 @@ impl ComponentTicks {
357358
/// component_ticks.set_changed(world.read_change_tick());
358359
/// ```
359360
#[inline]
360-
pub fn set_changed(&mut self, change_tick: u32) {
361-
self.changed = change_tick;
361+
pub fn set_changed(&self, change_tick: u32) {
362+
self.changed.set(change_tick);
362363
}
363364
}
364365

crates/bevy_ecs/src/lib.rs

Lines changed: 51 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,13 +47,34 @@ mod tests {
4747
};
4848
use bevy_tasks::TaskPool;
4949
use parking_lot::Mutex;
50-
use std::{any::TypeId, sync::Arc};
50+
use std::{
51+
any::TypeId,
52+
sync::{
53+
atomic::{AtomicUsize, Ordering},
54+
Arc,
55+
},
56+
};
5157

5258
#[derive(Debug, PartialEq, Eq)]
5359
struct A(usize);
5460
struct B(usize);
5561
struct C;
5662

63+
#[derive(Clone, Debug)]
64+
struct DropCk(Arc<AtomicUsize>);
65+
impl DropCk {
66+
fn new_pair() -> (Self, Arc<AtomicUsize>) {
67+
let atomic = Arc::new(AtomicUsize::new(0));
68+
(DropCk(atomic.clone()), atomic)
69+
}
70+
}
71+
72+
impl Drop for DropCk {
73+
fn drop(&mut self) {
74+
self.0.as_ref().fetch_add(1, Ordering::Relaxed);
75+
}
76+
}
77+
5778
#[test]
5879
fn random_access() {
5980
let mut world = World::new();
@@ -1174,4 +1195,33 @@ mod tests {
11741195
});
11751196
assert_eq!(*world.get_resource::<i32>().unwrap(), 1);
11761197
}
1198+
1199+
#[test]
1200+
fn insert_overwrite_drop() {
1201+
let (dropck1, dropped1) = DropCk::new_pair();
1202+
let (dropck2, dropped2) = DropCk::new_pair();
1203+
let mut world = World::default();
1204+
world.spawn().insert(dropck1).insert(dropck2);
1205+
assert_eq!(dropped1.load(Ordering::Relaxed), 1);
1206+
assert_eq!(dropped2.load(Ordering::Relaxed), 0);
1207+
drop(world);
1208+
assert_eq!(dropped1.load(Ordering::Relaxed), 1);
1209+
assert_eq!(dropped2.load(Ordering::Relaxed), 1);
1210+
}
1211+
1212+
#[test]
1213+
fn insert_overwrite_drop_sparse() {
1214+
let (dropck1, dropped1) = DropCk::new_pair();
1215+
let (dropck2, dropped2) = DropCk::new_pair();
1216+
let mut world = World::default();
1217+
world
1218+
.register_component(ComponentDescriptor::new::<DropCk>(StorageType::SparseSet))
1219+
.unwrap();
1220+
world.spawn().insert(dropck1).insert(dropck2);
1221+
assert_eq!(dropped1.load(Ordering::Relaxed), 1);
1222+
assert_eq!(dropped2.load(Ordering::Relaxed), 0);
1223+
drop(world);
1224+
assert_eq!(dropped1.load(Ordering::Relaxed), 1);
1225+
assert_eq!(dropped2.load(Ordering::Relaxed), 1);
1226+
}
11771227
}

crates/bevy_ecs/src/query/fetch.rs

Lines changed: 15 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -342,7 +342,7 @@ impl<'w, T: Component> Fetch<'w> for ReadFetch<T> {
342342
let column = tables[archetype.table_id()]
343343
.get_column(state.component_id)
344344
.unwrap();
345-
self.table_components = column.get_ptr().cast::<T>();
345+
self.table_components = column.get_data_ptr().cast::<T>();
346346
}
347347
StorageType::SparseSet => self.entities = archetype.entities().as_ptr(),
348348
}
@@ -353,7 +353,7 @@ impl<'w, T: Component> Fetch<'w> for ReadFetch<T> {
353353
self.table_components = table
354354
.get_column(state.component_id)
355355
.unwrap()
356-
.get_ptr()
356+
.get_data_ptr()
357357
.cast::<T>();
358358
}
359359

@@ -386,7 +386,7 @@ impl<T: Component> WorldQuery for &mut T {
386386
pub struct WriteFetch<T> {
387387
storage_type: StorageType,
388388
table_components: NonNull<T>,
389-
table_ticks: *mut ComponentTicks,
389+
table_ticks: *const ComponentTicks,
390390
entities: *const Entity,
391391
entity_table_rows: *const usize,
392392
sparse_set: *const ComponentSparseSet,
@@ -508,8 +508,8 @@ impl<'w, T: Component> Fetch<'w> for WriteFetch<T> {
508508
let column = tables[archetype.table_id()]
509509
.get_column(state.component_id)
510510
.unwrap();
511-
self.table_components = column.get_ptr().cast::<T>();
512-
self.table_ticks = column.get_ticks_mut_ptr();
511+
self.table_components = column.get_data_ptr().cast::<T>();
512+
self.table_ticks = column.get_ticks_ptr();
513513
}
514514
StorageType::SparseSet => self.entities = archetype.entities().as_ptr(),
515515
}
@@ -518,8 +518,8 @@ impl<'w, T: Component> Fetch<'w> for WriteFetch<T> {
518518
#[inline]
519519
unsafe fn set_table(&mut self, state: &Self::State, table: &Table) {
520520
let column = table.get_column(state.component_id).unwrap();
521-
self.table_components = column.get_ptr().cast::<T>();
522-
self.table_ticks = column.get_ticks_mut_ptr();
521+
self.table_components = column.get_data_ptr().cast::<T>();
522+
self.table_ticks = column.get_ticks_ptr();
523523
}
524524

525525
#[inline]
@@ -529,7 +529,7 @@ impl<'w, T: Component> Fetch<'w> for WriteFetch<T> {
529529
let table_row = *self.entity_table_rows.add(archetype_index);
530530
Mut {
531531
value: &mut *self.table_components.as_ptr().add(table_row),
532-
component_ticks: &mut *self.table_ticks.add(table_row),
532+
component_ticks: &*self.table_ticks.add(table_row),
533533
change_tick: self.change_tick,
534534
last_change_tick: self.last_change_tick,
535535
}
@@ -540,7 +540,7 @@ impl<'w, T: Component> Fetch<'w> for WriteFetch<T> {
540540
(*self.sparse_set).get_with_ticks(entity).unwrap();
541541
Mut {
542542
value: &mut *component.cast::<T>(),
543-
component_ticks: &mut *component_ticks,
543+
component_ticks: &*component_ticks,
544544
change_tick: self.change_tick,
545545
last_change_tick: self.last_change_tick,
546546
}
@@ -552,7 +552,7 @@ impl<'w, T: Component> Fetch<'w> for WriteFetch<T> {
552552
unsafe fn table_fetch(&mut self, table_row: usize) -> Self::Item {
553553
Mut {
554554
value: &mut *self.table_components.as_ptr().add(table_row),
555-
component_ticks: &mut *self.table_ticks.add(table_row),
555+
component_ticks: &*self.table_ticks.add(table_row),
556556
change_tick: self.change_tick,
557557
last_change_tick: self.last_change_tick,
558558
}
@@ -853,7 +853,7 @@ impl<'w, T: Component> Fetch<'w> for ChangeTrackersFetch<T> {
853853
let column = tables[archetype.table_id()]
854854
.get_column(state.component_id)
855855
.unwrap();
856-
self.table_ticks = column.get_ticks_mut_ptr().cast::<ComponentTicks>();
856+
self.table_ticks = column.get_ticks_ptr().cast::<ComponentTicks>();
857857
}
858858
StorageType::SparseSet => self.entities = archetype.entities().as_ptr(),
859859
}
@@ -864,7 +864,7 @@ impl<'w, T: Component> Fetch<'w> for ChangeTrackersFetch<T> {
864864
self.table_ticks = table
865865
.get_column(state.component_id)
866866
.unwrap()
867-
.get_ticks_mut_ptr()
867+
.get_ticks_ptr()
868868
.cast::<ComponentTicks>();
869869
}
870870

@@ -874,7 +874,7 @@ impl<'w, T: Component> Fetch<'w> for ChangeTrackersFetch<T> {
874874
StorageType::Table => {
875875
let table_row = *self.entity_table_rows.add(archetype_index);
876876
ChangeTrackers {
877-
component_ticks: *self.table_ticks.add(table_row),
877+
component_ticks: (&*self.table_ticks.add(table_row)).clone(),
878878
marker: PhantomData,
879879
last_change_tick: self.last_change_tick,
880880
change_tick: self.change_tick,
@@ -883,7 +883,7 @@ impl<'w, T: Component> Fetch<'w> for ChangeTrackersFetch<T> {
883883
StorageType::SparseSet => {
884884
let entity = *self.entities.add(archetype_index);
885885
ChangeTrackers {
886-
component_ticks: *(*self.sparse_set).get_ticks(entity).unwrap(),
886+
component_ticks: (&*self.sparse_set).get_ticks(entity).cloned().unwrap(),
887887
marker: PhantomData,
888888
last_change_tick: self.last_change_tick,
889889
change_tick: self.change_tick,
@@ -895,7 +895,7 @@ impl<'w, T: Component> Fetch<'w> for ChangeTrackersFetch<T> {
895895
#[inline]
896896
unsafe fn table_fetch(&mut self, table_row: usize) -> Self::Item {
897897
ChangeTrackers {
898-
component_ticks: *self.table_ticks.add(table_row),
898+
component_ticks: (&*self.table_ticks.add(table_row)).clone(),
899899
marker: PhantomData,
900900
last_change_tick: self.last_change_tick,
901901
change_tick: self.change_tick,

crates/bevy_ecs/src/query/filter.rs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -561,7 +561,7 @@ macro_rules! impl_tick_filter {
561561
$(#[$fetch_meta])*
562562
pub struct $fetch_name<T> {
563563
storage_type: StorageType,
564-
table_ticks: *mut ComponentTicks,
564+
table_ticks: *const ComponentTicks,
565565
entity_table_rows: *const usize,
566566
marker: PhantomData<T>,
567567
entities: *const Entity,
@@ -630,7 +630,7 @@ macro_rules! impl_tick_filter {
630630
unsafe fn init(world: &World, state: &Self::State, last_change_tick: u32, change_tick: u32) -> Self {
631631
let mut value = Self {
632632
storage_type: state.storage_type,
633-
table_ticks: ptr::null_mut::<ComponentTicks>(),
633+
table_ticks: ptr::null::<ComponentTicks>(),
634634
entities: ptr::null::<Entity>(),
635635
entity_table_rows: ptr::null::<usize>(),
636636
sparse_set: ptr::null::<ComponentSparseSet>(),
@@ -655,7 +655,7 @@ macro_rules! impl_tick_filter {
655655
unsafe fn set_table(&mut self, state: &Self::State, table: &Table) {
656656
self.table_ticks = table
657657
.get_column(state.component_id).unwrap()
658-
.get_ticks_mut_ptr();
658+
.get_ticks_ptr();
659659
}
660660

661661
unsafe fn set_archetype(&mut self, state: &Self::State, archetype: &Archetype, tables: &Tables) {
@@ -665,7 +665,7 @@ macro_rules! impl_tick_filter {
665665
let table = &tables[archetype.table_id()];
666666
self.table_ticks = table
667667
.get_column(state.component_id).unwrap()
668-
.get_ticks_mut_ptr();
668+
.get_ticks_ptr();
669669
}
670670
StorageType::SparseSet => self.entities = archetype.entities().as_ptr(),
671671
}
@@ -683,7 +683,7 @@ macro_rules! impl_tick_filter {
683683
}
684684
StorageType::SparseSet => {
685685
let entity = *self.entities.add(archetype_index);
686-
let ticks = (*(*self.sparse_set).get_ticks(entity).unwrap());
686+
let ticks = (&*self.sparse_set).get_ticks(entity).cloned().unwrap();
687687
$is_detected(&ticks, self.last_change_tick, self.change_tick)
688688
}
689689
}

crates/bevy_ecs/src/reflect.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -116,7 +116,7 @@ impl<C: Component + Reflect + FromWorld> FromType<C> for ReflectComponent {
116116
/// Unique borrow of a Reflected component
117117
pub struct ReflectMut<'a> {
118118
pub(crate) value: &'a mut dyn Reflect,
119-
pub(crate) component_ticks: &'a mut ComponentTicks,
119+
pub(crate) component_ticks: &'a ComponentTicks,
120120
pub(crate) last_change_tick: u32,
121121
pub(crate) change_tick: u32,
122122
}

crates/bevy_ecs/src/schedule/stage.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2050,7 +2050,7 @@ mod tests {
20502050
assert!(time_since_last_check <= MAX_DELTA);
20512051
let time_since_last_check = tracker
20522052
.change_tick
2053-
.wrapping_sub(tracker.component_ticks.changed);
2053+
.wrapping_sub(tracker.component_ticks.changed.get());
20542054
assert!(time_since_last_check <= MAX_DELTA);
20552055
}
20562056
let change_tick = world.change_tick.get_mut();

crates/bevy_ecs/src/storage/blob_vec.rs

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -89,12 +89,22 @@ impl BlobVec {
8989
/// `index` must be in bounds
9090
/// Allows aliased mutable access to `index`'s data. Caller must ensure this does not happen
9191
#[inline]
92-
pub unsafe fn set_unchecked(&self, index: usize, value: *mut u8) {
92+
pub unsafe fn initialize_unchecked(&mut self, index: usize, value: *mut u8) {
9393
debug_assert!(index < self.len());
9494
let ptr = self.get_unchecked(index);
9595
std::ptr::copy_nonoverlapping(value, ptr, self.item_layout.size());
9696
}
9797

98+
/// # Safety
99+
/// index must be in-bounds
100+
///
101+
pub unsafe fn replace_unchecked(&mut self, index: usize, value: *mut u8) {
102+
debug_assert!(index < self.len());
103+
let ptr = self.get_unchecked(index);
104+
(self.drop)(ptr);
105+
std::ptr::copy_nonoverlapping(value, ptr, self.item_layout.size());
106+
}
107+
98108
/// increases the length by one (and grows the vec if needed) with uninitialized memory and
99109
/// returns the index
100110
///
@@ -267,7 +277,7 @@ mod tests {
267277
/// `blob_vec` must have a layout that matches Layout::new::<T>()
268278
unsafe fn push<T>(blob_vec: &mut BlobVec, mut value: T) {
269279
let index = blob_vec.push_uninit();
270-
blob_vec.set_unchecked(index, (&mut value as *mut T).cast::<u8>());
280+
blob_vec.initialize_unchecked(index, (&mut value as *mut T).cast::<u8>());
271281
std::mem::forget(value);
272282
}
273283

0 commit comments

Comments
 (0)