1use std::fmt;
2use std::sync::Arc;
3
4use super::{
5 CoreCapabilityBundle, EngineId, ExecutionContextIdentity, HardwareClassId, InputSignatureEntry,
6 ProviderDeviceIdentity, RuntimeConfigError, StorageClass,
7};
8use tenferro_tensor::{AllocationDomainId, Placement, TensorRead};
9
10#[derive(Debug)]
11pub(super) struct CandidateRegistrationToken;
12
13type InputPlacementPredicate = dyn Fn(&Placement, &StorageClass) -> bool + Send + Sync + 'static;
14type InputSignaturePredicate = dyn Fn(&Placement, Option<&'static str>, Option<AllocationDomainId>, &StorageClass) -> bool
15 + Send
16 + Sync
17 + 'static;
18type InputTensorPredicate =
19 dyn for<'a> Fn(&TensorRead<'a>, &StorageClass) -> bool + Send + Sync + 'static;
20
21#[derive(Clone)]
23pub struct InputPlacementContract(Arc<InputPlacementPredicate>);
24
25impl InputPlacementContract {
26 pub fn new(
44 predicate: impl Fn(&Placement, &StorageClass) -> bool + Send + Sync + 'static,
45 ) -> Self {
46 Self(Arc::new(predicate))
47 }
48
49 fn accepts(&self, placement: &Placement, storage_class: &StorageClass) -> bool {
50 (self.0)(placement, storage_class)
51 }
52}
53
54impl fmt::Debug for InputPlacementContract {
55 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
56 formatter.write_str("InputPlacementContract(..)")
57 }
58}
59
60#[derive(Clone)]
62pub struct InputSignatureContract(Arc<InputSignaturePredicate>);
63
64impl InputSignatureContract {
65 pub fn new(
87 predicate: impl Fn(&Placement, Option<&'static str>, Option<AllocationDomainId>, &StorageClass) -> bool
88 + Send
89 + Sync
90 + 'static,
91 ) -> Self {
92 Self(Arc::new(predicate))
93 }
94
95 fn accepts(&self, input: &InputSignatureEntry, storage_class: &StorageClass) -> bool {
96 (self.0)(
97 input.placement(),
98 input.backend_family(),
99 input.allocation_domain(),
100 storage_class,
101 )
102 }
103}
104
105impl fmt::Debug for InputSignatureContract {
106 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
107 formatter.write_str("InputSignatureContract(..)")
108 }
109}
110
111#[derive(Clone)]
113pub struct RuntimeInputContract(Arc<InputTensorPredicate>);
114
115impl RuntimeInputContract {
116 pub fn new(
127 predicate: impl for<'a> Fn(&TensorRead<'a>, &StorageClass) -> bool + Send + Sync + 'static,
128 ) -> Self {
129 Self(Arc::new(predicate))
130 }
131
132 fn accepts(&self, input: &TensorRead<'_>, storage_class: &StorageClass) -> bool {
133 (self.0)(input, storage_class)
134 }
135}
136
137impl fmt::Debug for RuntimeInputContract {
138 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
139 formatter.write_str("RuntimeInputContract(..)")
140 }
141}
142
143#[derive(Clone)]
145pub struct ResidentOutputContract(Arc<InputTensorPredicate>);
146
147impl ResidentOutputContract {
148 pub fn new(
159 predicate: impl for<'a> Fn(&TensorRead<'a>, &StorageClass) -> bool + Send + Sync + 'static,
160 ) -> Self {
161 Self(Arc::new(predicate))
162 }
163
164 fn accepts(&self, input: &TensorRead<'_>, storage_class: &StorageClass) -> bool {
165 (self.0)(input, storage_class)
166 }
167}
168
169impl fmt::Debug for ResidentOutputContract {
170 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
171 formatter.write_str("ResidentOutputContract(..)")
172 }
173}
174
175#[derive(Clone, Debug)]
177pub struct InputIngressContract {
178 placement: InputPlacementContract,
179 signature: InputSignatureContract,
180 runtime_input: RuntimeInputContract,
181 resident_output: ResidentOutputContract,
182}
183
184impl InputIngressContract {
185 pub fn new(
204 placement: InputPlacementContract,
205 signature: InputSignatureContract,
206 runtime_input: RuntimeInputContract,
207 resident_output: ResidentOutputContract,
208 ) -> Self {
209 Self {
210 placement,
211 signature,
212 runtime_input,
213 resident_output,
214 }
215 }
216
217 fn accepts_placement(&self, placement: &Placement, storage_class: &StorageClass) -> bool {
218 self.placement.accepts(placement, storage_class)
219 }
220
221 fn accepts_signature(&self, input: &InputSignatureEntry, storage_class: &StorageClass) -> bool {
222 self.signature.accepts(input, storage_class)
223 }
224
225 fn accepts_runtime_input(&self, input: &TensorRead<'_>, storage_class: &StorageClass) -> bool {
226 self.runtime_input.accepts(input, storage_class)
227 }
228
229 fn owns_resident_output(&self, input: &TensorRead<'_>, storage_class: &StorageClass) -> bool {
230 self.resident_output.accepts(input, storage_class)
231 }
232}
233
234#[derive(Clone, Debug)]
269pub struct EngineRegistrationMetadata {
270 engine_id: EngineId,
271 provider_device_identity: ProviderDeviceIdentity,
272 hardware_class: HardwareClassId,
273 storage_classes: Arc<[StorageClass]>,
274 default_storage_class: StorageClass,
275 capabilities: CoreCapabilityBundle,
276}
277
278impl EngineRegistrationMetadata {
279 pub fn new(
282 engine_id: EngineId,
283 provider_device_identity: ProviderDeviceIdentity,
284 hardware_class: HardwareClassId,
285 storage_classes: Arc<[StorageClass]>,
286 default_storage_class: StorageClass,
287 capabilities: CoreCapabilityBundle,
288 ) -> Self {
289 Self {
290 engine_id,
291 provider_device_identity,
292 hardware_class,
293 storage_classes,
294 default_storage_class,
295 capabilities,
296 }
297 }
298}
299
300pub struct ExecutableEngineRegistrationConfig {
351 metadata: EngineRegistrationMetadata,
352 context_identity: ExecutionContextIdentity,
353 ingress: InputIngressContract,
354}
355
356impl fmt::Debug for ExecutableEngineRegistrationConfig {
357 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
358 formatter
359 .debug_struct("ExecutableEngineRegistrationConfig")
360 .field("metadata", &self.metadata)
361 .field("context_identity", &self.context_identity)
362 .field("ingress_present", &true)
363 .finish_non_exhaustive()
364 }
365}
366
367impl ExecutableEngineRegistrationConfig {
368 pub fn new(
370 metadata: EngineRegistrationMetadata,
371 context_identity: ExecutionContextIdentity,
372 ingress: InputIngressContract,
373 ) -> Self {
374 Self {
375 metadata,
376 context_identity,
377 ingress,
378 }
379 }
380}
381
382#[derive(Debug)]
383pub struct PreparationOnlyEngineRegistrationConfig {
384 metadata: EngineRegistrationMetadata,
385 context_identity: ExecutionContextIdentity,
386}
387
388impl PreparationOnlyEngineRegistrationConfig {
389 pub fn new(
391 metadata: EngineRegistrationMetadata,
392 context_identity: ExecutionContextIdentity,
393 ) -> Self {
394 Self {
395 metadata,
396 context_identity,
397 }
398 }
399}
400
401#[derive(Clone)]
403pub(crate) struct ExecutableEngineContract {
404 provider_device_identity: ProviderDeviceIdentity,
405 context_identity: ExecutionContextIdentity,
406 capabilities: CoreCapabilityBundle,
407 ingress: InputIngressContract,
408}
409
410impl ExecutableEngineContract {
411 pub(super) fn new(
415 provider_device_identity: ProviderDeviceIdentity,
416 context_identity: ExecutionContextIdentity,
417 capabilities: CoreCapabilityBundle,
418 ingress: InputIngressContract,
419 ) -> Self {
420 Self {
421 provider_device_identity,
422 context_identity,
423 capabilities,
424 ingress,
425 }
426 }
427
428 pub(super) fn capabilities(&self) -> &CoreCapabilityBundle {
429 &self.capabilities
430 }
431
432 pub(super) fn provider_device_identity(&self) -> &ProviderDeviceIdentity {
433 &self.provider_device_identity
434 }
435
436 pub(super) fn context_identity(&self) -> ExecutionContextIdentity {
437 self.context_identity
438 }
439
440 pub(super) fn accepts_input_placement(
441 &self,
442 placement: &Placement,
443 storage_class: &StorageClass,
444 ) -> bool {
445 self.ingress.accepts_placement(placement, storage_class)
446 }
447
448 pub(super) fn accepts_input_signature(
449 &self,
450 input: &InputSignatureEntry,
451 storage_class: &StorageClass,
452 ) -> bool {
453 self.ingress.accepts_signature(input, storage_class)
454 }
455
456 pub(super) fn accepts_runtime_input(
457 &self,
458 input: &TensorRead<'_>,
459 storage_class: &StorageClass,
460 ) -> bool {
461 self.ingress.accepts_runtime_input(input, storage_class)
462 }
463
464 pub(super) fn owns_resident_tensor(
465 &self,
466 input: &TensorRead<'_>,
467 storage_class: &StorageClass,
468 ) -> bool {
469 self.ingress.owns_resident_output(input, storage_class)
470 }
471}
472
473impl fmt::Debug for ExecutableEngineContract {
474 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
475 formatter
476 .debug_struct("ExecutableEngineContract")
477 .field("provider_device_identity", &self.provider_device_identity)
478 .field("context_identity", &self.context_identity)
479 .field("capabilities", &self.capabilities)
480 .field("ingress", &self.ingress)
481 .finish_non_exhaustive()
482 }
483}
484
485pub(crate) struct ProviderExecutableBinding {
487 engine_id: EngineId,
488 hardware_class: HardwareClassId,
489 storage_classes: Arc<[StorageClass]>,
490 default_storage_class: StorageClass,
491 contract: ExecutableEngineContract,
492}
493
494impl ProviderExecutableBinding {
495 pub(super) fn new(
498 engine_id: EngineId,
499 hardware_class: HardwareClassId,
500 storage_classes: Arc<[StorageClass]>,
501 default_storage_class: StorageClass,
502 contract: ExecutableEngineContract,
503 ) -> Result<Self, RuntimeConfigError> {
504 validate_storage_classes(&engine_id, &storage_classes, &default_storage_class)?;
505 Ok(Self {
506 engine_id,
507 hardware_class,
508 storage_classes,
509 default_storage_class,
510 contract,
511 })
512 }
513
514 pub(super) fn engine_id(&self) -> &EngineId {
515 &self.engine_id
516 }
517
518 pub(super) fn hardware_class(&self) -> &HardwareClassId {
519 &self.hardware_class
520 }
521
522 pub(super) fn storage_classes(&self) -> &[StorageClass] {
523 &self.storage_classes
524 }
525
526 pub(super) fn default_storage_class(&self) -> &StorageClass {
527 &self.default_storage_class
528 }
529
530 pub(super) fn contract(&self) -> &ExecutableEngineContract {
531 &self.contract
532 }
533}
534
535impl fmt::Debug for ProviderExecutableBinding {
536 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
537 formatter
538 .debug_struct("ProviderExecutableBinding")
539 .field("engine_id", &self.engine_id)
540 .field("hardware_class", &self.hardware_class)
541 .field("storage_class_count", &self.storage_classes.len())
542 .field("contract", &self.contract)
543 .finish_non_exhaustive()
544 }
545}
546
547impl Clone for ProviderExecutableBinding {
548 fn clone(&self) -> Self {
549 Self {
550 engine_id: self.engine_id.clone(),
551 hardware_class: self.hardware_class.clone(),
552 storage_classes: Arc::clone(&self.storage_classes),
553 default_storage_class: self.default_storage_class.clone(),
554 contract: self.contract.clone(),
555 }
556 }
557}
558
559pub(crate) struct ProviderPreparationBinding {
561 engine_id: EngineId,
562 provider_device_identity: ProviderDeviceIdentity,
563 context_identity: ExecutionContextIdentity,
564 hardware_class: HardwareClassId,
565 storage_classes: Arc<[StorageClass]>,
566 default_storage_class: StorageClass,
567 capabilities: CoreCapabilityBundle,
568}
569
570impl ProviderPreparationBinding {
571 pub(super) fn new(
574 engine_id: EngineId,
575 provider_device_identity: ProviderDeviceIdentity,
576 context_identity: ExecutionContextIdentity,
577 hardware_class: HardwareClassId,
578 storage_classes: Arc<[StorageClass]>,
579 default_storage_class: StorageClass,
580 capabilities: CoreCapabilityBundle,
581 ) -> Result<Self, RuntimeConfigError> {
582 validate_storage_classes(&engine_id, &storage_classes, &default_storage_class)?;
583 Ok(Self {
584 engine_id,
585 provider_device_identity,
586 context_identity,
587 hardware_class,
588 storage_classes,
589 default_storage_class,
590 capabilities,
591 })
592 }
593
594 pub(super) fn engine_id(&self) -> &EngineId {
595 &self.engine_id
596 }
597
598 pub(super) fn provider_device_identity(&self) -> &ProviderDeviceIdentity {
599 &self.provider_device_identity
600 }
601
602 pub(super) fn context_identity(&self) -> ExecutionContextIdentity {
603 self.context_identity
604 }
605
606 pub(super) fn hardware_class(&self) -> &HardwareClassId {
607 &self.hardware_class
608 }
609
610 pub(super) fn storage_classes(&self) -> &[StorageClass] {
611 &self.storage_classes
612 }
613
614 pub(super) fn default_storage_class(&self) -> &StorageClass {
615 &self.default_storage_class
616 }
617
618 pub(super) fn capabilities(&self) -> &CoreCapabilityBundle {
619 &self.capabilities
620 }
621}
622
623impl Clone for ProviderPreparationBinding {
624 fn clone(&self) -> Self {
625 Self {
626 engine_id: self.engine_id.clone(),
627 provider_device_identity: self.provider_device_identity.clone(),
628 context_identity: self.context_identity,
629 hardware_class: self.hardware_class.clone(),
630 storage_classes: Arc::clone(&self.storage_classes),
631 default_storage_class: self.default_storage_class.clone(),
632 capabilities: self.capabilities.clone(),
633 }
634 }
635}
636
637impl fmt::Debug for ProviderPreparationBinding {
638 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
639 formatter
640 .debug_struct("ProviderPreparationBinding")
641 .field("engine_id", &self.engine_id)
642 .field("provider_device_identity", &self.provider_device_identity)
643 .field("context_identity", &self.context_identity)
644 .field("hardware_class", &self.hardware_class)
645 .field("storage_class_count", &self.storage_classes.len())
646 .field("capabilities", &self.capabilities)
647 .finish_non_exhaustive()
648 }
649}
650
651#[derive(Clone, Debug)]
653pub(crate) enum EngineRegistrationState {
654 PreparationOnly { binding: ProviderPreparationBinding },
657 Executable(ProviderExecutableBinding),
659}
660
661impl EngineRegistrationState {
662 pub(super) fn capabilities(&self) -> &CoreCapabilityBundle {
663 match self {
664 Self::PreparationOnly { binding } => binding.capabilities(),
665 Self::Executable(binding) => binding.contract().capabilities(),
666 }
667 }
668
669 pub(super) fn engine_id(&self) -> &EngineId {
670 match self {
671 Self::PreparationOnly { binding } => binding.engine_id(),
672 Self::Executable(binding) => binding.engine_id(),
673 }
674 }
675
676 pub(super) fn provider_device_identity(&self) -> &ProviderDeviceIdentity {
677 match self {
678 Self::PreparationOnly { binding } => binding.provider_device_identity(),
679 Self::Executable(binding) => binding.contract().provider_device_identity(),
680 }
681 }
682
683 pub(super) fn context_identity(&self) -> ExecutionContextIdentity {
684 match self {
685 Self::PreparationOnly { binding } => binding.context_identity(),
686 Self::Executable(binding) => binding.contract().context_identity(),
687 }
688 }
689
690 pub(super) fn hardware_class(&self) -> &HardwareClassId {
691 match self {
692 Self::PreparationOnly { binding } => binding.hardware_class(),
693 Self::Executable(binding) => binding.hardware_class(),
694 }
695 }
696
697 pub(super) fn storage_classes(&self) -> &[StorageClass] {
698 match self {
699 Self::PreparationOnly { binding } => binding.storage_classes(),
700 Self::Executable(binding) => binding.storage_classes(),
701 }
702 }
703
704 pub(super) fn default_storage_class(&self) -> &StorageClass {
705 match self {
706 Self::PreparationOnly { binding } => binding.default_storage_class(),
707 Self::Executable(binding) => binding.default_storage_class(),
708 }
709 }
710}
711
712#[derive(Clone)]
714pub struct EngineRegistration {
715 state: EngineRegistrationState,
716 candidate_token: Arc<CandidateRegistrationToken>,
717}
718
719impl EngineRegistration {
720 pub(super) fn preparation_only(binding: ProviderPreparationBinding) -> Self {
722 Self {
723 state: EngineRegistrationState::PreparationOnly { binding },
724 candidate_token: Arc::new(CandidateRegistrationToken),
725 }
726 }
727
728 pub(super) fn executable(binding: ProviderExecutableBinding) -> Self {
730 Self {
731 state: EngineRegistrationState::Executable(binding),
732 candidate_token: Arc::new(CandidateRegistrationToken),
733 }
734 }
735
736 pub(super) fn from_state(state: EngineRegistrationState) -> Self {
737 Self {
738 state,
739 candidate_token: Arc::new(CandidateRegistrationToken),
740 }
741 }
742
743 #[cfg(test)]
745 pub(crate) fn execution_state(&self) -> &EngineRegistrationState {
746 &self.state
747 }
748
749 pub fn engine_id(&self) -> &EngineId {
751 self.state.engine_id()
752 }
753
754 pub fn provider_device_identity(&self) -> &ProviderDeviceIdentity {
756 self.state.provider_device_identity()
757 }
758
759 pub fn context_identity(&self) -> ExecutionContextIdentity {
761 self.state.context_identity()
762 }
763
764 pub fn hardware_class(&self) -> &HardwareClassId {
766 self.state.hardware_class()
767 }
768
769 pub fn storage_classes(&self) -> &[StorageClass] {
771 self.state.storage_classes()
772 }
773
774 pub(super) fn with_candidate_token(
775 mut self,
776 candidate_token: Arc<CandidateRegistrationToken>,
777 ) -> Self {
778 self.candidate_token = candidate_token;
779 self
780 }
781
782 pub(super) fn into_state_and_token(
783 self,
784 ) -> (EngineRegistrationState, Arc<CandidateRegistrationToken>) {
785 (self.state, self.candidate_token)
786 }
787
788 pub fn default_storage_class(&self) -> &StorageClass {
790 self.state.default_storage_class()
791 }
792
793 pub fn capabilities(&self) -> &CoreCapabilityBundle {
795 self.state.capabilities()
796 }
797
798 pub(super) fn candidate_identical(&self, other: &Self) -> bool {
799 self.engine_id() == other.engine_id()
800 && Arc::ptr_eq(&self.candidate_token, &other.candidate_token)
801 }
802}
803
804pub fn assemble_executable_engine_registration(
817 config: ExecutableEngineRegistrationConfig,
818) -> Result<EngineRegistration, RuntimeConfigError> {
819 let ExecutableEngineRegistrationConfig {
820 metadata:
821 EngineRegistrationMetadata {
822 engine_id,
823 provider_device_identity,
824 hardware_class,
825 storage_classes,
826 default_storage_class,
827 capabilities,
828 },
829 context_identity,
830 ingress,
831 } = config;
832 let contract = ExecutableEngineContract::new(
833 provider_device_identity,
834 context_identity,
835 capabilities,
836 ingress,
837 );
838 let binding = ProviderExecutableBinding::new(
839 engine_id,
840 hardware_class,
841 storage_classes,
842 default_storage_class,
843 contract,
844 )?;
845 Ok(EngineRegistration::executable(binding))
846}
847
848pub fn assemble_preparation_only_engine_registration(
860 config: PreparationOnlyEngineRegistrationConfig,
861) -> Result<EngineRegistration, RuntimeConfigError> {
862 let PreparationOnlyEngineRegistrationConfig {
863 metadata:
864 EngineRegistrationMetadata {
865 engine_id,
866 provider_device_identity,
867 hardware_class,
868 storage_classes,
869 default_storage_class,
870 capabilities,
871 },
872 context_identity,
873 } = config;
874 let binding = ProviderPreparationBinding::new(
875 engine_id,
876 provider_device_identity,
877 context_identity,
878 hardware_class,
879 storage_classes,
880 default_storage_class,
881 capabilities,
882 )?;
883 Ok(EngineRegistration::preparation_only(binding))
884}
885
886impl fmt::Debug for EngineRegistration {
887 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
888 formatter
889 .debug_struct("EngineRegistration")
890 .field("engine_id", self.engine_id())
891 .field("provider_device_identity", self.provider_device_identity())
892 .field("context_identity", &self.context_identity())
893 .field("hardware_class", self.hardware_class())
894 .field("storage_class_count", &self.storage_classes().len())
895 .field("default_storage_class", self.default_storage_class())
896 .field("state", &self.state)
897 .finish_non_exhaustive()
898 }
899}
900
901fn validate_storage_classes(
902 engine_id: &EngineId,
903 storage_classes: &[StorageClass],
904 default_storage_class: &StorageClass,
905) -> Result<(), RuntimeConfigError> {
906 if storage_classes.is_empty() {
907 return Err(RuntimeConfigError::EmptyStorageClasses {
908 engine_id: engine_id.clone(),
909 });
910 }
911 for duplicate_index in 0..storage_classes.len() {
912 if let Some(first_index) = (0..duplicate_index)
913 .find(|&first| storage_classes[first] == storage_classes[duplicate_index])
914 {
915 return Err(RuntimeConfigError::DuplicateStorageClass {
916 engine_id: engine_id.clone(),
917 storage_class: storage_classes[duplicate_index].clone(),
918 first_index,
919 duplicate_index,
920 });
921 }
922 }
923 if !storage_classes
924 .iter()
925 .any(|storage_class| storage_class == default_storage_class)
926 {
927 return Err(RuntimeConfigError::DefaultStorageClassNotListed {
928 engine_id: engine_id.clone(),
929 default_storage_class: default_storage_class.clone(),
930 });
931 }
932 Ok(())
933}