Skip to content

Commit 5864605

Browse files
More robust AtomicIdCursor behavior
- Rename to [Atomic]IdCursor - Add comments documentating behavior on non-64-bit-atomic platforms - try_from().unwrap() in cases that can only fail with 32-bit AtomicIsize
1 parent 6946b37 commit 5864605

File tree

1 file changed

+26
-17
lines changed
  • crates/bevy_ecs/src/entity

1 file changed

+26
-17
lines changed

crates/bevy_ecs/src/entity/mod.rs

Lines changed: 26 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -32,14 +32,17 @@ use crate::{archetype::ArchetypeId, storage::SparseSetIndex};
3232
use std::{convert::TryFrom, fmt, mem, sync::atomic::Ordering};
3333

3434
#[cfg(target_has_atomic = "64")]
35-
use std::sync::atomic::AtomicI64 as AtomicInt;
35+
use std::sync::atomic::AtomicI64 as AtomicIdCursor;
3636
#[cfg(target_has_atomic = "64")]
37-
type Int = i64;
37+
type IdCursor = i64;
3838

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

4447
/// Lightweight unique ID of an entity.
4548
///
@@ -243,7 +246,7 @@ pub struct Entities {
243246
///
244247
/// Once `flush()` is done, `free_cursor` will equal `pending.len()`.
245248
pending: Vec<u32>,
246-
free_cursor: AtomicInt,
249+
free_cursor: AtomicIdCursor,
247250
/// Stores the number of free entities for [`len`](Entities::len)
248251
len: u32,
249252
}
@@ -256,8 +259,12 @@ impl Entities {
256259
// Use one atomic subtract to grab a range of new IDs. The range might be
257260
// entirely nonnegative, meaning all IDs come from the freelist, or entirely
258261
// negative, meaning they are all new IDs to allocate, or a mix of both.
259-
let range_end = self.free_cursor.fetch_sub(count as Int, Ordering::Relaxed);
260-
let range_start = range_end - count as Int;
262+
let range_end = self
263+
.free_cursor
264+
// Unwrap: these conversions can only fail on platforms that don't support 64-bit atomics
265+
// and use AtomicIsize instead (see note on `IdCursor`).
266+
.fetch_sub(IdCursor::try_from(count).unwrap(), Ordering::Relaxed);
267+
let range_start = range_end - IdCursor::try_from(count).unwrap();
261268

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

@@ -274,7 +281,7 @@ impl Entities {
274281
// In this example, we truncate the end to 0, leaving us with `-3..0`.
275282
// Then we negate these values to indicate how far beyond the end of `meta.end()`
276283
// to go, yielding `meta.len()+0 .. meta.len()+3`.
277-
let base = self.meta.len() as Int;
284+
let base = self.meta.len() as IdCursor;
278285

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

@@ -311,7 +318,7 @@ impl Entities {
311318
// and farther beyond `meta.len()`.
312319
Entity {
313320
generation: 0,
314-
id: u32::try_from(self.meta.len() as Int - n).expect("too many entities"),
321+
id: u32::try_from(self.meta.len() as IdCursor - n).expect("too many entities"),
315322
}
316323
}
317324
}
@@ -329,7 +336,7 @@ impl Entities {
329336
self.verify_flushed();
330337
self.len += 1;
331338
if let Some(id) = self.pending.pop() {
332-
let new_free_cursor = self.pending.len() as Int;
339+
let new_free_cursor = self.pending.len() as IdCursor;
333340
*self.free_cursor.get_mut() = new_free_cursor;
334341
Entity {
335342
generation: self.meta[id as usize].generation,
@@ -351,14 +358,14 @@ impl Entities {
351358

352359
let loc = if entity.id as usize >= self.meta.len() {
353360
self.pending.extend((self.meta.len() as u32)..entity.id);
354-
let new_free_cursor = self.pending.len() as Int;
361+
let new_free_cursor = self.pending.len() as IdCursor;
355362
*self.free_cursor.get_mut() = new_free_cursor;
356363
self.meta.resize(entity.id as usize + 1, EntityMeta::EMPTY);
357364
self.len += 1;
358365
None
359366
} else if let Some(index) = self.pending.iter().position(|item| *item == entity.id) {
360367
self.pending.swap_remove(index);
361-
let new_free_cursor = self.pending.len() as Int;
368+
let new_free_cursor = self.pending.len() as IdCursor;
362369
*self.free_cursor.get_mut() = new_free_cursor;
363370
self.len += 1;
364371
None
@@ -382,14 +389,14 @@ impl Entities {
382389

383390
let result = if entity.id as usize >= self.meta.len() {
384391
self.pending.extend((self.meta.len() as u32)..entity.id);
385-
let new_free_cursor = self.pending.len() as Int;
392+
let new_free_cursor = self.pending.len() as IdCursor;
386393
*self.free_cursor.get_mut() = new_free_cursor;
387394
self.meta.resize(entity.id as usize + 1, EntityMeta::EMPTY);
388395
self.len += 1;
389396
AllocAtWithoutReplacement::DidNotExist
390397
} else if let Some(index) = self.pending.iter().position(|item| *item == entity.id) {
391398
self.pending.swap_remove(index);
392-
let new_free_cursor = self.pending.len() as Int;
399+
let new_free_cursor = self.pending.len() as IdCursor;
393400
*self.free_cursor.get_mut() = new_free_cursor;
394401
self.len += 1;
395402
AllocAtWithoutReplacement::DidNotExist
@@ -424,7 +431,7 @@ impl Entities {
424431

425432
self.pending.push(entity.id);
426433

427-
let new_free_cursor = self.pending.len() as Int;
434+
let new_free_cursor = self.pending.len() as IdCursor;
428435
*self.free_cursor.get_mut() = new_free_cursor;
429436
self.len -= 1;
430437
Some(loc)
@@ -435,7 +442,9 @@ impl Entities {
435442
self.verify_flushed();
436443

437444
let freelist_size = *self.free_cursor.get_mut();
438-
let shortfall = additional as Int - freelist_size;
445+
// Unwrap: these conversions can only fail on platforms that don't support 64-bit atomics
446+
// and use AtomicIsize instead (see note on `IdCursor`).
447+
let shortfall = IdCursor::try_from(additional).unwrap() - freelist_size;
439448
if shortfall > 0 {
440449
self.meta.reserve(shortfall as usize);
441450
}
@@ -492,7 +501,7 @@ impl Entities {
492501
}
493502

494503
fn needs_flush(&mut self) -> bool {
495-
*self.free_cursor.get_mut() != self.pending.len() as Int
504+
*self.free_cursor.get_mut() != self.pending.len() as IdCursor
496505
}
497506

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

0 commit comments

Comments
 (0)