Skip to content

Commit af50c7f

Browse files
ian-h-chamberlainItsDoot
authored andcommitted
bevy_ecs: Use 32-bit entity ID cursor on platforms without AtomicI64 (bevyengine#4452)
# Objective - Fixes bevyengine#4451 ## Solution - Conditionally compile entity ID cursor as `AtomicI32` when compiling on a platform that does not support 64-bit atomics. - This effectively raises the MSRV to 1.60 as it uses a `#[cfg]` that was only just stabilized there. (should this be noted in changelog?) --- ## Changelog - Added `bevy_ecs` support for platforms without 64-bit atomic ints ## Migration Guide N/A
1 parent a2f5bad commit af50c7f

File tree

1 file changed

+33
-18
lines changed
  • crates/bevy_ecs/src/entity

1 file changed

+33
-18
lines changed

crates/bevy_ecs/src/entity/mod.rs

Lines changed: 33 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -40,11 +40,20 @@ pub use self::serde::*;
4040
pub use map_entities::*;
4141

4242
use crate::{archetype::ArchetypeId, storage::SparseSetIndex};
43-
use std::{
44-
convert::TryFrom,
45-
fmt, mem,
46-
sync::atomic::{AtomicI64, Ordering},
47-
};
43+
use std::{convert::TryFrom, fmt, mem, sync::atomic::Ordering};
44+
45+
#[cfg(target_has_atomic = "64")]
46+
use std::sync::atomic::AtomicI64 as AtomicIdCursor;
47+
#[cfg(target_has_atomic = "64")]
48+
type IdCursor = i64;
49+
50+
/// Most modern platforms support 64-bit atomics, but some less-common platforms
51+
/// do not. This fallback allows compilation using a 32-bit cursor instead, with
52+
/// the caveat that some conversions may fail (and panic) at runtime.
53+
#[cfg(not(target_has_atomic = "64"))]
54+
use std::sync::atomic::AtomicIsize as AtomicIdCursor;
55+
#[cfg(not(target_has_atomic = "64"))]
56+
type IdCursor = isize;
4857

4958
/// Lightweight identifier of an [entity](crate::entity).
5059
///
@@ -291,7 +300,7 @@ pub struct Entities {
291300
///
292301
/// Once `flush()` is done, `free_cursor` will equal `pending.len()`.
293302
pending: Vec<u32>,
294-
free_cursor: AtomicI64,
303+
free_cursor: AtomicIdCursor,
295304
/// Stores the number of free entities for [`len`](Entities::len)
296305
len: u32,
297306
}
@@ -304,8 +313,12 @@ impl Entities {
304313
// Use one atomic subtract to grab a range of new IDs. The range might be
305314
// entirely nonnegative, meaning all IDs come from the freelist, or entirely
306315
// negative, meaning they are all new IDs to allocate, or a mix of both.
307-
let range_end = self.free_cursor.fetch_sub(count as i64, Ordering::Relaxed);
308-
let range_start = range_end - count as i64;
316+
let range_end = self
317+
.free_cursor
318+
// Unwrap: these conversions can only fail on platforms that don't support 64-bit atomics
319+
// and use AtomicIsize instead (see note on `IdCursor`).
320+
.fetch_sub(IdCursor::try_from(count).unwrap(), Ordering::Relaxed);
321+
let range_start = range_end - IdCursor::try_from(count).unwrap();
309322

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

@@ -322,7 +335,7 @@ impl Entities {
322335
// In this example, we truncate the end to 0, leaving us with `-3..0`.
323336
// Then we negate these values to indicate how far beyond the end of `meta.end()`
324337
// to go, yielding `meta.len()+0 .. meta.len()+3`.
325-
let base = self.meta.len() as i64;
338+
let base = self.meta.len() as IdCursor;
326339

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

@@ -359,7 +372,7 @@ impl Entities {
359372
// and farther beyond `meta.len()`.
360373
Entity {
361374
generation: 0,
362-
id: u32::try_from(self.meta.len() as i64 - n).expect("too many entities"),
375+
id: u32::try_from(self.meta.len() as IdCursor - n).expect("too many entities"),
363376
}
364377
}
365378
}
@@ -377,7 +390,7 @@ impl Entities {
377390
self.verify_flushed();
378391
self.len += 1;
379392
if let Some(id) = self.pending.pop() {
380-
let new_free_cursor = self.pending.len() as i64;
393+
let new_free_cursor = self.pending.len() as IdCursor;
381394
*self.free_cursor.get_mut() = new_free_cursor;
382395
Entity {
383396
generation: self.meta[id as usize].generation,
@@ -399,14 +412,14 @@ impl Entities {
399412

400413
let loc = if entity.id as usize >= self.meta.len() {
401414
self.pending.extend((self.meta.len() as u32)..entity.id);
402-
let new_free_cursor = self.pending.len() as i64;
415+
let new_free_cursor = self.pending.len() as IdCursor;
403416
*self.free_cursor.get_mut() = new_free_cursor;
404417
self.meta.resize(entity.id as usize + 1, EntityMeta::EMPTY);
405418
self.len += 1;
406419
None
407420
} else if let Some(index) = self.pending.iter().position(|item| *item == entity.id) {
408421
self.pending.swap_remove(index);
409-
let new_free_cursor = self.pending.len() as i64;
422+
let new_free_cursor = self.pending.len() as IdCursor;
410423
*self.free_cursor.get_mut() = new_free_cursor;
411424
self.len += 1;
412425
None
@@ -430,14 +443,14 @@ impl Entities {
430443

431444
let result = if entity.id as usize >= self.meta.len() {
432445
self.pending.extend((self.meta.len() as u32)..entity.id);
433-
let new_free_cursor = self.pending.len() as i64;
446+
let new_free_cursor = self.pending.len() as IdCursor;
434447
*self.free_cursor.get_mut() = new_free_cursor;
435448
self.meta.resize(entity.id as usize + 1, EntityMeta::EMPTY);
436449
self.len += 1;
437450
AllocAtWithoutReplacement::DidNotExist
438451
} else if let Some(index) = self.pending.iter().position(|item| *item == entity.id) {
439452
self.pending.swap_remove(index);
440-
let new_free_cursor = self.pending.len() as i64;
453+
let new_free_cursor = self.pending.len() as IdCursor;
441454
*self.free_cursor.get_mut() = new_free_cursor;
442455
self.len += 1;
443456
AllocAtWithoutReplacement::DidNotExist
@@ -472,7 +485,7 @@ impl Entities {
472485

473486
self.pending.push(entity.id);
474487

475-
let new_free_cursor = self.pending.len() as i64;
488+
let new_free_cursor = self.pending.len() as IdCursor;
476489
*self.free_cursor.get_mut() = new_free_cursor;
477490
self.len -= 1;
478491
Some(loc)
@@ -483,7 +496,9 @@ impl Entities {
483496
self.verify_flushed();
484497

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

542557
fn needs_flush(&mut self) -> bool {
543-
*self.free_cursor.get_mut() != self.pending.len() as i64
558+
*self.free_cursor.get_mut() != self.pending.len() as IdCursor
544559
}
545560

546561
/// Allocates space for entities previously reserved with `reserve_entity` or

0 commit comments

Comments
 (0)