use std::cell::{Cell, RefCell};
use crate::energy::EnergyBudget;
use crate::shared_stats::{SharedRecipe, SharedStats};
use crate::split_loop::{AdaptiveConfig, Parallelism};
thread_local! {
static EXPLORER_CTX: RefCell<ExplorerCtx> = RefCell::new(ExplorerCtx::inactive());
static RNG_GET_COUNT: Cell<fn() -> u64> = const { Cell::new(|| 0) };
static RNG_RESEED: Cell<fn(u64)> = const { Cell::new(|_| {}) };
pub(crate) static SHARED_STATS: Cell<*mut SharedStats> = const { Cell::new(std::ptr::null_mut()) };
pub(crate) static SHARED_RECIPE: Cell<*mut SharedRecipe> = const { Cell::new(std::ptr::null_mut()) };
pub(crate) static EXPLORED_MAP_PTR: Cell<*mut u8> = const { Cell::new(std::ptr::null_mut()) };
pub(crate) static COVERAGE_BITMAP_PTR: Cell<*mut u8> = const { Cell::new(std::ptr::null_mut()) };
pub(crate) static ASSERTION_TABLE: Cell<*mut u8> = const { Cell::new(std::ptr::null_mut()) };
pub(crate) static ENERGY_BUDGET_PTR: Cell<*mut EnergyBudget> = const { Cell::new(std::ptr::null_mut()) };
pub(crate) static EACH_BUCKET_PTR: Cell<*mut u8> = const { Cell::new(std::ptr::null_mut()) };
pub(crate) static BITMAP_POOL: Cell<*mut u8> = const { Cell::new(std::ptr::null_mut()) };
pub(crate) static BITMAP_POOL_SLOTS: Cell<usize> = const { Cell::new(0) };
}
pub struct ExplorerCtx {
pub active: bool,
pub is_child: bool,
pub depth: u32,
pub max_depth: u32,
pub current_seed: u64,
pub recipe: Vec<(u64, u64)>,
pub timelines_per_split: u32,
pub adaptive: Option<AdaptiveConfig>,
pub parallelism: Option<Parallelism>,
pub warm_start: bool,
}
impl ExplorerCtx {
pub fn inactive() -> Self {
Self {
active: false,
is_child: false,
depth: 0,
max_depth: 0,
current_seed: 0,
recipe: Vec::new(),
timelines_per_split: 0,
adaptive: None,
parallelism: None,
warm_start: false,
}
}
}
pub fn set_rng_hooks(get_count: fn() -> u64, reseed: fn(u64)) {
RNG_GET_COUNT.with(|c| c.set(get_count));
RNG_RESEED.with(|c| c.set(reseed));
}
pub(crate) fn rng_get_count() -> u64 {
RNG_GET_COUNT.with(|c| (c.get())())
}
pub(crate) fn rng_reseed(seed: u64) {
RNG_RESEED.with(|c| (c.get())(seed));
}
pub(crate) fn with_ctx<R>(f: impl FnOnce(&ExplorerCtx) -> R) -> R {
EXPLORER_CTX.with(|ctx| f(&ctx.borrow()))
}
pub(crate) fn with_ctx_mut<R>(f: impl FnOnce(&mut ExplorerCtx) -> R) -> R {
EXPLORER_CTX.with(|ctx| f(&mut ctx.borrow_mut()))
}
pub fn explorer_is_active() -> bool {
with_ctx(|ctx| ctx.active)
}
pub fn explorer_is_child() -> bool {
with_ctx(|ctx| ctx.is_child)
}
pub fn assertion_table_ptr() -> *mut u8 {
ASSERTION_TABLE.with(|c| c.get())
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_default_hooks() {
assert_eq!(rng_get_count(), 0);
rng_reseed(42);
}
#[test]
fn test_set_hooks() {
thread_local! {
static CALL_COUNT: Cell<u64> = const { Cell::new(0) };
static LAST_SEED: Cell<u64> = const { Cell::new(0) };
}
set_rng_hooks(
|| CALL_COUNT.with(|c| c.get()),
|seed| LAST_SEED.with(|s| s.set(seed)),
);
CALL_COUNT.with(|c| c.set(42));
assert_eq!(rng_get_count(), 42);
rng_reseed(123);
assert_eq!(LAST_SEED.with(|s| s.get()), 123);
set_rng_hooks(|| 0, |_| {});
}
#[test]
fn test_inactive_by_default() {
assert!(!explorer_is_active());
assert!(!explorer_is_child());
}
}