pub mod bundle;
mod runtime;
#[allow(dead_code)]
mod sys;
mod target;
pub use runtime::*;
pub use target::{TARGET_FAMILY, TARGET_KEY, Target};
#[cfg(feature = "loom")]
pub mod loom;
#[derive(Debug, thiserror::Error)]
#[non_exhaustive]
pub enum Error {
#[error("{0}")]
Message(String),
#[cfg(feature = "loom")]
#[error("Loom compilation failed: {message}")]
Compile {
message: String,
diagnostics: Vec<loom::Diagnostic>,
},
#[error("{0}")]
Io(#[from] std::io::Error),
#[error("{0}")]
Json(#[from] serde_json::Error),
#[error("{0}")]
Library(#[from] libloading::Error),
#[cfg(feature = "download")]
#[error("{0}")]
Download(#[source] Box<ureq::Error>),
#[error("{context}: {message}")]
Runtime {
context: String,
code: i32,
message: String,
},
#[error("{context}: {source}")]
Context {
context: String,
#[source]
source: Box<Error>,
},
}
impl Error {
pub fn context(self, context: impl Into<String>) -> Self {
Self::Context {
context: context.into(),
source: Box::new(self),
}
}
}
pub type Result<T> = std::result::Result<T, Error>;
#[cfg(feature = "runner")]
pub mod runner;
pub(crate) fn cached_init<T: Clone>(
cache: &std::sync::Mutex<Option<T>>,
initialize: impl FnOnce() -> Result<T>,
) -> Result<T> {
let mut cached = cache
.lock()
.map_err(|_| Error::Message("initialization cache poisoned".into()))?;
if let Some(value) = cached.as_ref() {
return Ok(value.clone());
}
let value = initialize()?;
*cached = Some(value.clone());
Ok(value)
}
#[cfg(test)]
mod initialization_tests {
#[test]
fn failures_retry_and_concurrent_success_initializes_once() {
use std::sync::{
Mutex,
atomic::{AtomicUsize, Ordering},
};
let cache = Mutex::new(None);
assert!(
super::cached_init(&cache, || Err::<usize, _>(super::Error::Message(
"transient".into()
)))
.is_err()
);
let calls = AtomicUsize::new(0);
std::thread::scope(|scope| {
for _ in 0..8 {
scope.spawn(|| {
assert_eq!(
super::cached_init(&cache, || {
calls.fetch_add(1, Ordering::Relaxed);
Ok(42)
})
.unwrap(),
42
);
});
}
});
assert_eq!(calls.load(Ordering::Relaxed), 1);
}
}