use std::error::Error;
use std::fmt;
use std::sync::Arc;
use super::capability::{
CoreCapabilityKind, PreparationKeySummary, PreparedOperationBinding, UnsupportedReason,
};
use super::identity::{EngineId, ProviderDeviceIdentity, RuntimeEpoch, RuntimeId};
use super::policy::{ProgramPlacementConstraint, StorageClass, TransferEndpoint};
use super::specialization::SpecializationProjection;
use super::{CacheOwnerId, ExtensionModuleId};
use tenferro_ops::ShapeGuardError;
use tenferro_tensor::{AllocationDomainId, Error as TensorError, Placement};
#[derive(Clone, Debug, Eq, PartialEq, thiserror::Error)]
#[error("malformed {kind:?} identifier")]
pub struct IdentityError {
kind: IdentityKind,
}
impl IdentityError {
pub fn kind(&self) -> IdentityKind {
self.kind
}
pub(super) const fn malformed(kind: IdentityKind) -> Self {
Self { kind }
}
}
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
#[non_exhaustive]
pub enum IdentityKind {
Engine,
Provider,
ProviderTarget,
HardwareClass,
StorageClass,
LayoutClass,
CacheOwner,
ExtensionModule,
}
#[derive(Debug, Eq, PartialEq, thiserror::Error)]
#[non_exhaustive]
pub enum PlacementConstraintError {
#[error(
"engine {engine_id:?} is duplicated at positions \
{first_index} and {duplicate_index}"
)]
DuplicateEngine {
engine_id: EngineId,
first_index: usize,
duplicate_index: usize,
},
}
#[derive(Clone, Debug, Eq, PartialEq)]
#[non_exhaustive]
pub enum RegistrationKey {
Engine(EngineId),
ExtensionEngine {
family: &'static str,
engine: EngineId,
},
ExtensionPlanning {
module: ExtensionModuleId,
engine: EngineId,
},
ExtensionCacheOwner {
module: ExtensionModuleId,
owner: CacheOwnerId,
},
TransferProvider {
source: TransferEndpoint,
destination: TransferEndpoint,
},
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
#[non_exhaustive]
pub enum ExecutionPolicyError {}
impl fmt::Display for ExecutionPolicyError {
fn fmt(&self, _formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
match *self {}
}
}
impl std::error::Error for ExecutionPolicyError {}
#[derive(Debug, thiserror::Error)]
#[non_exhaustive]
pub enum RuntimeConfigError {
#[error("runtime identity space exhausted")]
IdentityExhausted,
#[error("malformed {kind:?} identifier")]
MalformedIdentity {
kind: IdentityKind,
},
#[error("engine {engine_id:?} is already registered")]
DuplicateEngine {
engine_id: EngineId,
},
#[error(
"provider/device target {provider_device_identity:?} is already bound to engine \
{first_engine_id:?}; cannot bind engine {duplicate_engine_id:?}"
)]
DuplicateProviderDeviceTarget {
provider_device_identity: ProviderDeviceIdentity,
first_engine_id: EngineId,
duplicate_engine_id: EngineId,
},
#[error(
"engine {engine_id:?} cannot rebind from provider/device target {current:?} \
to {replacement:?}; remove affected transfer routes, remove the old engine, \
register the replacement under the engine ID, and re-register the routes"
)]
EngineTargetRebind {
engine_id: EngineId,
current: ProviderDeviceIdentity,
replacement: ProviderDeviceIdentity,
},
#[error("duplicate {capability:?} capability")]
DuplicateCapability {
capability: CoreCapabilityKind,
},
#[error("conflicting registration {key:?}")]
ConflictingRegistration {
key: RegistrationKey,
},
#[error("engine {engine_id:?} is not registered")]
MissingEngine {
engine_id: EngineId,
},
#[error(
"extension module {module_id:?} family {family_id:?} has no engine registration for runtime engine {engine_id:?}"
)]
MissingExtensionEngine {
module_id: ExtensionModuleId,
family_id: &'static str,
engine_id: EngineId,
},
#[error("engine {engine_id:?} has no storage classes")]
EmptyStorageClasses {
engine_id: EngineId,
},
#[error(
"engine {engine_id:?} storage class {storage_class:?} is duplicated at \
positions {first_index} and {duplicate_index}"
)]
DuplicateStorageClass {
engine_id: EngineId,
storage_class: StorageClass,
first_index: usize,
duplicate_index: usize,
},
#[error("engine {engine_id:?} default storage class {default_storage_class:?} is not listed")]
DefaultStorageClassNotListed {
engine_id: EngineId,
default_storage_class: StorageClass,
},
#[error("transfer endpoint {endpoint:?} references an unregistered engine")]
UnknownTransferEndpointEngine {
endpoint: TransferEndpoint,
},
#[error("transfer endpoint {endpoint:?} uses storage unsupported by its engine")]
UnsupportedTransferEndpointStorage {
endpoint: TransferEndpoint,
},
#[error(
"no transfer provider is registered from source endpoint {source_endpoint:?} to destination \
endpoint {destination:?}"
)]
MissingTransferProvider {
source_endpoint: TransferEndpoint,
destination: TransferEndpoint,
},
#[error(
"stale transfer route from source endpoint {source_endpoint:?} to destination endpoint \
{destination:?}: endpoint {endpoint:?} was registered for {registered:?} but \
now resolves to {current:?}"
)]
StaleTransferRoute {
source_endpoint: TransferEndpoint,
destination: TransferEndpoint,
endpoint: TransferEndpoint,
registered: Box<ProviderDeviceIdentity>,
current: Box<ProviderDeviceIdentity>,
},
#[error(
"transfer route from source endpoint {source_endpoint:?} to destination endpoint {destination:?} \
has no resolved provider/device binding"
)]
UnboundTransferRoute {
source_endpoint: TransferEndpoint,
destination: TransferEndpoint,
},
#[error("bound candidate invariant failed for transfer endpoint {endpoint:?}")]
BoundCandidateInvariant {
endpoint: TransferEndpoint,
},
#[error("engine {engine_id:?} execution bridge has no complete input ingress validator")]
MissingInputIngressValidator {
engine_id: EngineId,
},
#[error("engine {engine_id:?} context identity mismatch")]
ContextIdentityMismatch {
engine_id: EngineId,
},
#[error("invalid execution policy: {reason}")]
InvalidExecutionPolicy {
reason: ExecutionPolicyError,
},
#[error("extension module registration failed")]
ExtensionModule {
#[source]
source: ExtensionModuleError,
},
}
#[derive(Debug, thiserror::Error)]
#[non_exhaustive]
pub enum EngineExecutionContractError {
#[error(
"instruction {instruction_index} output slot {output_slot} is not resident in \
engine {engine_id:?} storage {storage_class:?} \
(backend family {backend_family:?}, allocation domain {allocation_domain:?})"
)]
OutputResidencyMismatch {
instruction_index: usize,
output_slot: usize,
engine_id: EngineId,
storage_class: StorageClass,
backend_family: Option<&'static str>,
allocation_domain: Option<AllocationDomainId>,
},
}
#[derive(Debug, thiserror::Error)]
#[non_exhaustive]
pub enum InputIngressContractError {
#[error(
"input slot {input_slot} at placement {placement:?} with backend family \
{backend_family:?} and allocation domain {allocation_domain:?} is not accepted by \
ingress engine {ingress_engine_id:?} storage {ingress_storage_class:?}"
)]
ResidencyMismatch {
input_slot: usize,
ingress_engine_id: EngineId,
ingress_storage_class: StorageClass,
placement: Placement,
backend_family: Option<&'static str>,
allocation_domain: Option<AllocationDomainId>,
},
}
#[derive(Debug, thiserror::Error)]
#[non_exhaustive]
pub enum SubmissionError {
#[error("failed to spawn runtime submission worker")]
WorkerSpawn {
#[source]
source: std::io::Error,
},
}
impl From<IdentityError> for RuntimeConfigError {
fn from(source: IdentityError) -> Self {
Self::MalformedIdentity {
kind: source.kind(),
}
}
}
#[derive(Debug, thiserror::Error)]
#[non_exhaustive]
pub enum RuntimeReconfigureError {
#[error("runtime state failed")]
State {
#[source]
source: super::RuntimeStateError,
},
#[error("runtime reconfiguration edit failed")]
Edit {
#[source]
source: RuntimeConfigError,
},
#[error("runtime was concurrently reconfigured from {base:?} to {current:?}")]
ConcurrentReconfiguration {
base: RuntimeEpoch,
current: RuntimeEpoch,
},
#[error("runtime epoch exhausted at {current:?}")]
EpochExhausted {
current: RuntimeEpoch,
},
}
#[derive(Debug, thiserror::Error)]
#[non_exhaustive]
pub enum ExtensionModuleError {
#[error("extension module {module_id:?} is already installed")]
ConflictingModule {
module_id: ExtensionModuleId,
},
#[error("extension module {module_id:?} has conflicting engine {family_id:?}/{engine_id:?}")]
ConflictingEngine {
module_id: ExtensionModuleId,
family_id: &'static str,
engine_id: EngineId,
},
#[error(
"extension module {module_id:?} registered planning config without engine {engine_id:?}"
)]
PlanningConfigWithoutEngine {
module_id: ExtensionModuleId,
engine_id: EngineId,
},
#[error(
"extension module {module_id:?} config for engine {engine_id:?} has family \
{actual:?}, expected {expected:?}"
)]
PlanningConfigFamilyMismatch {
module_id: ExtensionModuleId,
engine_id: EngineId,
expected: &'static str,
actual: &'static str,
},
#[error("extension module {module_id:?} engine {engine_id:?} has no planning config")]
MissingPlanningConfig {
module_id: ExtensionModuleId,
engine_id: EngineId,
},
#[error("extension module {module_id:?} has conflicting planning config for {engine_id:?}")]
ConflictingPlanningConfig {
module_id: ExtensionModuleId,
engine_id: EngineId,
},
#[error("extension module {module_id:?} has conflicting cache owner {owner:?}")]
ConflictingCacheOwner {
module_id: ExtensionModuleId,
owner: CacheOwnerId,
},
}
#[derive(Debug, thiserror::Error)]
pub enum InputSignatureError {
#[error("shape rank {rank} does not match stride count {stride_count}")]
ShapeStrideRankMismatch {
rank: usize,
stride_count: usize,
},
#[error("alignment class {alignment_log2} is outside the usize alignment lattice")]
InvalidAlignmentClass {
alignment_log2: u8,
},
#[error("input {input} metadata is unavailable")]
TensorMetadata {
input: usize,
source: TensorError,
},
}
#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
pub enum RankRequirement {
ConcreteAxis {
axis: u32,
},
ExactStrides,
}
impl std::fmt::Display for RankRequirement {
fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::ConcreteAxis { axis } => write!(formatter, "concrete axis {axis}"),
Self::ExactStrides => formatter.write_str("exact strides"),
}
}
}
#[derive(Clone, Debug, Eq, PartialEq, thiserror::Error)]
pub enum InputSpecializationRequirementsError {
#[error("axis {axis} is duplicated at positions {first_index} and {duplicate_index}")]
DuplicateAxis {
axis: u32,
first_index: usize,
duplicate_index: usize,
},
#[error("{reason} requires rank specialization")]
RankRequired {
reason: RankRequirement,
},
#[error("alignment class {alignment_log2} is outside the usize alignment lattice")]
InvalidAlignmentClass {
alignment_log2: u8,
},
}
#[derive(Clone, Debug, Eq, PartialEq, thiserror::Error)]
pub enum SpecializationError {
#[error("expected {expected} inputs but got {actual}")]
WrongInputCount {
expected: usize,
actual: usize,
},
#[error("input {input} axis {axis} is outside rank {rank}")]
AxisOutOfRange {
input: usize,
axis: u32,
rank: usize,
},
#[error("input {input} has unknown alignment; required class {required_alignment_log2}")]
AlignmentUnavailable {
input: usize,
required_alignment_log2: u8,
},
#[error("input {input} rank {rank} is too large to project into u32 axes")]
ProjectionOverflow {
input: usize,
rank: usize,
},
}
#[derive(Clone, Copy, Debug, Eq, PartialEq, thiserror::Error)]
#[error("execution context mismatch: expected {expected:?}, actual {actual:?}")]
pub struct ExecutionContextMismatch {
pub expected: super::ExecutionContextIdentity,
pub actual: super::ExecutionContextIdentity,
}
#[derive(Clone, Debug, Eq, PartialEq, thiserror::Error)]
pub enum ProviderContractError {
#[error("operation {operation:?} is not in {expected:?}")]
WrongOperationFamily {
expected: CoreCapabilityKind,
operation: &'static str,
},
#[error("prepared binding mismatch")]
BindingMismatch {
expected: Box<PreparedOperationBinding>,
actual: Box<PreparedOperationBinding>,
},
#[error("prepared specialization projection mismatch")]
ProjectionMismatch {
expected: Box<SpecializationProjection>,
actual: Box<SpecializationProjection>,
},
#[error("invalid provider specialization: {source}")]
InvalidSpecialization {
#[source]
source: SpecializationError,
},
#[error("specialization did not strictly widen the current requirements")]
NonWideningSpecialization {
previous: Box<super::SpecializationRequirements>,
next: Box<super::SpecializationRequirements>,
},
#[error("specialization retry limit exceeded after {attempts} attempts; limit was {limit}")]
SpecializationRetryLimitExceeded {
attempts: usize,
limit: usize,
},
}
#[derive(Debug, thiserror::Error)]
pub enum PrepareError {
#[error("runtime mismatch: expected {expected:?}, actual {actual:?}")]
RuntimeMismatch {
expected: RuntimeId,
actual: RuntimeId,
},
#[error("stale runtime epoch: prepared {prepared:?}, current {current:?}")]
StaleEpoch {
prepared: RuntimeEpoch,
current: RuntimeEpoch,
},
#[error("input signature failed")]
InputSignature {
source: InputSignatureError,
},
#[error("shape guard failed")]
ShapeGuard {
#[source]
source: ShapeGuardError,
},
#[error("specialization failed")]
Specialization {
source: SpecializationError,
},
#[error("no eligible engine for {constraint:?}")]
NoEligibleEngine {
constraint: ProgramPlacementConstraint,
},
#[error(
"engine {engine_id:?} has a matching preparation capability but no executable binding"
)]
NoExecutableEngine {
engine_id: EngineId,
},
#[error("missing extension family {family_id:?}")]
MissingExtension {
family_id: &'static str,
},
#[error("no eligible input ingress for input {input_index} at placement {placement:?}")]
NoInputIngress {
input_index: usize,
placement: tenferro_tensor::Placement,
},
#[error(
"placement resolution exhausted its search budget after {attempts} attempts \
(limit {limit})"
)]
DispatchSearchBudgetExceeded {
attempts: usize,
limit: usize,
},
#[error(
"instruction {instruction_index} has no direct transfer provider for value slot \
{value_slot} from {available_source_endpoints:?} to {destination_endpoint:?}"
)]
MissingTransferProvider {
instruction_index: usize,
value_slot: usize,
destination_endpoint: TransferEndpoint,
available_source_endpoints: Vec<TransferEndpoint>,
},
#[error("resolved engine {engine_id:?} is unavailable in the preparation snapshot")]
ResolvedEngineUnavailable {
engine_id: EngineId,
},
#[error("operation is unsupported: {reason}")]
Unsupported {
reason: UnsupportedReason,
},
#[error("determinism unsupported by engine {engine_id:?}")]
DeterminismUnsupported {
engine_id: EngineId,
},
#[error("provider contract violation: {source}")]
ProviderContract {
#[source]
source: ProviderContractError,
},
#[error("preparation cycle at {key:?}")]
PreparationCycle {
key: PreparationKeySummary,
},
#[error("nested preparation unsupported: parent {parent:?}, requested {requested:?}")]
NestedPreparationUnsupported {
parent: PreparationKeySummary,
requested: PreparationKeySummary,
},
#[error(
"preparation cache is at capacity: {in_flight} in flight and \
{queued_distinct_keys} queued distinct keys"
)]
CacheInFlightCapacityExceeded {
in_flight: usize,
queued_distinct_keys: usize,
},
#[error("preparation cache state unavailable: {source}")]
CacheState {
#[source]
source: super::RuntimeStateError,
},
#[error("engine preparation failed: {source}")]
Engine {
#[source]
source: Arc<dyn Error + Send + Sync>,
},
}