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 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183
//! This module works around limitations of the Rust programming language, and provides missing
//! functionalities that we may expect the Rust programming language and its standard libraries
//! to provide.
pub mod rev_group;
pub mod zeroed_alloc;
/// Const function for min value of two usize numbers.
pub const fn min_of_usize(a: usize, b: usize) -> usize {
if a > b {
b
} else {
a
}
}
#[cfg(feature = "nightly")]
pub use core::intrinsics::{likely, unlikely};
// likely() and unlikely() compiler hints in stable Rust
// [1]: https://github.com/rust-lang/hashbrown/blob/a41bd76de0a53838725b997c6085e024c47a0455/src/raw/mod.rs#L48-L70
// [2]: https://users.rust-lang.org/t/compiler-hint-for-unlikely-likely-for-if-branches/62102/3
#[cfg(not(feature = "nightly"))]
#[inline]
#[cold]
fn cold() {}
#[cfg(not(feature = "nightly"))]
#[inline]
pub fn likely(b: bool) -> bool {
if !b {
cold();
}
b
}
#[cfg(not(feature = "nightly"))]
#[inline]
pub fn unlikely(b: bool) -> bool {
if b {
cold();
}
b
}
use std::cell::UnsafeCell;
use std::mem::MaybeUninit;
use std::sync::Once;
/// InitializeOnce creates an uninitialized value that needs to be manually initialized later. InitializeOnce
/// guarantees the value is only initialized once. This type is used to allow more efficient reads.
/// Unlike the `lazy_static!` which checks whether the static is initialized
/// in every read, InitializeOnce has no extra check for reads.
pub struct InitializeOnce<T: 'static> {
v: UnsafeCell<MaybeUninit<T>>,
/// This is used to guarantee `init_fn` is only called once.
once: Once,
}
impl<T> InitializeOnce<T> {
pub const fn new() -> Self {
InitializeOnce {
v: UnsafeCell::new(MaybeUninit::uninit()),
once: Once::new(),
}
}
/// Initialize the value. This should be called before ever using the struct.
/// If this method is called by multiple threads, the first thread will
/// initialize the value, and the other threads will be blocked until the
/// initialization is done (`Once` returns).
pub fn initialize_once(&self, init_fn: &'static dyn Fn() -> T) {
self.once.call_once(|| {
unsafe { &mut *self.v.get() }.write(init_fn());
});
debug_assert!(self.once.is_completed());
}
/// Get the value. This should only be used after initialize_once()
pub fn get_ref(&self) -> &T {
// We only assert in debug builds.
debug_assert!(self.once.is_completed());
unsafe { (*self.v.get()).assume_init_ref() }
}
/// Get a mutable reference to the value.
/// This is currently only used for SFTMap during plan creation (single threaded),
/// and before the plan creation is done, the binding cannot use MMTK at all.
///
/// # Safety
/// The caller needs to make sure there is no race when mutating the value.
#[allow(clippy::mut_from_ref)]
pub unsafe fn get_mut(&self) -> &mut T {
// We only assert in debug builds.
debug_assert!(self.once.is_completed());
unsafe { (*self.v.get()).assume_init_mut() }
}
}
impl<T> std::ops::Deref for InitializeOnce<T> {
type Target = T;
fn deref(&self) -> &Self::Target {
self.get_ref()
}
}
unsafe impl<T> Sync for InitializeOnce<T> {}
/// This implements `std::array::from_fn` introduced in Rust 1.63.
/// We should replace this with the standard counterpart after bumping MSRV,
/// but we also need to evaluate whether it would use too much stack space (see code comments).
pub(crate) fn array_from_fn<T, const N: usize, F>(mut cb: F) -> [T; N]
where
F: FnMut(usize) -> T,
{
// Note on unsafety: An alternative to the unsafe implementation below is creating a fixed
// array of `[0, 1, 2, ..., N-1]` and using the `.map(cb)` method to create the result.
// However, the `array::map` method implemented in the standard library consumes a surprisingly
// large amount of stack space. For VMs that run on confined stacks, such as JikesRVM, that
// would cause stack overflow. Therefore we implement it manually using unsafe primitives.
let mut result_array: MaybeUninit<[T; N]> = MaybeUninit::zeroed();
let array_ptr = result_array.as_mut_ptr();
for index in 0..N {
let item = cb(index);
unsafe {
std::ptr::addr_of_mut!((*array_ptr)[index]).write(item);
}
}
unsafe { result_array.assume_init() }
}
/// Create a formatted string that makes the best effort idenfying the current process and thread.
pub fn debug_process_thread_id() -> String {
let pid = unsafe { libc::getpid() };
#[cfg(target_os = "linux")]
{
// `gettid()` is Linux-specific.
let tid = unsafe { libc::gettid() };
format!("PID: {}, TID: {}", pid, tid)
}
#[cfg(not(target_os = "linux"))]
{
// TODO: When we support other platforms, use platform-specific methods to get thread
// identifiers.
format!("PID: {}", pid)
}
}
#[cfg(test)]
mod initialize_once_tests {
use super::*;
#[test]
fn test_threads_compete_initialize() {
use std::sync::atomic::AtomicUsize;
use std::sync::atomic::Ordering;
use std::thread;
// Create multiple threads to initialize the same `InitializeOnce` value
const N_THREADS: usize = 1000;
// The test value
static I: InitializeOnce<usize> = InitializeOnce::new();
// Count how many times the function is called
static INITIALIZE_COUNT: AtomicUsize = AtomicUsize::new(0);
// The function to create initial value
fn initialize_usize() -> usize {
INITIALIZE_COUNT.fetch_add(1, Ordering::SeqCst);
42
}
let mut threads = vec![];
for _ in 1..N_THREADS {
threads.push(thread::spawn(|| {
I.initialize_once(&initialize_usize);
// Every thread should see the value correctly initialized.
assert_eq!(*I, 42);
}));
}
threads.into_iter().for_each(|t| t.join().unwrap());
// The initialize_usize should only be called once
assert_eq!(INITIALIZE_COUNT.load(Ordering::SeqCst), 1);
}
}