use std::cell::Cell;
use std::marker::PhantomData;
use std::sync::atomic::{AtomicBool, Ordering};
use fuel_dispatch::PrecisionRequirement;
static GLOBAL_STRICT: AtomicBool = AtomicBool::new(false);
std::thread_local! {
static SCOPE_DEPTH: Cell<usize> = const { Cell::new(0) };
}
#[must_use = "strict mode ends when the guard is dropped"]
pub struct StrictReproducibilityGuard {
_not_send: PhantomData<*const ()>,
}
pub fn strict_reproducibility() -> StrictReproducibilityGuard {
SCOPE_DEPTH.with(|d| d.set(d.get() + 1));
StrictReproducibilityGuard {
_not_send: PhantomData,
}
}
impl Drop for StrictReproducibilityGuard {
fn drop(&mut self) {
SCOPE_DEPTH.with(|d| d.set(d.get().saturating_sub(1)));
}
}
pub fn set_global_strict_reproducibility(on: bool) {
GLOBAL_STRICT.store(on, Ordering::Relaxed);
}
pub fn is_strict_reproducibility() -> bool {
GLOBAL_STRICT.load(Ordering::Relaxed) || SCOPE_DEPTH.with(|d| d.get() > 0)
}
pub(crate) fn precision_requirement() -> PrecisionRequirement {
if is_strict_reproducibility() {
PrecisionRequirement::BIT_STABLE
} else {
PrecisionRequirement::default()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn off_by_default_is_unconstrained() {
assert!(!is_strict_reproducibility());
assert!(precision_requirement().is_unconstrained());
}
#[test]
fn scope_requires_bit_stable_and_restores_on_drop() {
{
let _g = strict_reproducibility();
assert!(is_strict_reproducibility());
assert_eq!(precision_requirement(), PrecisionRequirement::BIT_STABLE);
}
assert!(!is_strict_reproducibility());
assert!(precision_requirement().is_unconstrained());
}
#[test]
fn scopes_nest() {
let outer = strict_reproducibility();
{
let _inner = strict_reproducibility();
}
assert!(is_strict_reproducibility(), "inner drop must not end outer");
drop(outer);
assert!(!is_strict_reproducibility());
}
#[test]
fn scope_is_thread_local() {
let _g = strict_reproducibility();
let other = std::thread::spawn(is_strict_reproducibility)
.join()
.unwrap();
assert!(!other, "a scope must not leak to other threads");
}
}