Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Merged by Bors] - bevy_ecs: Use 32-bit entity ID cursor on platforms without AtomicI64 #4452

Closed
Closed
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
51 changes: 33 additions & 18 deletions crates/bevy_ecs/src/entity/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,11 +29,20 @@ pub use self::serde::*;
pub use map_entities::*;

use crate::{archetype::ArchetypeId, storage::SparseSetIndex};
use std::{
convert::TryFrom,
fmt, mem,
sync::atomic::{AtomicI64, Ordering},
};
use std::{convert::TryFrom, fmt, mem, sync::atomic::Ordering};

#[cfg(target_has_atomic = "64")]
use std::sync::atomic::AtomicI64 as AtomicIdCursor;
#[cfg(target_has_atomic = "64")]
type IdCursor = i64;

/// Most modern platforms support 64-bit atomics, but some less-common platforms
/// do not. This fallback allows compilation using a 32-bit cursor instead, with
/// the caveat that some conversions may fail (and panic) at runtime.
#[cfg(not(target_has_atomic = "64"))]
use std::sync::atomic::AtomicIsize as AtomicIdCursor;
#[cfg(not(target_has_atomic = "64"))]
type IdCursor = isize;

/// Lightweight unique ID of an entity.
///
Expand Down Expand Up @@ -237,7 +246,7 @@ pub struct Entities {
///
/// Once `flush()` is done, `free_cursor` will equal `pending.len()`.
pending: Vec<u32>,
free_cursor: AtomicI64,
free_cursor: AtomicIdCursor,
/// Stores the number of free entities for [`len`](Entities::len)
len: u32,
}
Expand All @@ -250,8 +259,12 @@ impl Entities {
// Use one atomic subtract to grab a range of new IDs. The range might be
// entirely nonnegative, meaning all IDs come from the freelist, or entirely
// negative, meaning they are all new IDs to allocate, or a mix of both.
let range_end = self.free_cursor.fetch_sub(count as i64, Ordering::Relaxed);
let range_start = range_end - count as i64;
let range_end = self
.free_cursor
// Unwrap: these conversions can only fail on platforms that don't support 64-bit atomics
// and use AtomicIsize instead (see note on `IdCursor`).
.fetch_sub(IdCursor::try_from(count).unwrap(), Ordering::Relaxed);
let range_start = range_end - IdCursor::try_from(count).unwrap();

let freelist_range = range_start.max(0) as usize..range_end.max(0) as usize;

Expand All @@ -268,7 +281,7 @@ impl Entities {
// In this example, we truncate the end to 0, leaving us with `-3..0`.
// Then we negate these values to indicate how far beyond the end of `meta.end()`
// to go, yielding `meta.len()+0 .. meta.len()+3`.
let base = self.meta.len() as i64;
let base = self.meta.len() as IdCursor;

let new_id_end = u32::try_from(base - range_start).expect("too many entities");

Expand Down Expand Up @@ -305,7 +318,7 @@ impl Entities {
// and farther beyond `meta.len()`.
Entity {
generation: 0,
id: u32::try_from(self.meta.len() as i64 - n).expect("too many entities"),
id: u32::try_from(self.meta.len() as IdCursor - n).expect("too many entities"),
}
}
}
Expand All @@ -323,7 +336,7 @@ impl Entities {
self.verify_flushed();
self.len += 1;
if let Some(id) = self.pending.pop() {
let new_free_cursor = self.pending.len() as i64;
let new_free_cursor = self.pending.len() as IdCursor;
*self.free_cursor.get_mut() = new_free_cursor;
Entity {
generation: self.meta[id as usize].generation,
Expand All @@ -345,14 +358,14 @@ impl Entities {

let loc = if entity.id as usize >= self.meta.len() {
self.pending.extend((self.meta.len() as u32)..entity.id);
let new_free_cursor = self.pending.len() as i64;
let new_free_cursor = self.pending.len() as IdCursor;
*self.free_cursor.get_mut() = new_free_cursor;
self.meta.resize(entity.id as usize + 1, EntityMeta::EMPTY);
self.len += 1;
None
} else if let Some(index) = self.pending.iter().position(|item| *item == entity.id) {
self.pending.swap_remove(index);
let new_free_cursor = self.pending.len() as i64;
let new_free_cursor = self.pending.len() as IdCursor;
*self.free_cursor.get_mut() = new_free_cursor;
self.len += 1;
None
Expand All @@ -376,14 +389,14 @@ impl Entities {

let result = if entity.id as usize >= self.meta.len() {
self.pending.extend((self.meta.len() as u32)..entity.id);
let new_free_cursor = self.pending.len() as i64;
let new_free_cursor = self.pending.len() as IdCursor;
*self.free_cursor.get_mut() = new_free_cursor;
self.meta.resize(entity.id as usize + 1, EntityMeta::EMPTY);
self.len += 1;
AllocAtWithoutReplacement::DidNotExist
} else if let Some(index) = self.pending.iter().position(|item| *item == entity.id) {
self.pending.swap_remove(index);
let new_free_cursor = self.pending.len() as i64;
let new_free_cursor = self.pending.len() as IdCursor;
*self.free_cursor.get_mut() = new_free_cursor;
self.len += 1;
AllocAtWithoutReplacement::DidNotExist
Expand Down Expand Up @@ -418,7 +431,7 @@ impl Entities {

self.pending.push(entity.id);

let new_free_cursor = self.pending.len() as i64;
let new_free_cursor = self.pending.len() as IdCursor;
*self.free_cursor.get_mut() = new_free_cursor;
self.len -= 1;
Some(loc)
Expand All @@ -429,7 +442,9 @@ impl Entities {
self.verify_flushed();

let freelist_size = *self.free_cursor.get_mut();
let shortfall = additional as i64 - freelist_size;
// Unwrap: these conversions can only fail on platforms that don't support 64-bit atomics
// and use AtomicIsize instead (see note on `IdCursor`).
let shortfall = IdCursor::try_from(additional).unwrap() - freelist_size;
if shortfall > 0 {
self.meta.reserve(shortfall as usize);
}
Expand Down Expand Up @@ -486,7 +501,7 @@ impl Entities {
}

fn needs_flush(&mut self) -> bool {
*self.free_cursor.get_mut() != self.pending.len() as i64
*self.free_cursor.get_mut() != self.pending.len() as IdCursor
}

/// Allocates space for entities previously reserved with `reserve_entity` or
Expand Down