This policy attempts to reuse the same instance slot for subsequent instantiations of the same module. This is particularly useful when using a pooling backend such as memfd that benefits from this reuse: for example, in the memfd case, instantiating the same module into the same slot allows us to avoid several calls to mmap() because the same mappings can be reused. The policy tracks a freelist per "compiled module ID", and when allocating a slot for an instance, tries these three options in order: 1. A slot from the freelist for this module (i.e., last used for another instantiation of this particular module), or 3. A slot that was last used by some other module or never before. The "victim" slot for choice 2 is randomly chosen. The data structures are carefully designed so that all updates are O(1), and there is no retry-loop in any of the random selection. This policy is now the default when the memfd backend is selected via the `memfd-allocator` feature flag.
44 lines
1.4 KiB
Rust
44 lines
1.4 KiB
Rust
//! 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())
|
|
}
|
|
}
|