mmtk/util/rust_util/mod.rs
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
//! 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 atomic_box;
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> {}
/// 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);
}
}