use std::fmt;
use std::sync::atomic::{AtomicU64, Ordering};
static FAULTS: AtomicU64 = AtomicU64::new(0);
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum RuntimeError {
PoisonedLock(&'static str),
Abandoned(&'static str),
AlreadyCollected(&'static str),
EntropyFailed,
CapIdCollision,
}
impl fmt::Display for RuntimeError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
RuntimeError::PoisonedLock(where_) => {
write!(f, "runtime mutex poisoned at {where_}")
}
RuntimeError::Abandoned(where_) => {
write!(
f,
"{where_}: flow was destroyed before producing an outcome"
)
}
RuntimeError::AlreadyCollected(where_) => {
write!(f, "{where_}: outcome was already collected")
}
RuntimeError::EntropyFailed => {
write!(f, "capability CSPRNG unavailable")
}
RuntimeError::CapIdCollision => {
write!(f, "could not allocate a unique capability id")
}
}
}
}
impl std::error::Error for RuntimeError {}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum LifecycleError {
NoSuchFlow(super::process::FlowId),
InvalidCapability,
InvalidMonitor,
InvalidLink,
NotOwner,
AlreadyRegistered,
AlreadyLinked,
EmptyName,
SelfRelation,
Unavailable,
}
impl fmt::Display for LifecycleError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::NoSuchFlow(id) => write!(f, "no live flow {id}"),
Self::InvalidCapability => write!(f, "unknown or revoked capability"),
Self::InvalidMonitor => write!(f, "unknown monitor"),
Self::InvalidLink => write!(f, "unknown link"),
Self::NotOwner => write!(f, "caller does not own this relation"),
Self::AlreadyRegistered => write!(f, "registry name already taken"),
Self::AlreadyLinked => write!(f, "flows are already linked"),
Self::EmptyName => write!(f, "registry name must be non-empty"),
Self::SelfRelation => write!(f, "cannot link or monitor a flow to itself"),
Self::Unavailable => write!(f, "runtime table unavailable (poisoned lock)"),
}
}
}
impl std::error::Error for LifecycleError {}
#[cold]
pub fn report_fault(err: RuntimeError) {
FAULTS.fetch_add(1, Ordering::Relaxed);
eprintln!("byteflow: {err} — fail-closed");
}
pub fn fault_count() -> u64 {
FAULTS.load(Ordering::Relaxed)
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum SpawnError {
BadFunction { index: u32, table_size: u32 },
VerifyFailed(crate::bytecode::VerifyError),
InvalidCapability,
Unavailable,
FlowLimit { current: usize, max: u32 },
SpawnDenied(String),
NameTaken { name: String },
ThreadSpawnFailed(String),
VmInit(String),
}
impl fmt::Display for SpawnError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
SpawnError::BadFunction { index, table_size } => {
write!(
f,
"spawn: function index {index} out of range (table size {table_size})"
)
}
SpawnError::VerifyFailed(err) => write!(f, "chunk verification failed: {err}"),
SpawnError::InvalidCapability => {
write!(f, "spawn: argument capability is unknown or not held")
}
SpawnError::Unavailable => write!(f, "spawn: runtime table unavailable"),
SpawnError::FlowLimit { current, max } => {
write!(f, "spawn: live flow limit reached ({current}/{max})")
}
SpawnError::SpawnDenied(msg) => write!(f, "spawn: {msg}"),
SpawnError::NameTaken { name } => {
write!(f, "spawn: registry name {name:?} already taken")
}
SpawnError::ThreadSpawnFailed(msg) => {
write!(f, "failed to spawn runtime thread: {msg}")
}
SpawnError::VmInit(msg) => write!(f, "vm init failed: {msg}"),
}
}
}
impl std::error::Error for SpawnError {}
impl From<crate::vm::Fault> for SpawnError {
fn from(fault: crate::vm::Fault) -> Self {
match fault {
crate::vm::Fault::BadFunction { index, table_size } => {
SpawnError::BadFunction { index, table_size }
}
other => SpawnError::VmInit(other.to_string()),
}
}
}