1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43
//! Unique IDs for modules in the runtime.
use std::{
num::NonZeroU64,
sync::atomic::{AtomicU64, Ordering},
};
/// A unique identifier (within an engine or similar) for a compiled
/// module.
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct CompiledModuleId(NonZeroU64);
/// An allocator for compiled module IDs.
pub struct CompiledModuleIdAllocator {
next: AtomicU64,
}
impl CompiledModuleIdAllocator {
/// Create a compiled-module ID allocator.
pub fn new() -> Self {
Self {
next: AtomicU64::new(1),
}
}
/// Allocate a new ID.
pub fn alloc(&self) -> CompiledModuleId {
// Note: why is `Relaxed` OK here?
//
// The only requirement we have is that IDs are unique. We
// don't care how one module's ID compares to another, i.e.,
// what order they come in. `Relaxed` means that this
// `fetch_add` operation does not have any particular
// synchronization (ordering) with respect to any other memory
// access in the program. However, `fetch_add` is always
// atomic with respect to other accesses to this variable
// (`self.next`). So we will always hand out separate, unique
// IDs correctly, just in some possibly arbitrary order (which
// is fine).
let id = self.next.fetch_add(1, Ordering::Relaxed);
CompiledModuleId(NonZeroU64::new(id).unwrap())
}
}