#![deny(warnings, missing_debug_implementations, missing_docs)]
pub mod annotations;
pub mod future;
pub mod hint;
pub mod lazy_static;
pub mod rand;
pub mod sync;
pub mod thread;
pub mod current;
pub mod scheduler;
mod runtime;
use std::cell::Cell;
pub use runtime::runner::{PortfolioRunner, Runner};
#[derive(Clone, Debug)]
#[non_exhaustive]
pub struct Config {
pub stack_size: usize,
pub failure_persistence: FailurePersistence,
pub max_steps: MaxSteps,
pub max_time: Option<std::time::Duration>,
pub silence_warnings: bool,
pub record_steps_in_span: bool,
pub ungraceful_shutdown_config: UngracefulShutdownConfig,
}
std::thread_local! {
pub(crate) static UNGRACEFUL_SHUTDOWN_CONFIG: Cell<UngracefulShutdownConfig> = const { Cell::new(UngracefulShutdownConfig::new()) };
}
#[derive(Copy, Clone, Debug)]
#[non_exhaustive]
pub enum ContinuationFunctionBehavior {
Drop,
Leak,
}
impl ContinuationFunctionBehavior {
pub const fn new() -> Self {
Self::Leak
}
}
impl Default for ContinuationFunctionBehavior {
fn default() -> Self {
Self::new()
}
}
#[derive(Copy, Clone, Debug)]
#[non_exhaustive]
pub struct UngracefulShutdownConfig {
pub immediately_return_on_panic: bool,
pub continuation_function_behavior: ContinuationFunctionBehavior,
}
impl UngracefulShutdownConfig {
pub const fn new() -> Self {
Self {
immediately_return_on_panic: false,
continuation_function_behavior: ContinuationFunctionBehavior::new(),
}
}
}
impl Default for UngracefulShutdownConfig {
fn default() -> Self {
Self::new()
}
}
impl Config {
pub fn new() -> Self {
Self {
stack_size: 0xf000,
failure_persistence: FailurePersistence::Print,
max_steps: MaxSteps::FailAfter(1_000_000),
max_time: None,
silence_warnings: false,
record_steps_in_span: false,
ungraceful_shutdown_config: UngracefulShutdownConfig::default(),
}
}
}
impl Default for Config {
fn default() -> Self {
Self::new()
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
#[non_exhaustive]
pub enum FailurePersistence {
None,
Print,
File(Option<std::path::PathBuf>),
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[non_exhaustive]
pub enum MaxSteps {
None,
FailAfter(usize),
ContinueAfter(usize),
}
#[doc(hidden)]
pub fn check<F>(f: F)
where
F: Fn() + Send + Sync + 'static,
{
use crate::scheduler::RoundRobinScheduler;
let runner = Runner::new(RoundRobinScheduler::new(1), Default::default());
runner.run(f);
}
pub fn check_urw<F>(f: F, iterations: usize)
where
F: Fn() + Send + Sync + 'static,
{
use crate::scheduler::UrwRandomScheduler;
let runner = Runner::new(UrwRandomScheduler::new(iterations), Default::default());
runner.run(f);
}
pub fn check_random<F>(f: F, iterations: usize)
where
F: Fn() + Send + Sync + 'static,
{
use crate::scheduler::RandomScheduler;
let runner = Runner::new(RandomScheduler::new(iterations), Default::default());
runner.run(f);
}
pub fn check_random_with_seed<F>(f: F, seed: u64, iterations: usize)
where
F: Fn() + Send + Sync + 'static,
{
use crate::scheduler::RandomScheduler;
let scheduler = RandomScheduler::new_from_seed(seed, iterations);
let runner = Runner::new(scheduler, Default::default());
runner.run(f);
}
pub fn check_pct<F>(f: F, iterations: usize, depth: usize)
where
F: Fn() + Send + Sync + 'static,
{
use crate::scheduler::PctScheduler;
let scheduler = PctScheduler::new(depth, iterations);
let runner = Runner::new(scheduler, Default::default());
runner.run(f);
}
pub fn check_dfs<F>(f: F, max_iterations: Option<usize>)
where
F: Fn() + Send + Sync + 'static,
{
use crate::scheduler::DfsScheduler;
let scheduler = DfsScheduler::new(max_iterations, false);
let runner = Runner::new(scheduler, Default::default());
runner.run(f);
}
pub fn check_uncontrolled_nondeterminism<F>(f: F, max_iterations: usize)
where
F: Fn() + Send + Sync + 'static,
{
use crate::scheduler::RandomScheduler;
use crate::scheduler::UncontrolledNondeterminismCheckScheduler;
let scheduler = UncontrolledNondeterminismCheckScheduler::new(RandomScheduler::new(max_iterations));
let runner = Runner::new(scheduler, Config::default());
runner.run(f);
}
pub fn replay<F>(f: F, encoded_schedule: &str)
where
F: Fn() + Send + Sync + 'static,
{
use crate::scheduler::ReplayScheduler;
let scheduler = ReplayScheduler::new_from_encoded(encoded_schedule);
let runner = Runner::new(scheduler, Default::default());
runner.run(f);
}
pub fn annotate_replay<F>(f: F, encoded_schedule: &str)
where
F: Fn() + Send + Sync + 'static,
{
use crate::scheduler::{AnnotationScheduler, ReplayScheduler};
let scheduler_inner = ReplayScheduler::new_from_encoded(encoded_schedule);
let scheduler = AnnotationScheduler::new(scheduler_inner);
let runner = Runner::new(scheduler, Default::default());
runner.run(f);
}
pub fn replay_from_file<F, P>(f: F, path: P)
where
F: Fn() + Send + Sync + 'static,
P: AsRef<std::path::Path>,
{
use crate::scheduler::ReplayScheduler;
let scheduler = ReplayScheduler::new_from_file(path).expect("could not load schedule from file");
let runner = Runner::new(scheduler, Default::default());
runner.run(f);
}
pub const CAPTURE_BACKTRACE: &str = "SHUTTLE_CAPTURE_BACKTRACE";
const RANDOM_SEED: &str = "SHUTTLE_RANDOM_SEED";
pub const SILENCE_WARNINGS: &str = "SHUTTLE_SILENCE_WARNINGS";
pub const ANNOTATION_FILE: &str = "SHUTTLE_ANNOTATION_FILE";
#[cfg(feature = "annotation")]
pub(crate) fn annotation_file() -> String {
std::env::var(ANNOTATION_FILE).unwrap_or_else(|_| "annotated.json".to_string())
}
pub(crate) fn silence_warnings() -> bool {
std::env::var(SILENCE_WARNINGS).is_ok()
}
pub(crate) fn backtrace_enabled() -> bool {
std::env::var(CAPTURE_BACKTRACE).is_ok()
}
pub(crate) fn seed_from_env(fallback_seed: u64) -> u64 {
let seed_env = std::env::var(RANDOM_SEED);
match seed_env {
Ok(s) => match s.as_str().parse::<u64>() {
Ok(seed) => {
tracing::info!(
"Initializing scheduler with the seed provided by {}: {}",
RANDOM_SEED,
seed
);
seed
}
Err(err) => panic!("The seed provided by {RANDOM_SEED} is not a valid u64: {err}"),
},
Err(_) => fallback_seed,
}
}
#[macro_export]
macro_rules! thread_local {
() => {};
($(#[$attr:meta])* $vis:vis static $name:ident: $t:ty = const { $init:expr }; $($rest:tt)*) => (
$crate::__thread_local_inner!($(#[$attr])* $vis $name, $t, $init);
$crate::thread_local!($($rest)*);
);
($(#[$attr:meta])* $vis:vis static $name:ident: $t:ty = const { $init:expr }) => (
$crate::__thread_local_inner!($(#[$attr])* $vis $name, $t, $init);
);
($(#[$attr:meta])* $vis:vis static $name:ident: $t:ty = $init:expr; $($rest:tt)*) => (
$crate::__thread_local_inner!($(#[$attr])* $vis $name, $t, $init);
$crate::thread_local!($($rest)*);
);
($(#[$attr:meta])* $vis:vis static $name:ident: $t:ty = $init:expr) => (
$crate::__thread_local_inner!($(#[$attr])* $vis $name, $t, $init);
);
}
#[doc(hidden)]
#[macro_export]
macro_rules! __thread_local_inner {
($(#[$attr:meta])* $vis:vis $name:ident, $t:ty, $init:expr) => {
$(#[$attr])* $vis static $name: $crate::thread::LocalKey<$t> =
$crate::thread::LocalKey {
init: || { $init },
_p: std::marker::PhantomData,
};
}
}
#[macro_export]
macro_rules! lazy_static {
($(#[$attr:meta])* static ref $N:ident : $T:ty = $e:expr; $($t:tt)*) => {
$crate::__lazy_static_internal!($(#[$attr])* () static ref $N : $T = $e; $($t)*);
};
($(#[$attr:meta])* pub static ref $N:ident : $T:ty = $e:expr; $($t:tt)*) => {
$crate::__lazy_static_internal!($(#[$attr])* (pub) static ref $N : $T = $e; $($t)*);
};
($(#[$attr:meta])* pub ($($vis:tt)+) static ref $N:ident : $T:ty = $e:expr; $($t:tt)*) => {
$crate::__lazy_static_internal!($(#[$attr])* (pub ($($vis)+)) static ref $N : $T = $e; $($t)*);
};
() => ()
}
#[macro_export]
#[doc(hidden)]
macro_rules! __lazy_static_internal {
($(#[$attr:meta])* ($($vis:tt)*) static ref $N:ident : $T:ty = $e:expr; $($t:tt)*) => {
$crate::__lazy_static_internal!(@MAKE TY, $(#[$attr])*, ($($vis)*), $N);
$crate::__lazy_static_internal!(@TAIL, $N : $T = $e);
$crate::lazy_static!($($t)*);
};
(@TAIL, $N:ident : $T:ty = $e:expr) => {
impl ::std::ops::Deref for $N {
type Target = $T;
fn deref(&self) -> &$T {
#[inline(always)]
fn __static_ref_initialize() -> $T { $e }
#[inline(always)]
fn __stability() -> &'static $T {
static LAZY: $crate::lazy_static::Lazy<$T> = $crate::lazy_static::Lazy::new(__static_ref_initialize);
LAZY.get()
}
__stability()
}
}
impl $crate::lazy_static::LazyStatic for $N {
fn initialize(lazy: &Self) {
let _ = &**lazy;
}
}
};
(@MAKE TY, $(#[$attr:meta])*, ($($vis:tt)*), $N:ident) => {
#[allow(missing_copy_implementations)]
#[allow(non_camel_case_types)]
#[allow(dead_code)]
$(#[$attr])*
$($vis)* struct $N {__private_field: ()}
#[doc(hidden)]
$($vis)* static $N: $N = $N {__private_field: ()};
};
() => ()
}