Skip to content

Commit 5ba2b9a

Browse files
committed
Unique WorldId (#2827)
# Objective Fixes these issues: - `WorldId`s currently aren't necessarily unique - I want to guarantee that they're unique to safeguard my librarified version of #2805 - There probably hasn't been a collision yet, but they could technically collide - `SystemId` isn't used for anything - It's no longer used now that `Locals` are stored within the `System`. - `bevy_ecs` depends on rand ## Solution - Instead of randomly generating `WorldId`s, just use an incrementing atomic counter, panicing on overflow. - Remove `SystemId` - We do need to allow Locals for exclusive systems at some point, but exclusive systems couldn't access their own `SystemId` anyway. - Now that these don't depend on rand, move it to a dev-dependency ## Todo Determine if `WorldId` should be `u32` based instead
1 parent 9919933 commit 5ba2b9a

File tree

9 files changed

+72
-66
lines changed

9 files changed

+72
-66
lines changed

crates/bevy_core/src/time/fixed_timestep.rs

+1-5
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ use bevy_ecs::{
44
component::ComponentId,
55
query::Access,
66
schedule::ShouldRun,
7-
system::{ConfigurableSystem, IntoSystem, Local, Res, ResMut, System, SystemId},
7+
system::{ConfigurableSystem, IntoSystem, Local, Res, ResMut, System},
88
world::World,
99
};
1010
use bevy_utils::HashMap;
@@ -148,10 +148,6 @@ impl System for FixedTimestep {
148148
Cow::Borrowed(std::any::type_name::<FixedTimestep>())
149149
}
150150

151-
fn id(&self) -> SystemId {
152-
self.internal_system.id()
153-
}
154-
155151
fn new_archetype(&mut self, archetype: &Archetype) {
156152
self.internal_system.new_archetype(archetype);
157153
}

crates/bevy_ecs/Cargo.toml

+1-1
Original file line numberDiff line numberDiff line change
@@ -24,11 +24,11 @@ fixedbitset = "0.4"
2424
fxhash = "0.2"
2525
thiserror = "1.0"
2626
downcast-rs = "1.2"
27-
rand = "0.8"
2827
serde = "1"
2928

3029
[dev-dependencies]
3130
parking_lot = "0.11"
31+
rand = "0.8"
3232

3333
[[example]]
3434
name = "events"

crates/bevy_ecs/src/schedule/run_criteria.rs

+1-7
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ use crate::{
33
component::ComponentId,
44
query::Access,
55
schedule::{BoxedRunCriteriaLabel, GraphNode, RunCriteriaLabel},
6-
system::{BoxedSystem, IntoSystem, System, SystemId},
6+
system::{BoxedSystem, IntoSystem, System},
77
world::World,
88
};
99
use std::borrow::Cow;
@@ -398,7 +398,6 @@ where
398398

399399
pub struct RunOnce {
400400
ran: bool,
401-
system_id: SystemId,
402401
archetype_component_access: Access<ArchetypeComponentId>,
403402
component_access: Access<ComponentId>,
404403
}
@@ -407,7 +406,6 @@ impl Default for RunOnce {
407406
fn default() -> Self {
408407
Self {
409408
ran: false,
410-
system_id: SystemId::new(),
411409
archetype_component_access: Default::default(),
412410
component_access: Default::default(),
413411
}
@@ -422,10 +420,6 @@ impl System for RunOnce {
422420
Cow::Borrowed(std::any::type_name::<RunOnce>())
423421
}
424422

425-
fn id(&self) -> SystemId {
426-
self.system_id
427-
}
428-
429423
fn new_archetype(&mut self, _archetype: &Archetype) {}
430424

431425
fn component_access(&self) -> &Access<ComponentId> {

crates/bevy_ecs/src/system/exclusive_system.rs

+1-13
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,13 @@
11
use crate::{
22
archetype::ArchetypeGeneration,
3-
system::{check_system_change_tick, BoxedSystem, IntoSystem, SystemId},
3+
system::{check_system_change_tick, BoxedSystem, IntoSystem},
44
world::World,
55
};
66
use std::borrow::Cow;
77

88
pub trait ExclusiveSystem: Send + Sync + 'static {
99
fn name(&self) -> Cow<'static, str>;
1010

11-
fn id(&self) -> SystemId;
12-
1311
fn run(&mut self, world: &mut World);
1412

1513
fn initialize(&mut self, world: &mut World);
@@ -20,7 +18,6 @@ pub trait ExclusiveSystem: Send + Sync + 'static {
2018
pub struct ExclusiveSystemFn {
2119
func: Box<dyn FnMut(&mut World) + Send + Sync + 'static>,
2220
name: Cow<'static, str>,
23-
id: SystemId,
2421
last_change_tick: u32,
2522
}
2623

@@ -29,10 +26,6 @@ impl ExclusiveSystem for ExclusiveSystemFn {
2926
self.name.clone()
3027
}
3128

32-
fn id(&self) -> SystemId {
33-
self.id
34-
}
35-
3629
fn run(&mut self, world: &mut World) {
3730
// The previous value is saved in case this exclusive system is run by another exclusive
3831
// system
@@ -67,7 +60,6 @@ where
6760
ExclusiveSystemFn {
6861
func: Box::new(self),
6962
name: core::any::type_name::<F>().into(),
70-
id: SystemId::new(),
7163
last_change_tick: 0,
7264
}
7365
}
@@ -83,10 +75,6 @@ impl ExclusiveSystem for ExclusiveSystemCoerced {
8375
self.system.name()
8476
}
8577

86-
fn id(&self) -> SystemId {
87-
self.system.id()
88-
}
89-
9078
fn run(&mut self, world: &mut World) {
9179
let archetypes = world.archetypes();
9280
let new_generation = archetypes.generation();

crates/bevy_ecs/src/system/function_system.rs

+2-9
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,8 @@ use crate::{
33
component::ComponentId,
44
query::{Access, FilteredAccessSet},
55
system::{
6-
check_system_change_tick, ReadOnlySystemParamFetch, System, SystemId, SystemParam,
7-
SystemParamFetch, SystemParamState,
6+
check_system_change_tick, ReadOnlySystemParamFetch, System, SystemParam, SystemParamFetch,
7+
SystemParamState,
88
},
99
world::{World, WorldId},
1010
};
@@ -13,7 +13,6 @@ use std::{borrow::Cow, marker::PhantomData};
1313

1414
/// The metadata of a [`System`].
1515
pub struct SystemMeta {
16-
pub(crate) id: SystemId,
1716
pub(crate) name: Cow<'static, str>,
1817
pub(crate) component_access_set: FilteredAccessSet<ComponentId>,
1918
pub(crate) archetype_component_access: Access<ArchetypeComponentId>,
@@ -30,7 +29,6 @@ impl SystemMeta {
3029
archetype_component_access: Access::default(),
3130
component_access_set: FilteredAccessSet::default(),
3231
is_send: true,
33-
id: SystemId::new(),
3432
last_change_tick: 0,
3533
}
3634
}
@@ -340,11 +338,6 @@ where
340338
self.system_meta.name.clone()
341339
}
342340

343-
#[inline]
344-
fn id(&self) -> SystemId {
345-
self.system_meta.id
346-
}
347-
348341
#[inline]
349342
fn new_archetype(&mut self, archetype: &Archetype) {
350343
let param_state = self.param_state.as_mut().unwrap();

crates/bevy_ecs/src/system/system.rs

-14
Original file line numberDiff line numberDiff line change
@@ -8,18 +8,6 @@ use crate::{
88
};
99
use std::borrow::Cow;
1010

11-
/// A [`System`] identifier.
12-
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
13-
pub struct SystemId(pub usize);
14-
15-
impl SystemId {
16-
/// Creates a new random `SystemId`.
17-
#[allow(clippy::new_without_default)]
18-
pub fn new() -> Self {
19-
SystemId(rand::random::<usize>())
20-
}
21-
}
22-
2311
/// An ECS system that can be added to a [Schedule](crate::schedule::Schedule)
2412
///
2513
/// Systems are functions with all arguments implementing [SystemParam](crate::system::SystemParam).
@@ -38,8 +26,6 @@ pub trait System: Send + Sync + 'static {
3826
type Out;
3927
/// Returns the system's name.
4028
fn name(&self) -> Cow<'static, str>;
41-
/// Returns the system's [`SystemId`].
42-
fn id(&self) -> SystemId;
4329
/// Register a new archetype for this system.
4430
fn new_archetype(&mut self, archetype: &Archetype);
4531
/// Returns the system's component [`Access`].

crates/bevy_ecs/src/system/system_chaining.rs

+1-7
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ use crate::{
22
archetype::{Archetype, ArchetypeComponentId},
33
component::ComponentId,
44
query::Access,
5-
system::{IntoSystem, System, SystemId},
5+
system::{IntoSystem, System},
66
world::World,
77
};
88
use std::borrow::Cow;
@@ -48,7 +48,6 @@ pub struct ChainSystem<SystemA, SystemB> {
4848
system_a: SystemA,
4949
system_b: SystemB,
5050
name: Cow<'static, str>,
51-
id: SystemId,
5251
component_access: Access<ComponentId>,
5352
archetype_component_access: Access<ArchetypeComponentId>,
5453
}
@@ -61,10 +60,6 @@ impl<SystemA: System, SystemB: System<In = SystemA::Out>> System for ChainSystem
6160
self.name.clone()
6261
}
6362

64-
fn id(&self) -> SystemId {
65-
self.id
66-
}
67-
6863
fn new_archetype(&mut self, archetype: &Archetype) {
6964
self.system_a.new_archetype(archetype);
7065
self.system_b.new_archetype(archetype);
@@ -143,7 +138,6 @@ where
143138
system_b,
144139
archetype_component_access: Default::default(),
145140
component_access: Default::default(),
146-
id: SystemId::new(),
147141
}
148142
}
149143
}
+56
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
use std::sync::atomic::{AtomicUsize, Ordering};
2+
3+
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
4+
// We use usize here because that is the largest `Atomic` we want to require
5+
/// A unique identifier for a [`super::World`].
6+
// Note that this *is* used by external crates as well as for internal safety checks
7+
pub struct WorldId(usize);
8+
9+
/// The next [`WorldId`].
10+
static MAX_WORLD_ID: AtomicUsize = AtomicUsize::new(0);
11+
12+
impl WorldId {
13+
/// Create a new, unique [`WorldId`]. Returns [`None`] if the supply of unique
14+
/// [`WorldId`]s has been exhausted
15+
///
16+
/// Please note that the [`WorldId`]s created from this method are unique across
17+
/// time - if a given [`WorldId`] is [`Drop`]ped its value still cannot be reused
18+
pub fn new() -> Option<Self> {
19+
MAX_WORLD_ID
20+
// We use `Relaxed` here since this atomic only needs to be consistent with itself
21+
.fetch_update(Ordering::Relaxed, Ordering::Relaxed, |val| {
22+
val.checked_add(1)
23+
})
24+
.map(WorldId)
25+
.ok()
26+
}
27+
}
28+
29+
#[cfg(test)]
30+
mod tests {
31+
use super::*;
32+
33+
#[test]
34+
fn world_ids_unique() {
35+
let ids = std::iter::repeat_with(WorldId::new)
36+
.take(50)
37+
.map(Option::unwrap)
38+
.collect::<Vec<_>>();
39+
for (i, &id1) in ids.iter().enumerate() {
40+
// For the first element, i is 0 - so skip 1
41+
for &id2 in ids.iter().skip(i + 1) {
42+
assert_ne!(id1, id2, "WorldIds should not repeat");
43+
}
44+
}
45+
}
46+
47+
// We cannot use this test as-is, as it causes other tests to panic due to using the same atomic variable.
48+
// #[test]
49+
// #[should_panic]
50+
// fn panic_on_overflow() {
51+
// MAX_WORLD_ID.store(usize::MAX - 50, Ordering::Relaxed);
52+
// std::iter::repeat_with(WorldId::new)
53+
// .take(500)
54+
// .for_each(|_| ());
55+
// }
56+
}

crates/bevy_ecs/src/world/mod.rs

+9-10
Original file line numberDiff line numberDiff line change
@@ -25,15 +25,9 @@ use std::{
2525
sync::atomic::{AtomicU32, Ordering},
2626
};
2727

28-
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
29-
pub struct WorldId(u64);
30-
31-
impl Default for WorldId {
32-
fn default() -> Self {
33-
WorldId(rand::random())
34-
}
35-
}
28+
mod identifier;
3629

30+
pub use identifier::WorldId;
3731
/// Stores and exposes operations on [entities](Entity), [components](Component), resources,
3832
/// and their associated metadata.
3933
///
@@ -94,7 +88,7 @@ pub struct World {
9488
impl Default for World {
9589
fn default() -> Self {
9690
Self {
97-
id: Default::default(),
91+
id: WorldId::new().expect("More `bevy` `World`s have been created than is supported"),
9892
entities: Default::default(),
9993
components: Default::default(),
10094
archetypes: Default::default(),
@@ -113,12 +107,17 @@ impl Default for World {
113107

114108
impl World {
115109
/// Creates a new empty [World]
110+
/// # Panics
111+
///
112+
/// If [`usize::MAX`] [`World`]s have been created.
113+
/// This guarantee allows System Parameters to safely uniquely identify a [`World`],
114+
/// since its [`WorldId`] is unique
116115
#[inline]
117116
pub fn new() -> World {
118117
World::default()
119118
}
120119

121-
/// Retrieves this world's unique ID
120+
/// Retrieves this [`World`]'s unique ID
122121
#[inline]
123122
pub fn id(&self) -> WorldId {
124123
self.id

0 commit comments

Comments
 (0)