use std::collections::BTreeMap;
use std::fmt;
use std::sync::Arc;
use tenferro_tensor::{AllocationDomainId, DType, Placement, Tensor, TensorRead};
use super::schedule::{EventDomainId, ExecutionLocation};
use super::{EngineId, ProviderDeviceIdentity, StorageClass, TransferEndpoint};
#[derive(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
pub(crate) struct TransferRoute {
source: TransferEndpoint,
destination: TransferEndpoint,
}
impl TransferRoute {
pub(crate) fn new(source: TransferEndpoint, destination: TransferEndpoint) -> Self {
Self {
source,
destination,
}
}
pub(crate) fn source(&self) -> &TransferEndpoint {
&self.source
}
pub(crate) fn destination(&self) -> &TransferEndpoint {
&self.destination
}
}
#[derive(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
pub(crate) struct ResolvedTransferEndpoint {
logical: TransferEndpoint,
provider_device_identity: ProviderDeviceIdentity,
event_domain_id: EventDomainId,
}
impl ResolvedTransferEndpoint {
pub(crate) fn new(
logical: TransferEndpoint,
provider_device_identity: ProviderDeviceIdentity,
event_domain_id: EventDomainId,
) -> Self {
Self {
logical,
provider_device_identity,
event_domain_id,
}
}
pub(crate) fn logical(&self) -> &TransferEndpoint {
&self.logical
}
pub(crate) fn provider_device_identity(&self) -> &ProviderDeviceIdentity {
&self.provider_device_identity
}
pub(crate) fn event_domain_id(&self) -> EventDomainId {
self.event_domain_id
}
}
#[derive(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
pub(crate) struct ResolvedTransferRoute {
source: ResolvedTransferEndpoint,
destination: ResolvedTransferEndpoint,
}
impl ResolvedTransferRoute {
pub(crate) fn new(
source: ResolvedTransferEndpoint,
destination: ResolvedTransferEndpoint,
) -> Self {
Self {
source,
destination,
}
}
pub(crate) fn source(&self) -> &ResolvedTransferEndpoint {
&self.source
}
pub(crate) fn destination(&self) -> &ResolvedTransferEndpoint {
&self.destination
}
}
#[derive(Clone, Debug)]
pub(crate) struct FrozenTransferRegistry {
routes: Arc<BTreeMap<ResolvedTransferRoute, Arc<dyn TransferProvider>>>,
}
impl FrozenTransferRegistry {
pub(crate) fn new(routes: BTreeMap<ResolvedTransferRoute, Arc<dyn TransferProvider>>) -> Self {
Self {
routes: Arc::new(routes),
}
}
pub(crate) fn iter(
&self,
) -> impl Iterator<Item = (&ResolvedTransferRoute, &Arc<dyn TransferProvider>)> {
self.routes.iter()
}
pub(crate) fn len(&self) -> usize {
self.routes.len()
}
pub(crate) fn contains(&self, route: &ResolvedTransferRoute) -> bool {
self.routes.contains_key(route)
}
pub(crate) fn get(&self, route: &ResolvedTransferRoute) -> Option<&Arc<dyn TransferProvider>> {
self.routes.get(route)
}
}
pub trait TransferProvider: fmt::Debug + Send + Sync + 'static {
fn transfer_blocking(&self, request: TransferRequest<'_>) -> crate::Result<Tensor>;
}
#[derive(Debug)]
pub struct TransferRequest<'a> {
source_location: &'a ExecutionLocation,
destination_location: &'a ExecutionLocation,
input: TensorRead<'a>,
}
impl<'a> TransferRequest<'a> {
pub(crate) fn new(
source_location: &'a ExecutionLocation,
destination_location: &'a ExecutionLocation,
input: TensorRead<'a>,
) -> Self {
Self {
source_location,
destination_location,
input,
}
}
pub fn source_engine_id(&self) -> &'a EngineId {
self.source_location.engine_id()
}
pub fn source_provider_device_identity(&self) -> &'a ProviderDeviceIdentity {
self.source_location.provider_device_identity()
}
pub fn source_event_domain_id(&self) -> EventDomainId {
self.source_location.event_domain_id()
}
pub fn source_storage_class(&self) -> &'a StorageClass {
self.source_location.storage_class()
}
pub fn destination_engine_id(&self) -> &'a EngineId {
self.destination_location.engine_id()
}
pub fn destination_provider_device_identity(&self) -> &'a ProviderDeviceIdentity {
self.destination_location.provider_device_identity()
}
pub fn destination_event_domain_id(&self) -> EventDomainId {
self.destination_location.event_domain_id()
}
pub fn destination_storage_class(&self) -> &'a StorageClass {
self.destination_location.storage_class()
}
pub fn input(&self) -> &TensorRead<'a> {
&self.input
}
}
#[derive(Debug, thiserror::Error)]
#[non_exhaustive]
pub enum TransferError {
#[error(
"no transfer provider registered from source endpoint {source_endpoint:?} \
(event domain {source_event_domain_id:?}) to destination endpoint {destination_endpoint:?} \
(event domain {destination_event_domain_id:?})"
)]
MissingProvider {
source_endpoint: TransferEndpoint,
source_event_domain_id: EventDomainId,
destination_endpoint: TransferEndpoint,
destination_event_domain_id: EventDomainId,
},
#[error("transfer provider returned an invalid tensor")]
ProviderContract {
#[source]
source: TransferProviderContractError,
},
}
#[derive(Debug, thiserror::Error)]
#[non_exhaustive]
pub enum TransferProviderContractError {
#[error("transfer source logical element count is invalid")]
LogicalElementCount {
#[source]
source: tenferro_tensor::Error,
},
#[error("transfer output dtype mismatch: expected {expected:?}, actual {actual:?}")]
DTypeMismatch {
expected: DType,
actual: DType,
},
#[error("transfer output shape mismatch: expected {expected:?}, actual {actual:?}")]
ShapeMismatch {
expected: Vec<usize>,
actual: Vec<usize>,
},
#[error(
"transfer output placement {actual:?} is incompatible with destination storage \
{destination_storage_class:?} on engine {destination_engine_id:?}"
)]
DestinationPlacementMismatch {
destination_engine_id: EngineId,
destination_storage_class: StorageClass,
actual: Placement,
},
#[error(
"transfer output storage family {actual_backend_family:?} and allocation domain \
{actual_allocation_domain:?} are not resident in destination storage \
{destination_storage_class:?} on engine {destination_engine_id:?}"
)]
DestinationResidencyMismatch {
destination_engine_id: EngineId,
destination_storage_class: StorageClass,
actual_backend_family: Option<&'static str>,
actual_allocation_domain: Option<AllocationDomainId>,
},
#[error(
"transfer output buffer length mismatch: shape requires {expected} elements, \
buffer reports {actual}"
)]
InvalidBufferLength {
expected: usize,
actual: usize,
},
}