bevy/crates/bevy_ecs/src/world/identifier.rs

93 lines
2.8 KiB
Rust
Raw Normal View History

use crate::{
storage::SparseSetIndex,
system::{ReadOnlySystemParam, SystemParam},
};
use std::sync::atomic::{AtomicUsize, Ordering};
#[derive(Copy, Clone, PartialEq, Eq, Debug, Hash)]
// We use usize here because that is the largest `Atomic` we want to require
/// A unique identifier for a [`super::World`].
// Note that this *is* used by external crates as well as for internal safety checks
pub struct WorldId(usize);
/// The next [`WorldId`].
static MAX_WORLD_ID: AtomicUsize = AtomicUsize::new(0);
impl WorldId {
/// Create a new, unique [`WorldId`]. Returns [`None`] if the supply of unique
/// [`WorldId`]s has been exhausted
///
/// Please note that the [`WorldId`]s created from this method are unique across
/// time - if a given [`WorldId`] is [`Drop`]ped its value still cannot be reused
pub fn new() -> Option<Self> {
MAX_WORLD_ID
// We use `Relaxed` here since this atomic only needs to be consistent with itself
.fetch_update(Ordering::Relaxed, Ordering::Relaxed, |val| {
val.checked_add(1)
})
.map(WorldId)
.ok()
}
}
// SAFETY: Has read-only access to shared World metadata
unsafe impl ReadOnlySystemParam for WorldId {}
// SAFETY: A World's ID is immutable and fetching it from the World does not borrow anything
unsafe impl SystemParam for WorldId {
type State = ();
type Item<'world, 'state> = WorldId;
fn init_state(_: &mut super::World, _: &mut crate::system::SystemMeta) -> Self::State {}
unsafe fn get_param<'world, 'state>(
_: &'state mut Self::State,
_: &crate::system::SystemMeta,
world: &'world super::World,
_: u32,
) -> Self::Item<'world, 'state> {
world.id
}
}
impl SparseSetIndex for WorldId {
#[inline]
fn sparse_set_index(&self) -> usize {
self.0
}
fn get_sparse_set_index(value: usize) -> Self {
Self(value)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn world_ids_unique() {
let ids = std::iter::repeat_with(WorldId::new)
.take(50)
.map(Option::unwrap)
.collect::<Vec<_>>();
for (i, &id1) in ids.iter().enumerate() {
// For the first element, i is 0 - so skip 1
for &id2 in ids.iter().skip(i + 1) {
assert_ne!(id1, id2, "WorldIds should not repeat");
}
}
}
// We cannot use this test as-is, as it causes other tests to panic due to using the same atomic variable.
// #[test]
// #[should_panic]
// fn panic_on_overflow() {
// MAX_WORLD_ID.store(usize::MAX - 50, Ordering::Relaxed);
// std::iter::repeat_with(WorldId::new)
// .take(500)
// .for_each(|_| ());
// }
}