Skip to main content

tenferro_runtime/runtime/
execution.rs

1//! Runtime-owned compiled-graph execution boundary.
2//!
3//! This module owns the private execution bridge used by
4//! `Runtime::run_compiled*`.
5
6use num_complex::{Complex32, Complex64};
7use std::collections::{HashMap, HashSet};
8use std::error::Error as StdError;
9use std::fmt;
10use std::panic::{catch_unwind, AssertUnwindSafe};
11use std::sync::{Arc, Condvar, Mutex, MutexGuard};
12use std::thread::{self, ThreadId};
13
14use smallvec::SmallVec;
15use tenferro_ops::shape_extent::ShapeExtent;
16use tenferro_tensor::{
17    AllocationGroup, DType, DescriptorSlot, GroupError, MemoryKind, Tensor, TensorBackend,
18    TensorRead, TensorValue, TensorView,
19};
20
21use crate::error::ErrorPhase;
22use crate::exec::{
23    DispatchMode, ExecInstruction, ExecProgram, ExecSlot, ExtensionExecutionDispatch,
24};
25use crate::extension_cache::{ExtensionCacheSelector, ExtensionCacheStore};
26use crate::graph::CompiledGraph;
27use crate::runtime::schedule::{
28    EventDependency, ExecutionLocation, ScheduledGraph, ScheduledNode, ScheduledNodeKind,
29    ScheduledTransfer, UnsupportedScheduledNodeError,
30};
31use crate::runtime::{
32    CacheOwnerError, CacheStats, EventDomainError, EventDomainOperation, EventDomainRun,
33    EventToken, InputSignature, PrepareError, PrepareOptions, PreparedOperationPlan, Runtime,
34    RuntimeCacheOwner, SubmissionError, TransferError, TransferProviderContractError,
35    TransferRequest,
36};
37use crate::{Error, Result};
38
39type RuntimeInputReads<'a> = SmallVec<[TensorRead<'a>; 8]>;
40type RuntimeInputShapes<'a> = SmallVec<[&'a [usize]; 8]>;
41type RuntimeShapeScratch = SmallVec<[usize; 8]>;
42
43#[derive(Clone, Copy, Debug)]
44pub(super) enum RuntimeOutputMode {
45    Tensor,
46    Value,
47}
48
49/// Runtime-prepared compiled graph execution handle.
50///
51/// This handle keeps the prepared execution staging and its direct provider
52/// witnesses inside the immutable schedule. It is tied to the runtime epoch
53/// that created it and becomes stale after runtime reconfiguration.
54#[derive(Clone)]
55pub struct PreparedCompiledGraph {
56    runtime_id: super::RuntimeId,
57    epoch: super::RuntimeEpoch,
58    program: CompiledGraph,
59    prepared: Arc<super::preparation::PreparedProgram>,
60}
61
62impl PreparedCompiledGraph {
63    /// Number of planned elementwise regions and the instructions they cover,
64    /// for execution-path parity tests.
65    #[doc(hidden)]
66    #[must_use]
67    pub fn elementwise_region_summary(&self) -> (usize, usize) {
68        super::region::region_summary(self.prepared.root().regions())
69    }
70
71    /// Number of planned elementwise regions executed as one fused command, and
72    /// the number that fell back to per-instruction dispatch.
73    #[doc(hidden)]
74    #[must_use]
75    pub fn elementwise_region_execution_counts(&self) -> (usize, usize) {
76        let counters = self.prepared.root().region_counters();
77        (counters.fused(), counters.fallbacks())
78    }
79
80    /// Number of successful scheduled event-domain submissions observed by this
81    /// prepared program's production executor.
82    ///
83    /// This is runtime submission evidence, rather than a plan-derived census.
84    #[doc(hidden)]
85    #[must_use]
86    pub fn execution_submission_count(&self) -> usize {
87        self.prepared.root().region_counters().submissions()
88    }
89
90    /// Plan-derived command count for the production execution path, for
91    /// execution-path tests: scheduled operations that no region covers, plus
92    /// one command per planned region.
93    ///
94    /// This is a plan census, not a runtime submission count.
95    #[doc(hidden)]
96    #[must_use]
97    pub fn execution_command_count(&self) -> usize {
98        let root = self.prepared.root();
99        super::region::command_count(root.schedule(), root.regions())
100    }
101}
102
103/// Asynchronous runtime execution handle returned by [`Runtime::submit`].
104pub struct ExecutionHandle {
105    submission: Arc<InFlightSubmission>,
106}
107
108/// Move-only input package for detached runtime submission.
109///
110/// # Examples
111///
112/// ```
113/// use tenferro_runtime::runtime::ExecutionInputs;
114///
115/// let inputs = ExecutionInputs::new(Vec::new())?;
116/// assert!(format!("{inputs:?}").contains("ExecutionInputs"));
117/// # Ok::<(), tenferro_runtime::Error>(())
118/// ```
119pub struct ExecutionInputs {
120    group: AllocationGroup,
121    bindings: Box<[DescriptorSlot]>,
122}
123
124impl ExecutionInputs {
125    /// Construct a package from already-owned tensors.
126    ///
127    /// # Errors
128    ///
129    /// Returns [`Error::RuntimeState`] when a tensor binding cannot be
130    /// registered in the allocation group.
131    pub fn new(tensors: Vec<Tensor>) -> Result<Self> {
132        let (group, bindings) = AllocationGroup::from_tensors(tensors).map_err(|error| {
133            Error::runtime_state(
134                "ExecutionInputs::new",
135                ErrorPhase::Execution,
136                error.to_string(),
137            )
138        })?;
139        Ok(Self { group, bindings })
140    }
141
142    pub(crate) fn as_reads(&self) -> Result<Vec<TensorRead<'_>>> {
143        self.group.read_views(&self.bindings).map_err(|error| {
144            Error::runtime_state(
145                "ExecutionInputs::as_reads",
146                ErrorPhase::Execution,
147                error.to_string(),
148            )
149        })
150    }
151}
152
153impl fmt::Debug for ExecutionInputs {
154    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
155        formatter
156            .debug_struct("ExecutionInputs")
157            .field("len", &self.bindings.len())
158            .finish()
159    }
160}
161
162/// Borrowed immutable inputs for synchronous, retirement-before-return execution.
163pub struct ScopedReadInputs<'env> {
164    bindings: Box<[ScopedReadBinding<'env>]>,
165}
166
167impl<'env> ScopedReadInputs<'env> {
168    /// Build a scoped input package from borrowed dtype-erased views.
169    pub fn new(bindings: Vec<TensorView<'env>>) -> Self {
170        Self {
171            bindings: bindings
172                .into_iter()
173                .map(|tensor| ScopedReadBinding { tensor })
174                .collect(),
175        }
176    }
177
178    fn as_reads(&self) -> Vec<TensorRead<'env>> {
179        self.bindings
180            .iter()
181            .map(|binding| TensorRead::from_view(binding.tensor.clone()))
182            .collect()
183    }
184
185    fn has_non_host_provider(&self) -> bool {
186        self.bindings.iter().any(|binding| {
187            !matches!(
188                binding.tensor.placement().memory_kind,
189                MemoryKind::PinnedHost | MemoryKind::UnpinnedHost
190            )
191        })
192    }
193
194    /// Return the number of borrowed bindings.
195    pub fn len(&self) -> usize {
196        self.bindings.len()
197    }
198
199    /// Return whether no borrowed bindings were supplied.
200    pub fn is_empty(&self) -> bool {
201        self.bindings.is_empty()
202    }
203}
204
205/// One immutable borrowed binding in a scoped input package.
206pub struct ScopedReadBinding<'env> {
207    tensor: TensorView<'env>,
208}
209
210impl<'env> ScopedReadBinding<'env> {
211    /// Wrap one immutable dtype-erased view.
212    pub fn new(tensor: TensorView<'env>) -> Self {
213        Self { tensor }
214    }
215
216    /// Borrow the binding's dtype-erased view.
217    pub fn tensor(&self) -> &TensorView<'env> {
218        &self.tensor
219    }
220}
221
222impl fmt::Debug for ScopedReadInputs<'_> {
223    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
224        formatter
225            .debug_struct("ScopedReadInputs")
226            .field("len", &self.bindings.len())
227            .finish()
228    }
229}
230
231/// A synchronous scoped execution result.
232// INVARIANT: these variants keep the borrowed execution contract allocation-free
233// at the API boundary; boxing would add an avoidable ownership allocation.
234#[allow(clippy::large_enum_variant)]
235#[derive(Debug)]
236pub enum ScopedExecutionOutcome<'env> {
237    /// All provider work retired before this call returned.
238    Completed(ScopedExecutionBundle<'env>),
239    /// Execution retired with an ordinary error and the original borrows remain available.
240    RetiredFailed {
241        error: Error,
242        inputs: ScopedReadInputs<'env>,
243    },
244}
245
246/// Metadata for a storage-free scoped output.
247#[derive(Clone, Debug, PartialEq, Eq)]
248pub struct OutputMetadata {
249    /// Scalar dtype of the logical output.
250    pub dtype: DType,
251    /// Logical shape of the output.
252    pub shape: Box<[usize]>,
253}
254
255/// Owned outputs and borrowed identity views from one completed scoped call.
256pub struct ScopedExecutionBundle<'env> {
257    owned: AllocationGroup,
258    outputs: Box<[ScopedOutput<'env>]>,
259}
260
261impl fmt::Debug for ScopedExecutionBundle<'_> {
262    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
263        formatter
264            .debug_struct("ScopedExecutionBundle")
265            .field("len", &self.outputs.len())
266            .finish()
267    }
268}
269
270impl<'env> ScopedExecutionBundle<'env> {
271    /// Borrow one completed output.
272    ///
273    /// # Errors
274    ///
275    /// Returns [`OutputAccessError::InvalidOutput`] for an invalid output
276    /// index or [`OutputAccessError::Group`] when the completed descriptor
277    /// group reports an output access failure.
278    pub fn output(&self, index: usize) -> std::result::Result<OutputRef<'_>, OutputAccessError> {
279        let output = self
280            .outputs
281            .get(index)
282            .ok_or(OutputAccessError::InvalidOutput { index })?;
283        match output {
284            ScopedOutput::Borrowed(view) => Ok(OutputRef::Tensor(view.clone())),
285            ScopedOutput::Owned(slot) => output_ref_from_group(&self.owned, *slot),
286            ScopedOutput::Metadata(metadata) => Ok(OutputRef::Metadata(metadata)),
287        }
288    }
289
290    /// Consume the bundle and extract a fresh owned output.
291    ///
292    /// # Errors
293    ///
294    /// Returns [`ScopedOutputExtractError::InvalidOutput`],
295    /// [`ScopedOutputExtractError::BorrowedOutput`], or
296    /// [`ScopedOutputExtractError::MetadataOutput`] for an output that cannot
297    /// be consumed, and [`ScopedOutputExtractError::Output`] for a structural
298    /// group extraction failure. Each error returns the unchanged bundle.
299    #[allow(clippy::result_large_err)]
300    pub fn into_owned_output(
301        self,
302        index: usize,
303    ) -> std::result::Result<Tensor, (Self, ScopedOutputExtractError)> {
304        let slot = match self.outputs.get(index) {
305            Some(ScopedOutput::Owned(slot)) => *slot,
306            Some(ScopedOutput::Borrowed(_)) => {
307                return Err((self, ScopedOutputExtractError::BorrowedOutput))
308            }
309            Some(ScopedOutput::Metadata(_)) => {
310                return Err((self, ScopedOutputExtractError::MetadataOutput))
311            }
312            None => return Err((self, ScopedOutputExtractError::InvalidOutput { index })),
313        };
314        let Self { owned, outputs } = self;
315        match owned.into_tensor(slot) {
316            Ok(tensor) => Ok(tensor),
317            Err((owned, error)) => Err((
318                Self { owned, outputs },
319                ScopedOutputExtractError::Output(OutputExtractError::Group(error)),
320            )),
321        }
322    }
323}
324
325/// Output classification used by scoped providers.
326// INVARIANT: borrowed views stay inline so scoped output classification does
327// not materialize an extra owner or heap allocation.
328#[allow(clippy::large_enum_variant)]
329#[derive(Debug)]
330pub enum ScopedOutput<'env> {
331    /// A result that aliases an immutable caller input.
332    Borrowed(TensorView<'env>),
333    /// A fresh result owned by the scoped bundle's allocation group.
334    Owned(DescriptorSlot),
335    /// Metadata-only output with no tensor payload.
336    Metadata(OutputMetadata),
337}
338
339/// Failure while consuming a scoped output.
340#[derive(Debug, thiserror::Error)]
341pub enum ScopedOutputExtractError {
342    #[error("scoped output index {index} is outside the output set")]
343    InvalidOutput { index: usize },
344    #[error("scoped output is borrowed from the caller")]
345    BorrowedOutput,
346    #[error("scoped output contains metadata only")]
347    MetadataOutput,
348    #[error("scoped owned output cannot be extracted: {0}")]
349    Output(#[from] OutputExtractError),
350}
351
352/// A pre-admission scoped submission rejection. The original borrows remain available.
353#[derive(Debug)]
354pub struct ScopedSubmitRejected<'env> {
355    source: Box<Error>,
356    inputs: ScopedReadInputs<'env>,
357}
358
359impl<'env> ScopedSubmitRejected<'env> {
360    /// Consume the rejection and preserve its original environment lifetime.
361    pub fn into_parts(self) -> (Error, ScopedReadInputs<'env>) {
362        (*self.source, self.inputs)
363    }
364}
365
366impl fmt::Display for ScopedSubmitRejected<'_> {
367    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
368        write!(
369            formatter,
370            "scoped submission rejected before admission: {}",
371            self.source
372        )
373    }
374}
375
376impl StdError for ScopedSubmitRejected<'_> {
377    fn source(&self) -> Option<&(dyn StdError + 'static)> {
378        Some(self.source.as_ref())
379    }
380}
381
382/// A detached execution result retaining one allocation group for all outputs.
383#[derive(Debug)]
384pub struct ExecutionBundle {
385    group: AllocationGroup,
386    outputs: Box<[DescriptorSlot]>,
387}
388
389/// A borrowed output view from an [`ExecutionBundle`].
390// INVARIANT: this read-only result is a zero-allocation projection of an
391// already-prepared view; boxing would add work on the output hot path.
392#[allow(clippy::large_enum_variant)]
393#[derive(Debug)]
394pub enum OutputRef<'a> {
395    Tensor(TensorView<'a>),
396    Metadata(&'a OutputMetadata),
397}
398
399#[derive(Debug, thiserror::Error)]
400pub enum OutputAccessError {
401    #[error("execution output index {index} is outside the output set")]
402    InvalidOutput { index: usize },
403    #[error("execution output group is invalid: {0}")]
404    Group(#[from] GroupError),
405}
406
407#[derive(Debug, thiserror::Error)]
408pub enum OutputExtractError {
409    #[error("execution output index {index} is outside the output set")]
410    InvalidOutput { index: usize },
411    #[error("execution output cannot be extracted: {0}")]
412    Group(#[from] GroupError),
413}
414
415impl ExecutionBundle {
416    fn from_inputs_and_outputs(inputs: ExecutionInputs, outputs: Vec<Tensor>) -> Result<Self> {
417        let mut group = inputs.group;
418        let mut output_slots = Vec::with_capacity(outputs.len());
419        for output in outputs {
420            let slot = group.append_tensor(output).map_err(|error| {
421                Error::runtime_state(
422                    "ExecutionBundle::from_inputs_and_outputs",
423                    ErrorPhase::Execution,
424                    error.to_string(),
425                )
426            })?;
427            output_slots.push(slot);
428        }
429        Ok(Self {
430            group,
431            outputs: output_slots.into_boxed_slice(),
432        })
433    }
434
435    #[cfg(test)]
436    pub(super) fn from_outputs(outputs: Vec<Tensor>) -> Result<Self> {
437        let (group, bindings) = AllocationGroup::from_tensors(outputs).map_err(|error| {
438            Error::runtime_state(
439                "ExecutionBundle::from_outputs",
440                ErrorPhase::Execution,
441                error.to_string(),
442            )
443        })?;
444        Ok(Self {
445            group,
446            outputs: bindings,
447        })
448    }
449
450    /// # Errors
451    ///
452    /// Returns [`OutputAccessError::InvalidOutput`] for an invalid output
453    /// index or [`OutputAccessError::Group`] when the output descriptor group
454    /// reports an access failure.
455    pub fn output(&self, index: usize) -> std::result::Result<OutputRef<'_>, OutputAccessError> {
456        let slot = *self
457            .outputs
458            .get(index)
459            .ok_or(OutputAccessError::InvalidOutput { index })?;
460        let mut views = self.group.read_views(std::slice::from_ref(&slot))?;
461        let view = views
462            .pop()
463            .ok_or(OutputAccessError::InvalidOutput { index })?;
464        match view {
465            TensorRead::View(view) => Ok(OutputRef::Tensor(view)),
466            TensorRead::Tensor(_) => Err(OutputAccessError::InvalidOutput { index }),
467        }
468    }
469
470    // INVARIANT: extraction errors return the unchanged move-only bundle so
471    // callers can retry or inspect it without a hidden copy.
472    ///
473    /// # Errors
474    ///
475    /// Returns [`OutputExtractError::InvalidOutput`] for an invalid index or
476    /// [`OutputExtractError::Group`] for a structural group extraction
477    /// failure. Each error returns the unchanged bundle without copying.
478    #[allow(clippy::result_large_err)]
479    pub fn into_output(
480        self,
481        index: usize,
482    ) -> std::result::Result<Tensor, (Self, OutputExtractError)> {
483        let slot = match self.outputs.get(index).copied() {
484            Some(slot) => slot,
485            None => return Err((self, OutputExtractError::InvalidOutput { index })),
486        };
487        let ExecutionBundle { group, outputs } = self;
488        match group.into_tensor(slot) {
489            Ok(tensor) => Ok(tensor),
490            Err((group, error)) => Err((Self { group, outputs }, OutputExtractError::Group(error))),
491        }
492    }
493}
494
495/// The result of a detached submission after the provider retirement point.
496#[derive(Debug)]
497pub enum ExecutionOutcome {
498    /// The scheduled graph completed and produced one alias-safe output bundle.
499    Completed(Box<ExecutionBundle>),
500    /// The graph retired with an ordinary execution error; the input owner is
501    /// available again because retirement was observed.
502    RetiredFailed {
503        error: Error,
504        inputs: Box<ExecutionInputs>,
505    },
506    /// Completion could not be proven. No owner is exposed and the private
507    /// in-flight record retains all provider state permanently.
508    CompletionUnproven {
509        error: Error,
510        diagnostic_keys: Box<[String]>,
511    },
512}
513
514/// A detached submission failed before ownership could be transferred to the
515/// in-flight worker.
516///
517/// # Examples
518///
519/// ```
520/// use tenferro_runtime::{Error, runtime::{ExecutionInputs, SubmitError}};
521///
522/// let error = SubmitError::PreAdmission {
523///     source: Box::new(Error::Internal("input rejected".into())),
524///     inputs: Box::new(ExecutionInputs::new(Vec::new())?),
525/// };
526/// assert!(error.to_string().contains("before admission"));
527/// # Ok::<(), tenferro_runtime::Error>(())
528/// ```
529#[derive(Debug)]
530pub enum SubmitError {
531    /// Preparation/admission rejected the request. The exact input package is
532    /// returned unchanged to the caller.
533    PreAdmission {
534        source: Box<Error>,
535        inputs: Box<ExecutionInputs>,
536    },
537}
538
539impl SubmitError {
540    /// Recover the unchanged package from a pre-admission rejection.
541    ///
542    /// # Examples
543    ///
544    /// ```
545    /// use tenferro_runtime::{Error, runtime::{ExecutionInputs, SubmitError}};
546    ///
547    /// let error = SubmitError::PreAdmission {
548    ///     source: Box::new(Error::Internal("input rejected".into())),
549    ///     inputs: Box::new(ExecutionInputs::new(Vec::new())?),
550    /// };
551    /// assert!(error.into_pre_admission().is_some());
552    /// # Ok::<(), tenferro_runtime::Error>(())
553    /// ```
554    pub fn into_pre_admission(self) -> Option<(Error, ExecutionInputs)> {
555        match self {
556            Self::PreAdmission { source, inputs } => Some((*source, *inputs)),
557        }
558    }
559}
560
561impl fmt::Display for SubmitError {
562    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
563        match self {
564            Self::PreAdmission { source, .. } => {
565                write!(formatter, "submission rejected before admission: {source}")
566            }
567        }
568    }
569}
570
571impl StdError for SubmitError {
572    fn source(&self) -> Option<&(dyn StdError + 'static)> {
573        match self {
574            Self::PreAdmission { source, .. } => {
575                // Keep the pre-admission typed source at the same depth as the
576                // historical runtime error chain while the owner travels in
577                // this wrapper.
578                source.source().or(Some(source.as_ref()))
579            }
580        }
581    }
582}
583
584impl From<SubmitError> for Error {
585    fn from(error: SubmitError) -> Self {
586        match error {
587            SubmitError::PreAdmission { source, .. } => *source,
588        }
589    }
590}
591
592impl ExecutionHandle {
593    /// Wait for submitted work to finish and return its tensor outputs.
594    ///
595    /// # Errors
596    ///
597    /// Returns the submitted runtime execution [`Error`], including
598    /// [`ErrorKind::RuntimeState`](tenferro_tensor::ErrorKind::RuntimeState)
599    /// when the handle was already consumed or the worker panicked.
600    pub fn wait(self) -> Result<ExecutionOutcome> {
601        self.submission.wait()
602    }
603}
604
605impl fmt::Debug for ExecutionHandle {
606    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
607        formatter
608            .debug_struct("ExecutionHandle")
609            .field("pending", &self.submission.is_pending())
610            .finish_non_exhaustive()
611    }
612}
613
614pub(super) struct InFlightSubmission {
615    work: Mutex<Option<InFlightWork>>,
616    completion: Mutex<Option<Result<ExecutionOutcome>>>,
617    completed: Condvar,
618}
619
620struct AdmittedExecution {
621    prepared: PreparedCompiledGraph,
622    inputs: ExecutionInputs,
623}
624
625enum InFlightWork {
626    Admitted(Box<AdmittedExecution>),
627    #[cfg(test)]
628    Test {
629        inputs: Box<ExecutionInputs>,
630        work: Box<dyn FnOnce() -> Result<Vec<Tensor>> + Send>,
631    },
632}
633
634impl InFlightSubmission {
635    fn new(prepared: PreparedCompiledGraph, inputs: ExecutionInputs) -> Self {
636        Self {
637            work: Mutex::new(Some(InFlightWork::Admitted(Box::new(AdmittedExecution {
638                prepared,
639                inputs,
640            })))),
641            completion: Mutex::new(None),
642            completed: Condvar::new(),
643        }
644    }
645
646    #[cfg(test)]
647    pub(super) fn for_test(work: impl FnOnce() -> Result<Vec<Tensor>> + Send + 'static) -> Self {
648        Self {
649            work: Mutex::new(Some(InFlightWork::Test {
650                inputs: Box::new(ExecutionInputs::new(Vec::new()).expect("empty test inputs")),
651                work: Box::new(work),
652            })),
653            completion: Mutex::new(None),
654            completed: Condvar::new(),
655        }
656    }
657
658    fn into_unstarted_inputs(self) -> ExecutionInputs {
659        let work = match self.work.into_inner() {
660            Ok(work) => work,
661            Err(poisoned) => poisoned.into_inner(),
662        };
663        match work {
664            Some(InFlightWork::Admitted(admitted)) => admitted.inputs,
665            #[cfg(test)]
666            Some(InFlightWork::Test { inputs, .. }) => *inputs,
667            None => unreachable!("unstarted submission must still contain its owner"),
668        }
669    }
670
671    pub(super) fn run(&self) {
672        let work = match self.work.lock() {
673            Ok(mut work) => work.take(),
674            Err(poisoned) => poisoned.into_inner().take(),
675        };
676        let result = match work {
677            Some(InFlightWork::Admitted(admitted)) => run_admitted_work(admitted),
678            #[cfg(test)]
679            Some(InFlightWork::Test { work, .. }) => {
680                let result = catch_unwind(AssertUnwindSafe(work));
681                match result {
682                    Ok(Ok(outputs)) => ExecutionBundle::from_outputs(outputs)
683                        .map(|bundle| ExecutionOutcome::Completed(Box::new(bundle))),
684                    Ok(Err(error)) => Err(error),
685                    Err(payload) => Ok(ExecutionOutcome::CompletionUnproven {
686                        error: Error::runtime_state(
687                            "ExecutionHandle::wait",
688                            ErrorPhase::Execution,
689                            panic_payload_message(payload),
690                        ),
691                        diagnostic_keys: Box::from(["execution.retirement-unproven".to_owned()]),
692                    }),
693                }
694            }
695            None => Err(Error::runtime_state(
696                "Runtime::submit",
697                ErrorPhase::Execution,
698                "in-flight submission work was already consumed",
699            )),
700        };
701        match self.completion.lock() {
702            Ok(mut completion) => {
703                *completion = Some(result);
704            }
705            Err(poisoned) => {
706                *poisoned.into_inner() = Some(Err(Error::runtime_state(
707                    "ExecutionHandle::wait",
708                    ErrorPhase::Execution,
709                    "in-flight completion lock poisoned",
710                )));
711            }
712        }
713        self.completed.notify_all();
714    }
715
716    fn wait(&self) -> Result<ExecutionOutcome> {
717        let mut completion = self.completion.lock().map_err(|_| {
718            Error::runtime_state(
719                "ExecutionHandle::wait",
720                ErrorPhase::Execution,
721                "in-flight completion lock poisoned",
722            )
723        })?;
724        loop {
725            if let Some(result) = completion.take() {
726                return result;
727            }
728            completion = self.completed.wait(completion).map_err(|_| {
729                Error::runtime_state(
730                    "ExecutionHandle::wait",
731                    ErrorPhase::Execution,
732                    "in-flight completion lock poisoned while waiting",
733                )
734            })?;
735        }
736    }
737
738    fn is_pending(&self) -> bool {
739        self.completion
740            .lock()
741            .map_or(true, |completion| completion.is_none())
742    }
743}
744
745fn run_admitted_work(admitted: Box<AdmittedExecution>) -> Result<ExecutionOutcome> {
746    let execution = catch_unwind(AssertUnwindSafe(|| {
747        let input_reads = admitted.inputs.as_reads()?;
748        execute_admitted(&admitted.prepared, &input_reads)
749    }));
750    match execution {
751        Ok(Ok(outputs)) => {
752            let AdmittedExecution { inputs, .. } = *admitted;
753            match ExecutionBundle::from_inputs_and_outputs(inputs, outputs) {
754                Ok(bundle) => Ok(ExecutionOutcome::Completed(Box::new(bundle))),
755                Err(error) => Ok(ExecutionOutcome::CompletionUnproven {
756                    error,
757                    diagnostic_keys: Box::from(["execution.bundle-build".to_owned()]),
758                }),
759            }
760        }
761        Ok(Err(error)) => {
762            let AdmittedExecution { inputs, .. } = *admitted;
763            Ok(ExecutionOutcome::RetiredFailed {
764                error,
765                inputs: Box::new(inputs),
766            })
767        }
768        Err(payload) => {
769            let error = Error::runtime_state(
770                "ExecutionHandle::wait",
771                ErrorPhase::Execution,
772                panic_payload_message(payload),
773            );
774            // The completion witness was lost after admission. Leak the
775            // private record deliberately: no owner or provider state may be
776            // exposed while retirement is unproven.
777            Box::leak(admitted);
778            Ok(ExecutionOutcome::CompletionUnproven {
779                error,
780                diagnostic_keys: Box::from(["execution.retirement-unproven".to_owned()]),
781            })
782        }
783    }
784}
785
786pub(super) trait SubmissionSpawner {
787    fn spawn(&self, submission: Arc<InFlightSubmission>) -> std::io::Result<()>;
788}
789
790pub(super) fn spawn_in_flight(
791    submission: Arc<InFlightSubmission>,
792    spawner: &dyn SubmissionSpawner,
793) -> std::result::Result<ExecutionHandle, Box<(ExecutionInputs, Error)>> {
794    if let Err(source) = spawner.spawn(Arc::clone(&submission)) {
795        // INVARIANT: a SubmissionSpawner error means it did not take or start
796        // the submission, so the sole Arc can be unwrapped and the exact
797        // pre-admission owner recovered.
798        let submission = match Arc::try_unwrap(submission) {
799            Ok(submission) => submission,
800            Err(_) => unreachable!("failed spawner must not retain submission"),
801        };
802        let inputs = submission.into_unstarted_inputs();
803        let error = Error::runtime_state_source(
804            "Runtime::submit",
805            ErrorPhase::Execution,
806            SubmissionError::WorkerSpawn { source },
807        );
808        return Err(Box::new((inputs, error)));
809    }
810    Ok(ExecutionHandle { submission })
811}
812
813pub(super) struct OsThreadSpawner;
814
815impl SubmissionSpawner for OsThreadSpawner {
816    fn spawn(&self, submission: Arc<InFlightSubmission>) -> std::io::Result<()> {
817        thread::Builder::new()
818            .name("tenferro-runtime-submit".to_string())
819            .spawn(move || submission.run())
820            .map(drop)
821    }
822}
823
824impl fmt::Debug for PreparedCompiledGraph {
825    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
826        formatter
827            .debug_struct("PreparedCompiledGraph")
828            .field("runtime_id", &self.runtime_id)
829            .field("epoch", &self.epoch)
830            .field("program", &self.program)
831            .finish_non_exhaustive()
832    }
833}
834
835fn panic_payload_message(payload: Box<dyn std::any::Any + Send + 'static>) -> String {
836    if let Some(message) = payload.downcast_ref::<&str>() {
837        format!("submitted execution panicked: {message}")
838    } else if let Some(message) = payload.downcast_ref::<String>() {
839        format!("submitted execution panicked: {message}")
840    } else {
841        "submitted execution panicked".to_string()
842    }
843}
844
845#[allow(
846    dead_code,
847    reason = "Phase 5 runtime execution task adds erased dispatch methods"
848)]
849pub(super) trait ErasedTensorBackendExecutor: fmt::Debug + Send + Sync {
850    fn backend_type_name(&self) -> &'static str;
851    fn extension_cache_stats(&self) -> std::result::Result<CacheStats, CacheOwnerError>;
852    fn clear_extension_caches(&self) -> std::result::Result<(), CacheOwnerError>;
853    fn execute(
854        &self,
855        program: &ExecProgram,
856        operations: &[PreparedOperationPlan],
857        inputs: Vec<Tensor>,
858    ) -> Result<Vec<Tensor>>;
859    fn execute_tensor_refs(
860        &self,
861        program: &ExecProgram,
862        operations: &[PreparedOperationPlan],
863        inputs: &[&Tensor],
864    ) -> Result<Vec<Tensor>>;
865    fn execute_values(
866        &self,
867        program: &ExecProgram,
868        operations: &[PreparedOperationPlan],
869        inputs: Vec<Tensor>,
870    ) -> Result<Vec<TensorValue>>;
871    fn execute_value_refs(
872        &self,
873        program: &ExecProgram,
874        operations: &[PreparedOperationPlan],
875        inputs: &[&Tensor],
876    ) -> Result<Vec<TensorValue>>;
877    fn execute_slot_instruction<'input>(
878        &self,
879        instruction_index: usize,
880        instruction: &ExecInstruction,
881        operations: &[PreparedOperationPlan],
882        slots: &mut [Option<ExecSlot<'input>>],
883        output_mode: RuntimeOutputMode,
884        terminal_slots: &[bool],
885    ) -> Result<()>;
886    /// Execute one planned elementwise region as a single fused command.
887    ///
888    /// Reads `input_slots` from `slots`, runs `plan`, and writes the fused
889    /// outputs into `output_slots`. Returns `Ok(false)` when the backend
890    /// rejects the fusion, in which case the caller executes the region's
891    /// instructions one by one; nothing is written in that case.
892    fn execute_elementwise_fusion_slots<'input>(
893        &self,
894        input_slots: &[usize],
895        instruction_count: usize,
896        plan: &tenferro_tensor::backend::ElementwiseFusionPlan,
897        slots: &mut [Option<ExecSlot<'input>>],
898        output_slots: &[usize],
899    ) -> Result<bool>;
900    fn materialize_slot<'input>(&self, slot: ExecSlot<'input>) -> Result<Tensor>;
901    fn materialize_slot_value<'input>(&self, slot: ExecSlot<'input>) -> Result<TensorValue>;
902}
903
904pub(super) fn erased_tensor_backend_executor<B>(backend: B) -> Arc<dyn ErasedTensorBackendExecutor>
905where
906    B: TensorBackend + Send + Sync + 'static,
907{
908    Arc::new(TensorBackendExecutor::<B>::new(backend))
909}
910
911pub(super) fn extension_cache_owner(
912    executor: Arc<dyn ErasedTensorBackendExecutor>,
913) -> Arc<dyn RuntimeCacheOwner> {
914    Arc::new(TensorBackendExtensionCacheOwner { executor })
915}
916
917#[derive(Debug)]
918struct TensorBackendExtensionCacheOwner {
919    executor: Arc<dyn ErasedTensorBackendExecutor>,
920}
921
922impl RuntimeCacheOwner for TensorBackendExtensionCacheOwner {
923    fn cache_stats(&self) -> std::result::Result<CacheStats, CacheOwnerError> {
924        self.executor.extension_cache_stats()
925    }
926
927    fn clear_caches(&self) -> std::result::Result<(), CacheOwnerError> {
928        self.executor.clear_extension_caches()
929    }
930}
931
932#[allow(
933    dead_code,
934    reason = "Phase 5 runtime execution task consumes backend execution state"
935)]
936struct TensorBackendExecutorState<B: TensorBackend + 'static> {
937    backend: B,
938    backend_cache: B::RuntimeCache,
939    extension_caches: ExtensionCacheStore,
940    slot_workspace: Vec<Option<ExecSlot<'static>>>,
941    borrowed_slot_workspace_capacity: usize,
942}
943
944struct TensorBackendExecutor<B: TensorBackend + 'static> {
945    state: Mutex<TensorBackendExecutorSlot<B>>,
946    available: Condvar,
947}
948
949struct TensorBackendExecutorSlot<B: TensorBackend + 'static> {
950    state: Option<TensorBackendExecutorState<B>>,
951    active_thread: Option<ThreadId>,
952    execution_poisoned: bool,
953}
954
955impl<B> TensorBackendExecutor<B>
956where
957    B: TensorBackend + Send + Sync + 'static,
958{
959    fn new(backend: B) -> Self {
960        Self {
961            state: Mutex::new(TensorBackendExecutorSlot {
962                state: Some(TensorBackendExecutorState {
963                    backend,
964                    backend_cache: B::RuntimeCache::default(),
965                    extension_caches: ExtensionCacheStore::new(),
966                    slot_workspace: Vec::new(),
967                    borrowed_slot_workspace_capacity: 0,
968                }),
969                active_thread: None,
970                execution_poisoned: false,
971            }),
972            available: Condvar::new(),
973        }
974    }
975
976    fn lease_state(&self, caller: &'static str) -> Result<TensorBackendExecutorLease<'_, B>> {
977        let current = thread::current().id();
978        let mut slot = self.lock_slot(caller)?;
979        loop {
980            if slot.execution_poisoned {
981                return Err(Error::runtime_state(
982                    caller,
983                    ErrorPhase::Execution,
984                    "tensor backend executor state poisoned by panic during prior execution",
985                ));
986            }
987            if let Some(state) = slot.state.take() {
988                slot.active_thread = Some(current);
989                return Ok(TensorBackendExecutorLease {
990                    executor: self,
991                    state: Some(state),
992                });
993            }
994            if slot.active_thread == Some(current) {
995                return Err(Error::runtime_state(
996                    caller,
997                    ErrorPhase::Execution,
998                    "reentrant tensor backend executor call would deadlock",
999                ));
1000            }
1001            slot = self.available.wait(slot).map_err(|_| {
1002                Error::runtime_state(
1003                    caller,
1004                    ErrorPhase::Execution,
1005                    "tensor backend executor state lock poisoned while waiting",
1006                )
1007            })?;
1008        }
1009    }
1010
1011    fn lock_slot(
1012        &self,
1013        caller: &'static str,
1014    ) -> Result<MutexGuard<'_, TensorBackendExecutorSlot<B>>> {
1015        self.state.lock().map_err(|_| {
1016            Error::runtime_state(
1017                caller,
1018                ErrorPhase::Execution,
1019                "tensor backend executor state lock poisoned",
1020            )
1021        })
1022    }
1023}
1024
1025struct TensorBackendExecutorLease<'a, B: TensorBackend + 'static> {
1026    executor: &'a TensorBackendExecutor<B>,
1027    state: Option<TensorBackendExecutorState<B>>,
1028}
1029
1030impl<B: TensorBackend + 'static> TensorBackendExecutorLease<'_, B> {
1031    fn state_mut(&mut self) -> &mut TensorBackendExecutorState<B> {
1032        self.state
1033            .as_mut()
1034            .expect("executor state lease always owns state before drop")
1035    }
1036}
1037
1038impl<B: TensorBackend + 'static> Drop for TensorBackendExecutorLease<'_, B> {
1039    fn drop(&mut self) {
1040        let Some(state) = self.state.take() else {
1041            return;
1042        };
1043        let panicking = thread::panicking();
1044        let mut wake_all_waiters = panicking;
1045        match self.executor.state.lock() {
1046            Ok(mut slot) => {
1047                slot.state = Some(state);
1048                slot.active_thread = None;
1049                slot.execution_poisoned |= panicking;
1050            }
1051            Err(poisoned_lock) => {
1052                let mut slot = poisoned_lock.into_inner();
1053                slot.state = Some(state);
1054                slot.active_thread = None;
1055                slot.execution_poisoned = true;
1056                wake_all_waiters = true;
1057            }
1058        }
1059        if wake_all_waiters {
1060            self.executor.available.notify_all();
1061        } else {
1062            self.executor.available.notify_one();
1063        }
1064    }
1065}
1066
1067impl<B: TensorBackend + 'static> fmt::Debug for TensorBackendExecutor<B> {
1068    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
1069        formatter
1070            .debug_struct("TensorBackendExecutor")
1071            .field("backend_type", &std::any::type_name::<B>())
1072            .field("state_lock_poisoned", &self.state.is_poisoned())
1073            .finish_non_exhaustive()
1074    }
1075}
1076
1077impl<B> ErasedTensorBackendExecutor for TensorBackendExecutor<B>
1078where
1079    B: TensorBackend + Send + Sync + 'static,
1080{
1081    fn backend_type_name(&self) -> &'static str {
1082        std::any::type_name::<B>()
1083    }
1084
1085    fn extension_cache_stats(&self) -> std::result::Result<CacheStats, CacheOwnerError> {
1086        let lease = self
1087            .lease_state("Runtime::extension_cache_stats")
1088            .map_err(cache_owner_error)?;
1089        let state = lease
1090            .state
1091            .as_ref()
1092            .expect("executor state lease always owns state before drop");
1093        Ok(cache_stats_from_tensor_stats(
1094            state.extension_caches.stats(ExtensionCacheSelector::All),
1095        ))
1096    }
1097
1098    fn clear_extension_caches(&self) -> std::result::Result<(), CacheOwnerError> {
1099        let mut lease = self
1100            .lease_state("Runtime::clear_extension_caches")
1101            .map_err(cache_owner_error)?;
1102        lease.state_mut().extension_caches.clear();
1103        Ok(())
1104    }
1105
1106    fn execute(
1107        &self,
1108        program: &ExecProgram,
1109        operations: &[PreparedOperationPlan],
1110        inputs: Vec<Tensor>,
1111    ) -> Result<Vec<Tensor>> {
1112        validate_exec_input_count(program, inputs.len())?;
1113        let mut lease = self.lease_state("Runtime::run_compiled")?;
1114        let TensorBackendExecutorState {
1115            backend,
1116            backend_cache,
1117            extension_caches,
1118            slot_workspace,
1119            borrowed_slot_workspace_capacity: _,
1120        } = lease.state_mut();
1121        let mut extension_dispatch = ExtensionExecutionDispatch {
1122            operations,
1123            caches: extension_caches,
1124        };
1125        crate::segment::eval_exec_segmented_with_cache_and_workspace(
1126            backend,
1127            program,
1128            inputs,
1129            slot_workspace,
1130            backend_cache,
1131            Some(&mut extension_dispatch),
1132        )
1133    }
1134
1135    fn execute_tensor_refs(
1136        &self,
1137        program: &ExecProgram,
1138        operations: &[PreparedOperationPlan],
1139        inputs: &[&Tensor],
1140    ) -> Result<Vec<Tensor>> {
1141        validate_exec_input_count(program, inputs.len())?;
1142        let inputs = inputs
1143            .iter()
1144            .map(|tensor| ExecSlot::Read(TensorRead::from_tensor(tensor)))
1145            .collect();
1146        let mut lease = self.lease_state("Runtime::run_compiled")?;
1147        let TensorBackendExecutorState {
1148            backend,
1149            backend_cache,
1150            extension_caches,
1151            borrowed_slot_workspace_capacity,
1152            ..
1153        } = lease.state_mut();
1154        let mut extension_dispatch = ExtensionExecutionDispatch {
1155            operations,
1156            caches: extension_caches,
1157        };
1158        let mut slot_workspace = Vec::with_capacity(*borrowed_slot_workspace_capacity);
1159        let result = crate::segment::eval_exec_segmented_slots_with_cache_and_workspace(
1160            backend,
1161            program,
1162            inputs,
1163            &mut slot_workspace,
1164            backend_cache,
1165            Some(&mut extension_dispatch),
1166        );
1167        *borrowed_slot_workspace_capacity = slot_workspace.capacity();
1168        result
1169    }
1170
1171    fn execute_values(
1172        &self,
1173        program: &ExecProgram,
1174        operations: &[PreparedOperationPlan],
1175        inputs: Vec<Tensor>,
1176    ) -> Result<Vec<TensorValue>> {
1177        validate_exec_input_count(program, inputs.len())?;
1178        let inputs = inputs.into_iter().map(ExecSlot::Owned).collect();
1179        let mut lease = self.lease_state("Runtime::run_compiled_values")?;
1180        let TensorBackendExecutorState {
1181            backend,
1182            backend_cache,
1183            extension_caches,
1184            slot_workspace,
1185            borrowed_slot_workspace_capacity: _,
1186        } = lease.state_mut();
1187        let mut extension_dispatch = ExtensionExecutionDispatch {
1188            operations,
1189            caches: extension_caches,
1190        };
1191        crate::segment::eval_exec_segmented_slot_values_with_cache_and_workspace(
1192            backend,
1193            program,
1194            inputs,
1195            slot_workspace,
1196            backend_cache,
1197            Some(&mut extension_dispatch),
1198        )
1199    }
1200
1201    fn execute_value_refs(
1202        &self,
1203        program: &ExecProgram,
1204        operations: &[PreparedOperationPlan],
1205        inputs: &[&Tensor],
1206    ) -> Result<Vec<TensorValue>> {
1207        validate_exec_input_count(program, inputs.len())?;
1208        let inputs = inputs
1209            .iter()
1210            .map(|tensor| ExecSlot::Read(TensorRead::from_tensor(tensor)))
1211            .collect();
1212        let mut lease = self.lease_state("Runtime::run_compiled_values")?;
1213        let TensorBackendExecutorState {
1214            backend,
1215            backend_cache,
1216            extension_caches,
1217            borrowed_slot_workspace_capacity,
1218            ..
1219        } = lease.state_mut();
1220        let mut extension_dispatch = ExtensionExecutionDispatch {
1221            operations,
1222            caches: extension_caches,
1223        };
1224        let mut slot_workspace = Vec::with_capacity(*borrowed_slot_workspace_capacity);
1225        let result = crate::segment::eval_exec_segmented_slot_values_with_cache_and_workspace(
1226            backend,
1227            program,
1228            inputs,
1229            &mut slot_workspace,
1230            backend_cache,
1231            Some(&mut extension_dispatch),
1232        );
1233        *borrowed_slot_workspace_capacity = slot_workspace.capacity();
1234        result
1235    }
1236
1237    fn execute_slot_instruction<'input>(
1238        &self,
1239        instruction_index: usize,
1240        instruction: &ExecInstruction,
1241        operations: &[PreparedOperationPlan],
1242        slots: &mut [Option<ExecSlot<'input>>],
1243        output_mode: RuntimeOutputMode,
1244        terminal_slots: &[bool],
1245    ) -> Result<()> {
1246        let mut lease = self.lease_state("Runtime::run_compiled scheduled instruction")?;
1247        let TensorBackendExecutorState {
1248            backend,
1249            backend_cache,
1250            extension_caches,
1251            ..
1252        } = lease.state_mut();
1253        let mut extension_dispatch = ExtensionExecutionDispatch {
1254            operations,
1255            caches: extension_caches,
1256        };
1257
1258        if matches!(output_mode, RuntimeOutputMode::Value)
1259            && backend.with_backend_session(|exec| {
1260                crate::exec::try_execute_terminal_value_instruction(
1261                    exec,
1262                    slots,
1263                    instruction,
1264                    terminal_slots,
1265                )
1266            })?
1267        {
1268            // Already handled as a metadata-only TensorValue.
1269        } else if crate::exec::is_host_instruction(instruction) {
1270            crate::exec::execute_host_instruction(backend, slots, instruction)?;
1271        } else if crate::exec::is_ffi_instruction(instruction) {
1272            crate::exec::execute_ffi_instruction_cached(
1273                backend,
1274                backend_cache,
1275                slots,
1276                instruction,
1277                DispatchMode::Unsegmented,
1278                Some(instruction_index),
1279                Some(&mut extension_dispatch),
1280            )?;
1281        } else {
1282            let result = backend.with_backend_session(|exec| {
1283                crate::exec::execute_backend_op(exec, slots, instruction)
1284            })?;
1285            slots[instruction.output_slots[0]] = Some(ExecSlot::Owned(result));
1286        }
1287        crate::exec::reclaim_last_use_inputs_backend(slots, instruction, backend);
1288        Ok(())
1289    }
1290
1291    fn execute_elementwise_fusion_slots<'input>(
1292        &self,
1293        input_slots: &[usize],
1294        instruction_count: usize,
1295        plan: &tenferro_tensor::backend::ElementwiseFusionPlan,
1296        slots: &mut [Option<ExecSlot<'input>>],
1297        output_slots: &[usize],
1298    ) -> Result<bool> {
1299        // The fused entry point takes owned tensors. A borrowed view input is
1300        // materialized once when the region is long enough to amortize that
1301        // copy; otherwise the per-instruction path, which resolves views itself,
1302        // stays in charge.
1303        let mut views: Vec<usize> = Vec::new();
1304        for &slot in input_slots {
1305            let value = slots
1306                .get(slot)
1307                .and_then(Option::as_ref)
1308                .ok_or_else(|| crate::Error::from(tenferro_tensor::Error::MissingValue { slot }))?;
1309            if value.as_tensor("elementwise_region").is_err() {
1310                views.push(slot);
1311            }
1312        }
1313        if !views.is_empty() {
1314            if instruction_count < super::region::VIEW_COPY_MIN_INSTRUCTIONS {
1315                return Ok(false);
1316            }
1317            let mut lease = self.lease_state("Runtime::run_prepared elementwise region copy")?;
1318            let backend = &mut lease.state_mut().backend;
1319            for &slot in &views {
1320                let tensor = {
1321                    let read = slots[slot].as_ref().map(ExecSlot::as_read).ok_or_else(|| {
1322                        crate::Error::from(tenferro_tensor::Error::MissingValue { slot })
1323                    })?;
1324                    backend.with_backend_session(|exec| exec.to_contiguous_read(read))?
1325                };
1326                slots[slot] = Some(ExecSlot::Owned(tensor));
1327            }
1328        }
1329
1330        let mut inputs: Vec<&Tensor> = Vec::with_capacity(input_slots.len());
1331        for &slot in input_slots {
1332            let value = slots
1333                .get(slot)
1334                .and_then(Option::as_ref)
1335                .ok_or_else(|| crate::Error::from(tenferro_tensor::Error::MissingValue { slot }))?;
1336            inputs.push(value.as_tensor("elementwise_region")?);
1337        }
1338
1339        if !crate::segment::plan_matches_input_dtypes(plan, &inputs) {
1340            return Ok(false);
1341        }
1342
1343        let mut lease = self.lease_state("Runtime::run_prepared elementwise region")?;
1344        let backend = &mut lease.state_mut().backend;
1345        let outputs =
1346            backend.with_backend_session(|exec| exec.execute_elementwise_fusion(&inputs, plan))?;
1347        let Some(outputs) = outputs else {
1348            return Ok(false);
1349        };
1350        if outputs.len() != output_slots.len() {
1351            return Err(crate::Error::Internal(format!(
1352                "fused elementwise region produced {} outputs for {} slots",
1353                outputs.len(),
1354                output_slots.len()
1355            )));
1356        }
1357        for (&slot, tensor) in output_slots.iter().zip(outputs) {
1358            slots[slot] = Some(ExecSlot::Owned(tensor));
1359        }
1360        Ok(true)
1361    }
1362
1363    fn materialize_slot<'input>(&self, slot: ExecSlot<'input>) -> Result<Tensor> {
1364        let mut lease = self.lease_state("Runtime::run_compiled collect outputs")?;
1365        let backend = &mut lease.state_mut().backend;
1366        backend.with_backend_session(|exec| slot.into_tensor(exec))
1367    }
1368
1369    fn materialize_slot_value<'input>(&self, slot: ExecSlot<'input>) -> Result<TensorValue> {
1370        let mut lease = self.lease_state("Runtime::run_compiled_values collect outputs")?;
1371        let backend = &mut lease.state_mut().backend;
1372        backend.with_backend_session(|exec| slot.into_value(exec))
1373    }
1374}
1375
1376fn cache_owner_error(error: Error) -> CacheOwnerError {
1377    CacheOwnerError::new(Arc::new(error))
1378}
1379
1380fn cache_stats_from_tensor_stats(stats: tenferro_tensor::CacheStats) -> CacheStats {
1381    CacheStats {
1382        entries: stats.entries,
1383        retained_bytes: stats.retained_bytes,
1384        hits: stats.hits,
1385        misses: stats.misses,
1386        evictions: stats.evictions,
1387        clears: stats.clears,
1388    }
1389}
1390
1391pub(super) fn run_compiled(
1392    runtime: &Runtime,
1393    program: &CompiledGraph,
1394    inputs: &[&Tensor],
1395) -> Result<Vec<Tensor>> {
1396    let inputs = resolve_input_refs(program, inputs)?;
1397    let signature = input_signature_reads(&inputs)?;
1398    let prepared = prepare(runtime, program, &signature)?;
1399    validate_prepared_epoch(runtime, prepared.root().epoch(), "Runtime::run_compiled")?;
1400    execute_scheduled_reads(
1401        prepared.root().staging(),
1402        prepared.root().schedule(),
1403        prepared.root().regions(),
1404        prepared.root().region_counters(),
1405        prepared.operations(),
1406        &inputs,
1407    )
1408}
1409
1410/// Execute one immutable borrowed package synchronously through provider retirement.
1411///
1412/// Asynchronous or non-host providers are rejected before preparation can enqueue
1413/// work. CPU/host execution returns only after the schedule has retired.
1414pub(super) fn execute_scoped_read_only<'env>(
1415    runtime: &Runtime,
1416    program: &CompiledGraph,
1417    inputs: ScopedReadInputs<'env>,
1418) -> std::result::Result<ScopedExecutionOutcome<'env>, ScopedSubmitRejected<'env>> {
1419    if inputs.has_non_host_provider() {
1420        return Err(ScopedSubmitRejected {
1421            source: Box::new(Error::unsupported(
1422                "Runtime::execute_scoped_read_only",
1423                ErrorPhase::Execution,
1424                "scoped borrowed execution requires host/CPU storage",
1425            )),
1426            inputs,
1427        });
1428    }
1429
1430    let reads = inputs.as_reads();
1431    let prepared = match prepare_compiled_reads(runtime, program, &reads) {
1432        Ok(prepared) => prepared,
1433        Err(source) => {
1434            return Err(ScopedSubmitRejected {
1435                source: Box::new(source),
1436                inputs,
1437            })
1438        }
1439    };
1440    if !schedule_supports_scoped_execution(prepared.prepared.root().schedule()) {
1441        return Err(ScopedSubmitRejected {
1442            source: Box::new(Error::unsupported(
1443                "Runtime::execute_scoped_read_only",
1444                ErrorPhase::Execution,
1445                "the selected provider does not prove retirement before scoped return",
1446            )),
1447            inputs,
1448        });
1449    }
1450
1451    match execute_scoped_admitted(
1452        prepared.prepared.root().staging(),
1453        prepared.prepared.root().schedule(),
1454        prepared.prepared.operations(),
1455        &reads,
1456    ) {
1457        Ok(bundle) => Ok(ScopedExecutionOutcome::Completed(bundle)),
1458        Err(error) => Ok(ScopedExecutionOutcome::RetiredFailed { error, inputs }),
1459    }
1460}
1461
1462fn execute_scoped_admitted<'env>(
1463    program: &ExecProgram,
1464    schedule: &ScheduledGraph,
1465    operations: &[PreparedOperationPlan],
1466    inputs: &[TensorRead<'env>],
1467) -> Result<ScopedExecutionBundle<'env>> {
1468    validate_exec_input_count(program, inputs.len())?;
1469    crate::exec::validate_exec_program(program, "scoped tensor executor")?;
1470    let input_slots = inputs.iter().cloned().map(ExecSlot::Read).collect();
1471    let mut located = execute_scheduled_slots(
1472        program,
1473        schedule,
1474        &[],
1475        &super::region::RegionExecutionCounters::default(),
1476        operations,
1477        input_slots,
1478        RuntimeOutputMode::Tensor,
1479    )?;
1480    collect_scoped_outputs(program, &mut located)
1481}
1482
1483pub(crate) fn collect_scoped_outputs<'env>(
1484    program: &ExecProgram,
1485    outputs: &mut [Option<LocatedExecSlot<'env>>],
1486) -> Result<ScopedExecutionBundle<'env>> {
1487    let mut owned = AllocationGroup::from_tensors(Vec::new())
1488        .map(|(group, _)| group)
1489        .map_err(|error| {
1490            Error::runtime_state(
1491                "Runtime::collect_scoped_outputs",
1492                ErrorPhase::Execution,
1493                error.to_string(),
1494            )
1495        })?;
1496    let mut scoped = Vec::with_capacity(program.output_slots.len());
1497    for &slot in &program.output_slots {
1498        let located = outputs
1499            .get_mut(slot)
1500            .and_then(Option::take)
1501            .ok_or(tenferro_tensor::Error::MissingValue { slot })?;
1502        let output = match located.value {
1503            ExecSlot::Read(read) => ScopedOutput::Borrowed(read.tensor_view()),
1504            ExecSlot::Owned(tensor) => {
1505                let slot = owned.append_tensor(tensor).map_err(|error| {
1506                    Error::runtime_state(
1507                        "Runtime::collect_scoped_outputs",
1508                        ErrorPhase::Execution,
1509                        error.to_string(),
1510                    )
1511                })?;
1512                ScopedOutput::Owned(slot)
1513            }
1514            ExecSlot::Value(value) => {
1515                let (group, slot, dtype, shape) = value.try_into_group_parts().map_err(|_| {
1516                    Error::runtime_state(
1517                        "Runtime::collect_scoped_outputs",
1518                        ErrorPhase::Execution,
1519                        "scoped value output was retained by another owner",
1520                    )
1521                })?;
1522                let slot = owned.append_group(group, slot).map_err(|error| {
1523                    Error::runtime_state(
1524                        "Runtime::collect_scoped_outputs",
1525                        ErrorPhase::Execution,
1526                        error.to_string(),
1527                    )
1528                })?;
1529                let _ = (dtype, shape);
1530                ScopedOutput::Owned(slot)
1531            }
1532        };
1533        scoped.push(output);
1534    }
1535    outputs.iter_mut().for_each(|output| *output = None);
1536    Ok(ScopedExecutionBundle {
1537        owned,
1538        outputs: scoped.into_boxed_slice(),
1539    })
1540}
1541
1542fn output_ref_from_group<'a>(
1543    group: &'a AllocationGroup,
1544    slot: DescriptorSlot,
1545) -> std::result::Result<OutputRef<'a>, OutputAccessError> {
1546    let mut views = group.read_views(std::slice::from_ref(&slot))?;
1547    match views.pop() {
1548        Some(TensorRead::View(view)) => Ok(OutputRef::Tensor(view)),
1549        Some(TensorRead::Tensor(_)) | None => Err(OutputAccessError::InvalidOutput {
1550            index: slot.index(),
1551        }),
1552    }
1553}
1554
1555fn schedule_supports_scoped_execution(schedule: &ScheduledGraph) -> bool {
1556    let supports = |location: &ExecutionLocation| {
1557        location
1558            .witness()
1559            .event_domain_driver()
1560            .supports_scoped_read_only()
1561    };
1562    supports(schedule.root_location())
1563        && schedule.input_locations().iter().all(supports)
1564        && schedule.operation_locations().iter().all(supports)
1565        && schedule.nodes().iter().all(|node| {
1566            node.event_domain_witness()
1567                .event_domain_driver()
1568                .supports_scoped_read_only()
1569        })
1570}
1571
1572pub(super) fn prepare_compiled(
1573    runtime: &Runtime,
1574    program: &CompiledGraph,
1575    inputs: &[&Tensor],
1576) -> Result<PreparedCompiledGraph> {
1577    let inputs = resolve_input_refs(program, inputs)?;
1578    let signature = input_signature_reads(&inputs)?;
1579    let prepared = prepare(runtime, program, &signature)?;
1580    validate_prepared_epoch(
1581        runtime,
1582        prepared.root().epoch(),
1583        "Runtime::prepare_compiled",
1584    )?;
1585    Ok(PreparedCompiledGraph {
1586        runtime_id: runtime.id(),
1587        epoch: prepared.root().epoch(),
1588        program: program.clone(),
1589        prepared,
1590    })
1591}
1592
1593pub(super) fn submit(
1594    runtime: &Runtime,
1595    program: &CompiledGraph,
1596    inputs: ExecutionInputs,
1597) -> std::result::Result<ExecutionHandle, SubmitError> {
1598    submit_with_spawner(runtime, program, inputs, &OsThreadSpawner)
1599}
1600
1601pub(super) fn submit_with_spawner(
1602    runtime: &Runtime,
1603    program: &CompiledGraph,
1604    inputs: ExecutionInputs,
1605    spawner: &dyn SubmissionSpawner,
1606) -> std::result::Result<ExecutionHandle, SubmitError> {
1607    let prepared = match prepare_submission(runtime, program, &inputs) {
1608        Ok(prepared) => prepared,
1609        Err(source) => {
1610            return Err(SubmitError::PreAdmission {
1611                source: Box::new(source),
1612                inputs: Box::new(inputs),
1613            })
1614        }
1615    };
1616    let submission = Arc::new(InFlightSubmission::new(prepared, inputs));
1617    match spawn_in_flight(submission, spawner) {
1618        Ok(handle) => Ok(handle),
1619        Err(failure) => {
1620            let (inputs, source) = *failure;
1621            Err(SubmitError::PreAdmission {
1622                source: Box::new(source),
1623                inputs: Box::new(inputs),
1624            })
1625        }
1626    }
1627}
1628
1629pub(super) fn run_prepared(
1630    runtime: &Runtime,
1631    prepared: &PreparedCompiledGraph,
1632    inputs: &[&Tensor],
1633) -> Result<Vec<Tensor>> {
1634    validate_prepared_runtime(runtime, prepared, "Runtime::run_prepared")?;
1635    let inputs = resolve_input_refs(&prepared.program, inputs)?;
1636    execute_scheduled_reads(
1637        prepared.prepared.root().staging(),
1638        prepared.prepared.root().schedule(),
1639        prepared.prepared.root().regions(),
1640        prepared.prepared.root().region_counters(),
1641        prepared.prepared.operations(),
1642        &inputs,
1643    )
1644}
1645
1646fn prepare_submission(
1647    runtime: &Runtime,
1648    program: &CompiledGraph,
1649    inputs: &ExecutionInputs,
1650) -> Result<PreparedCompiledGraph> {
1651    let input_reads = inputs.as_reads()?;
1652    prepare_compiled_reads(runtime, program, &input_reads)
1653}
1654
1655fn prepare_compiled_reads(
1656    runtime: &Runtime,
1657    program: &CompiledGraph,
1658    inputs: &[TensorRead<'_>],
1659) -> Result<PreparedCompiledGraph> {
1660    validate_ordered_input_metadata_reads(program, inputs)?;
1661    let signature = input_signature_reads(inputs)?;
1662    let prepared = prepare(runtime, program, &signature)?;
1663    validate_prepared_epoch(runtime, prepared.root().epoch(), "Runtime::submit")?;
1664    Ok(PreparedCompiledGraph {
1665        runtime_id: runtime.id(),
1666        epoch: prepared.root().epoch(),
1667        program: program.clone(),
1668        prepared,
1669    })
1670}
1671
1672fn execute_admitted(
1673    prepared: &PreparedCompiledGraph,
1674    inputs: &[TensorRead<'_>],
1675) -> Result<Vec<Tensor>> {
1676    execute_scheduled_reads(
1677        prepared.prepared.root().staging(),
1678        prepared.prepared.root().schedule(),
1679        prepared.prepared.root().regions(),
1680        prepared.prepared.root().region_counters(),
1681        prepared.prepared.operations(),
1682        inputs,
1683    )
1684}
1685
1686pub(super) fn run_compiled_values(
1687    runtime: &Runtime,
1688    program: &CompiledGraph,
1689    inputs: &[&Tensor],
1690) -> Result<Vec<TensorValue>> {
1691    let inputs = resolve_input_refs(program, inputs)?;
1692    let signature = input_signature_reads(&inputs)?;
1693    let prepared = prepare(runtime, program, &signature)?;
1694    validate_prepared_epoch(
1695        runtime,
1696        prepared.root().epoch(),
1697        "Runtime::run_compiled_values",
1698    )?;
1699    execute_scheduled_value_reads(
1700        prepared.root().staging(),
1701        prepared.root().schedule(),
1702        prepared.operations(),
1703        &inputs,
1704    )
1705}
1706
1707fn validate_prepared_runtime(
1708    runtime: &Runtime,
1709    prepared: &PreparedCompiledGraph,
1710    caller: &'static str,
1711) -> Result<()> {
1712    if runtime.id() != prepared.runtime_id {
1713        return Err(Error::runtime_state(
1714            caller,
1715            ErrorPhase::Execution,
1716            "prepared compiled graph belongs to a different runtime",
1717        ));
1718    }
1719    let epoch = runtime
1720        .epoch()
1721        .map_err(|source| Error::runtime_state_source(caller, ErrorPhase::Execution, source))?;
1722    if epoch != prepared.epoch {
1723        return Err(Error::runtime_state(
1724            caller,
1725            ErrorPhase::Execution,
1726            format!(
1727                "prepared epoch {:?} does not match current epoch {:?}",
1728                prepared.epoch, epoch
1729            ),
1730        ));
1731    }
1732    Ok(())
1733}
1734
1735fn prepare(
1736    runtime: &Runtime,
1737    program: &CompiledGraph,
1738    signature: &InputSignature,
1739) -> Result<Arc<super::preparation::PreparedProgram>> {
1740    runtime
1741        .prepare_compiled_for(program, signature, &PrepareOptions::new())
1742        .map_err(prepare_error)
1743}
1744
1745fn validate_prepared_epoch(
1746    runtime: &Runtime,
1747    prepared_epoch: super::RuntimeEpoch,
1748    caller: &'static str,
1749) -> Result<()> {
1750    let epoch = runtime
1751        .epoch()
1752        .map_err(|source| Error::runtime_state_source(caller, ErrorPhase::Execution, source))?;
1753    if epoch != prepared_epoch {
1754        return Err(Error::runtime_state(
1755            caller,
1756            ErrorPhase::Execution,
1757            format!(
1758                "prepared epoch {:?} does not match current epoch {:?}",
1759                prepared_epoch, epoch
1760            ),
1761        ));
1762    }
1763    Ok(())
1764}
1765
1766fn execute_scheduled_reads(
1767    program: &ExecProgram,
1768    schedule: &ScheduledGraph,
1769    regions: &[super::region::ElementwiseRegion],
1770    region_counters: &super::region::RegionExecutionCounters,
1771    operations: &[PreparedOperationPlan],
1772    inputs: &[TensorRead<'_>],
1773) -> Result<Vec<Tensor>> {
1774    validate_exec_input_count(program, inputs.len())?;
1775    crate::exec::validate_exec_program(program, "scheduled tensor executor")?;
1776    let inputs = inputs.iter().cloned().map(ExecSlot::Read).collect();
1777    execute_scheduled_slots(
1778        program,
1779        schedule,
1780        regions,
1781        region_counters,
1782        operations,
1783        inputs,
1784        RuntimeOutputMode::Tensor,
1785    )
1786    .and_then(|mut slots| {
1787        collect_tensor_outputs_with(program, &mut slots, |location, slot| {
1788            location.witness().executor().materialize_slot(slot)
1789        })
1790    })
1791}
1792
1793fn execute_scheduled_value_reads(
1794    program: &ExecProgram,
1795    schedule: &ScheduledGraph,
1796    operations: &[PreparedOperationPlan],
1797    inputs: &[TensorRead<'_>],
1798) -> Result<Vec<TensorValue>> {
1799    validate_exec_input_count(program, inputs.len())?;
1800    crate::exec::validate_exec_program(program, "scheduled value executor")?;
1801    let inputs = inputs.iter().cloned().map(ExecSlot::Read).collect();
1802    execute_scheduled_slots(
1803        program,
1804        schedule,
1805        &[],
1806        &super::region::RegionExecutionCounters::default(),
1807        operations,
1808        inputs,
1809        RuntimeOutputMode::Value,
1810    )
1811    .and_then(|mut slots| {
1812        collect_value_outputs_with(program, &mut slots, |location, slot| {
1813            location.witness().executor().materialize_slot_value(slot)
1814        })
1815    })
1816}
1817
1818fn execute_scheduled_slots<'input>(
1819    program: &ExecProgram,
1820    schedule: &ScheduledGraph,
1821    regions: &[super::region::ElementwiseRegion],
1822    region_counters: &super::region::RegionExecutionCounters,
1823    operations: &[PreparedOperationPlan],
1824    inputs: Vec<ExecSlot<'input>>,
1825    output_mode: RuntimeOutputMode,
1826) -> Result<Vec<Option<LocatedExecSlot<'input>>>> {
1827    let terminal_slots = if matches!(output_mode, RuntimeOutputMode::Value) {
1828        crate::exec::terminal_output_slots(program)
1829    } else {
1830        Vec::new()
1831    };
1832    let mut staged = Vec::new();
1833    let mut located = (0..program.n_slots)
1834        .map(|_| Vec::new())
1835        .collect::<Vec<Vec<LocatedExecSlot<'input>>>>();
1836    // INVARIANT: the immutable schedule was validated during preparation;
1837    // runs are declared after the value stores so unwinding drops
1838    // and drains native domain work before any tensor storage is released.
1839    // Driver preflight completes before input ingress or any operation launch.
1840    // Planned elementwise regions: the start node executes the whole region as
1841    // one fused command and the covered nodes are skipped. Regions apply to
1842    // tensor output mode; value mode keeps the per-instruction path.
1843    let regions_applicable = matches!(output_mode, RuntimeOutputMode::Tensor);
1844    let mut region_starts: HashMap<usize, usize> = HashMap::new();
1845    let mut region_covered: HashSet<usize> = HashSet::new();
1846    if regions_applicable {
1847        for (region_index, region) in regions.iter().enumerate() {
1848            if let Some(&first) = region.node_indices.first() {
1849                region_starts.insert(first, region_index);
1850            }
1851            region_covered.extend(region.node_indices.iter().copied());
1852        }
1853    }
1854
1855    let mut event_domains = ScheduledEventDomains::new(schedule)?;
1856    let result = (|| {
1857        crate::exec::initialize_exec_slots_in(program, inputs, &mut staged)?;
1858        if schedule.input_locations().len() != program.input_slots.len() {
1859            return Err(Error::runtime_state(
1860                "Runtime::run_compiled",
1861                ErrorPhase::Execution,
1862                format!(
1863                    "prepared schedule has {} input locations for {} inputs",
1864                    schedule.input_locations().len(),
1865                    program.input_slots.len()
1866                ),
1867            ));
1868        }
1869        for (&slot, location) in program.input_slots.iter().zip(schedule.input_locations()) {
1870            let value = staged
1871                .get_mut(slot)
1872                .and_then(Option::take)
1873                .ok_or(tenferro_tensor::Error::MissingValue { slot })?;
1874            validate_runtime_input_ingress(location, &value.as_read(), slot)?;
1875            located[slot].push(LocatedExecSlot {
1876                location: location.clone(),
1877                value,
1878            });
1879        }
1880        for (node_index, node) in schedule.nodes().iter().enumerate() {
1881            match node {
1882                ScheduledNode::Operation(operation_node) => {
1883                    if let Some(&region_index) = region_starts.get(&node_index) {
1884                        let region = &regions[region_index];
1885                        let first_instruction = program
1886                            .instructions
1887                            .get(region.instruction_range.start)
1888                            .ok_or_else(|| {
1889                                Error::runtime_state(
1890                                    "Runtime::run_prepared",
1891                                    ErrorPhase::Execution,
1892                                    format!(
1893                                        "elementwise region references instruction {}, but the                                          execution program has {} instructions",
1894                                        region.instruction_range.start,
1895                                        program.instructions.len()
1896                                    ),
1897                                )
1898                            })?;
1899                        let operation = instruction_execution(schedule, first_instruction)?;
1900                        if operation.location() != operation_node.location() {
1901                            return Err(Error::runtime_state(
1902                                "Runtime::run_prepared",
1903                                ErrorPhase::Execution,
1904                                "elementwise region location does not match its first prepared                                  operation"
1905                                    .to_string(),
1906                            ));
1907                        }
1908                        let location = operation.location().clone();
1909                        let mut launch = || {
1910                            for &slot in &region.input_slots {
1911                                stage_slot_input(slot, &location, &mut located, &mut staged)?;
1912                            }
1913                            let applied = operation.executor().execute_elementwise_fusion_slots(
1914                                &region.input_slots,
1915                                region.instructions.len(),
1916                                &region.plan,
1917                                &mut staged,
1918                                &region.output_slots,
1919                            )?;
1920                            if applied {
1921                                region_counters.record_fused();
1922                                validate_region_outputs(region, &location, &staged)?;
1923                                return retain_region_results(
1924                                    region,
1925                                    &location,
1926                                    &mut located,
1927                                    &mut staged,
1928                                );
1929                            }
1930                            // The backend rejected the fusion: execute the
1931                            // region's instructions one by one.
1932                            region_counters.record_fallback();
1933                            for (offset, instruction) in region.instructions.iter().enumerate() {
1934                                let instruction_index = region.instruction_range.start + offset;
1935                                let member = instruction_execution(schedule, instruction)?;
1936                                if member.location() != operation_node.location() {
1937                                    return Err(Error::runtime_state(
1938                                        "Runtime::run_prepared",
1939                                        ErrorPhase::Execution,
1940                                        "elementwise region member location does not match the                                          region location"
1941                                            .to_string(),
1942                                    ));
1943                                }
1944                                stage_instruction_inputs(
1945                                    instruction,
1946                                    member.location(),
1947                                    &mut located,
1948                                    &mut staged,
1949                                )?;
1950                                member.executor().execute_slot_instruction(
1951                                    instruction_index,
1952                                    instruction,
1953                                    operations,
1954                                    &mut staged,
1955                                    output_mode,
1956                                    &terminal_slots,
1957                                )?;
1958                                validate_instruction_outputs(
1959                                    instruction_index,
1960                                    instruction,
1961                                    member.location(),
1962                                    &staged,
1963                                )?;
1964                                retain_instruction_results(
1965                                    instruction,
1966                                    member.location(),
1967                                    &mut located,
1968                                    &mut staged,
1969                                )?;
1970                            }
1971                            Ok(())
1972                        };
1973                        event_domains.enqueue(node_index, node, &mut launch)?;
1974                        region_counters.record_submission();
1975                        // Every covered node's completion resolves to the region's
1976                        // completion token, which was recorded after the region's
1977                        // last command.
1978                        let completion = EventDependency::from_completion(node.completion());
1979                        if let Some(token) = event_domains.completions.get(&completion).cloned() {
1980                            for &covered in &region.node_indices {
1981                                if covered == node_index {
1982                                    continue;
1983                                }
1984                                let covered_node = schedule.nodes().get(covered).ok_or_else(|| {
1985                                    Error::runtime_state(
1986                                        "Runtime::run_prepared",
1987                                        ErrorPhase::Execution,
1988                                        format!(
1989                                            "elementwise region references schedule node {covered},                                              but the schedule has {} nodes",
1990                                            schedule.nodes().len()
1991                                        ),
1992                                    )
1993                                })?;
1994                                event_domains.completions.insert(
1995                                    EventDependency::from_completion(covered_node.completion()),
1996                                    Arc::clone(&token),
1997                                );
1998                            }
1999                        }
2000                        continue;
2001                    }
2002                    if region_covered.contains(&node_index) {
2003                        // Executed as part of its region.
2004                        continue;
2005                    }
2006                    let mut launch = || {
2007                        let instruction_index = operation_node.instruction_index();
2008                        let instruction =
2009                            program.instructions.get(instruction_index).ok_or_else(|| {
2010                                Error::runtime_state(
2011                                    "Runtime::run_compiled",
2012                                    ErrorPhase::Execution,
2013                                    format!(
2014                                        "scheduled operation references instruction \
2015                                         {instruction_index}, but the execution program has {} \
2016                                         instructions",
2017                                        program.instructions.len()
2018                                    ),
2019                                )
2020                            })?;
2021                        let operation = instruction_execution(schedule, instruction)?;
2022                        if operation.location() != operation_node.location() {
2023                            return Err(Error::runtime_state(
2024                                "Runtime::run_compiled",
2025                                ErrorPhase::Execution,
2026                                format!(
2027                                    "scheduled instruction {instruction_index} location does not \
2028                                     match its prepared executor"
2029                                ),
2030                            ));
2031                        }
2032                        stage_instruction_inputs(
2033                            instruction,
2034                            operation.location(),
2035                            &mut located,
2036                            &mut staged,
2037                        )?;
2038                        operation.executor().execute_slot_instruction(
2039                            instruction_index,
2040                            instruction,
2041                            operations,
2042                            &mut staged,
2043                            output_mode,
2044                            &terminal_slots,
2045                        )?;
2046                        validate_instruction_outputs(
2047                            instruction_index,
2048                            instruction,
2049                            operation.location(),
2050                            &staged,
2051                        )?;
2052                        retain_instruction_results(
2053                            instruction,
2054                            operation.location(),
2055                            &mut located,
2056                            &mut staged,
2057                        )
2058                    };
2059                    event_domains.enqueue(node_index, node, &mut launch)?;
2060                    region_counters.record_submission();
2061                }
2062                ScheduledNode::Transfer(transfer) => {
2063                    let mut launch = || execute_scheduled_transfer(transfer, &mut located);
2064                    event_domains.enqueue(node_index, node, &mut launch)?;
2065                    region_counters.record_submission();
2066                }
2067                ScheduledNode::Collective(_) => {
2068                    return Err(Error::runtime_state_source(
2069                        "Runtime::run_compiled",
2070                        ErrorPhase::Execution,
2071                        UnsupportedScheduledNodeError {
2072                            node_index,
2073                            node_kind: ScheduledNodeKind::Collective,
2074                        },
2075                    ));
2076                }
2077                ScheduledNode::Barrier(_) => {
2078                    let mut launch = || Ok(());
2079                    event_domains.enqueue(node_index, node, &mut launch)?;
2080                    region_counters.record_submission();
2081                }
2082            }
2083        }
2084        Ok(())
2085    })();
2086    let drain = event_domains.drain();
2087    match (result, drain) {
2088        (Ok(()), Ok(())) => collect_located_outputs(program, &mut located),
2089        (Err(error), Ok(())) | (Ok(()), Err(error)) => {
2090            staged.clear();
2091            located.clear();
2092            Err(error)
2093        }
2094        (Err(primary), Err(cleanup)) => {
2095            staged.clear();
2096            located.clear();
2097            Err(scheduled_execution_cleanup_error(primary, cleanup))
2098        }
2099    }
2100}
2101
2102#[derive(Debug)]
2103pub(crate) struct ScheduledEventDomains {
2104    runs: Vec<RuntimeOwnedEventDomainRun>,
2105    completions: HashMap<EventDependency, Arc<dyn EventToken>>,
2106}
2107
2108#[derive(Debug, thiserror::Error)]
2109#[error(
2110    "scheduled node {node_index} depends on completion {dependency:?}, but no completion token was recorded"
2111)]
2112pub(crate) struct MissingScheduledDependencyCompletionError {
2113    pub(crate) dependency: EventDependency,
2114    pub(crate) node_index: usize,
2115}
2116
2117#[derive(Debug, thiserror::Error)]
2118#[error("scheduled transfer destination {destination:?} already contains value slot {value_slot}")]
2119pub(crate) struct DuplicateTransferDestinationError {
2120    pub(crate) value_slot: usize,
2121    pub(crate) destination: ExecutionLocation,
2122}
2123
2124impl ScheduledEventDomains {
2125    pub(crate) fn new(schedule: &ScheduledGraph) -> Result<Self> {
2126        schedule.preflight().map_err(|source| {
2127            Error::runtime_state_source("Runtime::run_compiled", ErrorPhase::Execution, source)
2128        })?;
2129        let mut drivers = Vec::new();
2130        let mut seen_domains = HashSet::new();
2131        for node in schedule.nodes() {
2132            let domain = node.completion().domain();
2133            if !seen_domains.insert(domain) {
2134                continue;
2135            }
2136            let witness = node.event_domain_witness();
2137            debug_assert_eq!(witness.event_domain_id(), domain);
2138            drivers.push((domain, witness.event_domain_driver().clone()));
2139        }
2140        let mut runs = Vec::with_capacity(drivers.len());
2141        for (domain, driver) in drivers {
2142            let run = RuntimeOwnedEventDomainRun::new(domain, driver.begin_run(domain)?);
2143            let actual = run.domain(EventDomainOperation::BeginRun)?;
2144            if actual != domain {
2145                return Err(event_domain_error(EventDomainError::RunDomainMismatch {
2146                    operation: EventDomainOperation::BeginRun,
2147                    node_index: None,
2148                    expected: domain,
2149                    actual,
2150                }));
2151            }
2152            runs.push(run);
2153        }
2154        Ok(Self {
2155            runs,
2156            completions: HashMap::new(),
2157        })
2158    }
2159
2160    #[cfg(test)]
2161    pub(crate) fn for_test(
2162        drivers: Vec<(super::EventDomainId, Arc<dyn super::EventDomainDriver>)>,
2163    ) -> Result<Self> {
2164        let mut runs = Vec::with_capacity(drivers.len());
2165        for (domain, driver) in drivers {
2166            let run = RuntimeOwnedEventDomainRun::new(domain, driver.begin_run(domain)?);
2167            let actual = run.domain(EventDomainOperation::BeginRun)?;
2168            if actual != domain {
2169                return Err(event_domain_error(EventDomainError::RunDomainMismatch {
2170                    operation: EventDomainOperation::BeginRun,
2171                    node_index: None,
2172                    expected: domain,
2173                    actual,
2174                }));
2175            }
2176            runs.push(run);
2177        }
2178        Ok(Self {
2179            runs,
2180            completions: HashMap::new(),
2181        })
2182    }
2183
2184    pub(crate) fn enqueue(
2185        &mut self,
2186        node_index: usize,
2187        node: &ScheduledNode,
2188        launch: &mut dyn FnMut() -> Result<()>,
2189    ) -> Result<()> {
2190        let completion = node.completion();
2191        let destination = completion.domain();
2192        let run_index = self.run_index(completion.domain())?;
2193        let actual_preflight_domain = self.runs[run_index].domain(EventDomainOperation::Enqueue)?;
2194        if actual_preflight_domain != destination {
2195            return Err(event_domain_error(EventDomainError::RunDomainMismatch {
2196                operation: EventDomainOperation::Enqueue,
2197                node_index: Some(node_index),
2198                expected: destination,
2199                actual: actual_preflight_domain,
2200            }));
2201        }
2202        let dependencies = self.classify_dependencies(node_index, node, destination)?;
2203        let actual_run_domain = self.runs[run_index].domain(EventDomainOperation::Enqueue)?;
2204        if actual_run_domain != destination {
2205            return Err(event_domain_error(EventDomainError::RunDomainMismatch {
2206                operation: EventDomainOperation::Enqueue,
2207                node_index: Some(node_index),
2208                expected: destination,
2209                actual: actual_run_domain,
2210            }));
2211        }
2212        let completion_event = self.runs[run_index].enqueue(&dependencies, launch)?;
2213        let actual = completion_event.origin();
2214        if actual != completion.domain() {
2215            return Err(event_domain_error(
2216                EventDomainError::CompletionTokenDomainMismatch {
2217                    operation: EventDomainOperation::ValidateCompletion,
2218                    node_index: Some(node_index),
2219                    expected: completion.domain(),
2220                    actual,
2221                },
2222            ));
2223        }
2224        self.completions.insert(
2225            EventDependency::from_completion(completion),
2226            completion_event,
2227        );
2228        Ok(())
2229    }
2230
2231    fn classify_dependencies(
2232        &self,
2233        node_index: usize,
2234        node: &ScheduledNode,
2235        destination: super::EventDomainId,
2236    ) -> Result<SmallVec<[Arc<dyn EventToken>; 4]>> {
2237        let mut admitted = SmallVec::with_capacity(node.dependencies().len());
2238        for dependency in node.dependencies() {
2239            let dependency_completion =
2240                self.completions.get(dependency).cloned().ok_or_else(|| {
2241                    Error::runtime_state_source(
2242                        "Runtime::run_compiled",
2243                        ErrorPhase::Execution,
2244                        MissingScheduledDependencyCompletionError {
2245                            dependency: *dependency,
2246                            node_index,
2247                        },
2248                    )
2249                })?;
2250            let actual = dependency_completion.origin();
2251            if actual != dependency.domain() {
2252                return Err(event_domain_error(
2253                    EventDomainError::DependencyDomainMismatch {
2254                        operation: match node {
2255                            ScheduledNode::Transfer(_) => EventDomainOperation::TransferBridge,
2256                            ScheduledNode::Operation(_)
2257                            | ScheduledNode::Collective(_)
2258                            | ScheduledNode::Barrier(_) => EventDomainOperation::Enqueue,
2259                        },
2260                        node_index: Some(node_index),
2261                        expected: dependency.domain(),
2262                        actual,
2263                    },
2264                ));
2265            }
2266            match node {
2267                ScheduledNode::Transfer(transfer) => {
2268                    let source = transfer.source_event_domain();
2269                    if actual == destination {
2270                        admitted.push(dependency_completion);
2271                    } else if actual == source {
2272                        dependency_completion.wait().map_err(|source_error| {
2273                            event_domain_error(EventDomainError::DependencyWaitFailed {
2274                                operation: EventDomainOperation::TransferBridge,
2275                                node_index: Some(node_index),
2276                                expected: destination,
2277                                actual,
2278                                source: Box::new(source_error),
2279                            })
2280                        })?;
2281                    } else {
2282                        return Err(event_domain_error(
2283                            EventDomainError::DependencyDomainMismatch {
2284                                operation: EventDomainOperation::TransferBridge,
2285                                node_index: Some(node_index),
2286                                expected: source,
2287                                actual,
2288                            },
2289                        ));
2290                    }
2291                }
2292                ScheduledNode::Operation(_)
2293                | ScheduledNode::Collective(_)
2294                | ScheduledNode::Barrier(_) => {
2295                    if actual != destination {
2296                        return Err(event_domain_error(
2297                            EventDomainError::DependencyDomainMismatch {
2298                                operation: EventDomainOperation::Enqueue,
2299                                node_index: Some(node_index),
2300                                expected: destination,
2301                                actual,
2302                            },
2303                        ));
2304                    }
2305                    admitted.push(dependency_completion);
2306                }
2307            }
2308        }
2309        Ok(admitted)
2310    }
2311
2312    fn run_index(&self, domain: super::EventDomainId) -> Result<usize> {
2313        if let Some(index) = self
2314            .runs
2315            .iter()
2316            .position(|run| run.requested_domain() == domain)
2317        {
2318            return Ok(index);
2319        }
2320        Err(missing_event_domain_driver(domain))
2321    }
2322
2323    pub(crate) fn drain(&mut self) -> Result<()> {
2324        let mut failures = Vec::new();
2325        for run in &mut self.runs {
2326            if let Err(error) = run.drain() {
2327                failures.push(error);
2328            }
2329        }
2330        let mut failures = failures.into_iter();
2331        let Some(mut error) = failures.next() else {
2332            return Ok(());
2333        };
2334        for failure in failures {
2335            error = Error::with_suppressed(error, failure);
2336        }
2337        Err(error)
2338    }
2339}
2340
2341#[derive(Debug)]
2342enum RuntimeOwnedEventDomainRunState {
2343    Pending(Box<dyn EventDomainRun>),
2344    Retired,
2345    Failed,
2346}
2347
2348#[derive(Clone, Copy, Debug, Eq, PartialEq)]
2349enum EventDomainRunTerminalState {
2350    Retired,
2351    Failed,
2352}
2353
2354impl fmt::Display for EventDomainRunTerminalState {
2355    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
2356        match self {
2357            Self::Retired => formatter.write_str("retired"),
2358            Self::Failed => formatter.write_str("failed"),
2359        }
2360    }
2361}
2362
2363#[derive(Debug)]
2364struct RuntimeOwnedEventDomainRun {
2365    requested_domain: super::EventDomainId,
2366    state: RuntimeOwnedEventDomainRunState,
2367}
2368
2369impl RuntimeOwnedEventDomainRun {
2370    fn new(requested_domain: super::EventDomainId, inner: Box<dyn EventDomainRun>) -> Self {
2371        Self {
2372            requested_domain,
2373            state: RuntimeOwnedEventDomainRunState::Pending(inner),
2374        }
2375    }
2376
2377    fn requested_domain(&self) -> super::EventDomainId {
2378        self.requested_domain
2379    }
2380
2381    fn domain(&self, operation: EventDomainOperation) -> Result<super::EventDomainId> {
2382        match &self.state {
2383            RuntimeOwnedEventDomainRunState::Pending(run) => Ok(run.domain()),
2384            RuntimeOwnedEventDomainRunState::Retired => Err(event_domain_run_state_error(
2385                operation,
2386                self.requested_domain,
2387                EventDomainRunTerminalState::Retired,
2388            )),
2389            RuntimeOwnedEventDomainRunState::Failed => Err(event_domain_run_state_error(
2390                operation,
2391                self.requested_domain,
2392                EventDomainRunTerminalState::Failed,
2393            )),
2394        }
2395    }
2396
2397    fn enqueue(
2398        &mut self,
2399        dependencies: &[Arc<dyn EventToken>],
2400        launch: &mut dyn FnMut() -> Result<()>,
2401    ) -> Result<Arc<dyn EventToken>> {
2402        match &mut self.state {
2403            RuntimeOwnedEventDomainRunState::Pending(run) => run.enqueue(dependencies, launch),
2404            RuntimeOwnedEventDomainRunState::Retired => Err(event_domain_run_state_error(
2405                EventDomainOperation::Enqueue,
2406                self.requested_domain,
2407                EventDomainRunTerminalState::Retired,
2408            )),
2409            RuntimeOwnedEventDomainRunState::Failed => Err(event_domain_run_state_error(
2410                EventDomainOperation::Enqueue,
2411                self.requested_domain,
2412                EventDomainRunTerminalState::Failed,
2413            )),
2414        }
2415    }
2416
2417    fn drain(&mut self) -> Result<()> {
2418        let run = match std::mem::replace(&mut self.state, RuntimeOwnedEventDomainRunState::Failed)
2419        {
2420            RuntimeOwnedEventDomainRunState::Pending(run) => run,
2421            RuntimeOwnedEventDomainRunState::Retired => {
2422                self.state = RuntimeOwnedEventDomainRunState::Retired;
2423                return Err(event_domain_run_state_error(
2424                    EventDomainOperation::Drain,
2425                    self.requested_domain,
2426                    EventDomainRunTerminalState::Retired,
2427                ));
2428            }
2429            RuntimeOwnedEventDomainRunState::Failed => {
2430                self.state = RuntimeOwnedEventDomainRunState::Failed;
2431                return Err(event_domain_run_state_error(
2432                    EventDomainOperation::Drain,
2433                    self.requested_domain,
2434                    EventDomainRunTerminalState::Failed,
2435                ));
2436            }
2437        };
2438        let domain = self.requested_domain;
2439        let mut run = run;
2440        let drain_result = catch_unwind(AssertUnwindSafe(|| run.drain()));
2441        drop_event_domain_run(run);
2442        match drain_result {
2443            Ok(Ok(())) => {
2444                self.state = RuntimeOwnedEventDomainRunState::Retired;
2445                Ok(())
2446            }
2447            Ok(Err(error)) => {
2448                self.state = RuntimeOwnedEventDomainRunState::Failed;
2449                Err(error)
2450            }
2451            Err(payload) => {
2452                self.state = RuntimeOwnedEventDomainRunState::Failed;
2453                Err(event_domain_error(EventDomainError::DrainPanicked {
2454                    operation: EventDomainOperation::Drain,
2455                    domain,
2456                    message: safe_event_domain_panic_message(payload),
2457                }))
2458            }
2459        }
2460    }
2461}
2462
2463/// Internal error returned when a runtime-owned event run is used after
2464/// explicit retirement or failure.
2465#[derive(Debug, thiserror::Error)]
2466#[error("{operation} used event-domain run {domain:?} after it reached terminal state {state}")]
2467pub(crate) struct EventDomainRunLifecycleError {
2468    operation: EventDomainOperation,
2469    domain: super::EventDomainId,
2470    state: EventDomainRunTerminalState,
2471}
2472
2473fn event_domain_run_state_error(
2474    operation: EventDomainOperation,
2475    domain: super::EventDomainId,
2476    state: EventDomainRunTerminalState,
2477) -> Error {
2478    Error::runtime_state_source(
2479        "Runtime::run_compiled",
2480        ErrorPhase::Execution,
2481        EventDomainRunLifecycleError {
2482            operation,
2483            domain,
2484            state,
2485        },
2486    )
2487}
2488
2489fn drop_event_domain_run(run: Box<dyn EventDomainRun>) {
2490    if catch_unwind(AssertUnwindSafe(|| drop(run))).is_err() {
2491        // The provider run has been consumed; its cleanup panic is contained.
2492    }
2493}
2494
2495impl Drop for RuntimeOwnedEventDomainRun {
2496    fn drop(&mut self) {
2497        let state = std::mem::replace(&mut self.state, RuntimeOwnedEventDomainRunState::Failed);
2498        if let RuntimeOwnedEventDomainRunState::Pending(run) = state {
2499            drop_event_domain_run(run);
2500        }
2501    }
2502}
2503
2504fn missing_event_domain_driver(domain: super::EventDomainId) -> Error {
2505    Error::from(EventDomainError::MissingDriver { domain })
2506}
2507
2508fn event_domain_error(source: EventDomainError) -> Error {
2509    Error::from(source)
2510}
2511
2512fn safe_event_domain_panic_message(payload: Box<dyn std::any::Any + Send + 'static>) -> String {
2513    match payload.downcast::<&'static str>() {
2514        Ok(message) => (*message).to_owned(),
2515        Err(payload) => match payload.downcast::<String>() {
2516            Ok(message) => *message,
2517            Err(_) => "non-string panic payload".to_owned(),
2518        },
2519    }
2520}
2521
2522fn scheduled_execution_cleanup_error(primary: Error, cleanup: Error) -> Error {
2523    Error::with_suppressed(primary, cleanup)
2524}
2525
2526fn instruction_execution<'a>(
2527    schedule: &'a ScheduledGraph,
2528    instruction: &ExecInstruction,
2529) -> Result<InstructionExecution<'a>> {
2530    let Some(operation_index) = instruction.semantic_operation_index else {
2531        let location = schedule.root_location();
2532        return Ok(InstructionExecution {
2533            witness: location.witness(),
2534            location,
2535        });
2536    };
2537    let location = schedule
2538        .operation_locations()
2539        .get(operation_index)
2540        .ok_or_else(|| {
2541            Error::runtime_state(
2542                "Runtime::run_compiled",
2543                ErrorPhase::Execution,
2544                format!(
2545                    "instruction references semantic operation {operation_index}, but prepared schedule has {} operations",
2546                    schedule.operation_locations().len()
2547                ),
2548            )
2549        })?;
2550    Ok(InstructionExecution {
2551        witness: location.witness(),
2552        location,
2553    })
2554}
2555
2556struct InstructionExecution<'a> {
2557    witness: &'a super::snapshot::ExecutableEngineSnapshot,
2558    location: &'a ExecutionLocation,
2559}
2560
2561impl InstructionExecution<'_> {
2562    fn executor(&self) -> &Arc<dyn ErasedTensorBackendExecutor> {
2563        self.witness.executor()
2564    }
2565
2566    fn location(&self) -> &ExecutionLocation {
2567        self.location
2568    }
2569}
2570
2571pub(crate) struct LocatedExecSlot<'input> {
2572    pub(crate) location: ExecutionLocation,
2573    pub(crate) value: ExecSlot<'input>,
2574}
2575
2576fn stage_instruction_inputs<'input>(
2577    instruction: &ExecInstruction,
2578    location: &ExecutionLocation,
2579    located: &mut [Vec<LocatedExecSlot<'input>>],
2580    staged: &mut [Option<ExecSlot<'input>>],
2581) -> Result<()> {
2582    for &slot in &instruction.input_slots {
2583        stage_slot_input(slot, location, located, staged)?;
2584    }
2585    Ok(())
2586}
2587
2588/// Move one slot's value from `located` into `staged`.
2589fn stage_slot_input<'input>(
2590    slot: usize,
2591    location: &ExecutionLocation,
2592    located: &mut [Vec<LocatedExecSlot<'input>>],
2593    staged: &mut [Option<ExecSlot<'input>>],
2594) -> Result<()> {
2595    if staged
2596        .get(slot)
2597        .ok_or(tenferro_tensor::Error::MissingValue { slot })?
2598        .is_some()
2599    {
2600        return Ok(());
2601    }
2602    let values = located
2603        .get_mut(slot)
2604        .ok_or(tenferro_tensor::Error::MissingValue { slot })?;
2605    let value_index = values
2606        .iter()
2607        .position(|value| &value.location == location)
2608        .ok_or(tenferro_tensor::Error::MissingValue { slot })?;
2609    staged[slot] = Some(values.swap_remove(value_index).value);
2610    Ok(())
2611}
2612
2613/// Validate every live-out of a fused elementwise region: present and resident
2614/// on the region's location.
2615fn validate_region_outputs(
2616    region: &super::region::ElementwiseRegion,
2617    location: &ExecutionLocation,
2618    staged: &[Option<ExecSlot<'_>>],
2619) -> Result<()> {
2620    for &output_slot in &region.output_slots {
2621        let output = staged
2622            .get(output_slot)
2623            .and_then(Option::as_ref)
2624            .ok_or(tenferro_tensor::Error::MissingValue { slot: output_slot })?;
2625        let output = output.as_read();
2626        if !location
2627            .witness()
2628            .owns_resident_tensor(&output, location.storage_class())
2629        {
2630            return Err(Error::runtime_state_source(
2631                "Runtime::run_prepared",
2632                ErrorPhase::Execution,
2633                super::EngineExecutionContractError::OutputResidencyMismatch {
2634                    instruction_index: region.instruction_range.start,
2635                    output_slot,
2636                    engine_id: location.engine_id().clone(),
2637                    storage_class: location.storage_class().clone(),
2638                    backend_family: output.backend_family(),
2639                    allocation_domain: output.allocation_domain(),
2640                },
2641            ));
2642        }
2643    }
2644    Ok(())
2645}
2646
2647/// Publish a fused region's results: release region inputs whose last use is
2648/// inside the region and move every live-out into `located`.
2649fn retain_region_results<'input>(
2650    region: &super::region::ElementwiseRegion,
2651    location: &ExecutionLocation,
2652    located: &mut [Vec<LocatedExecSlot<'input>>],
2653    staged: &mut [Option<ExecSlot<'input>>],
2654) -> Result<()> {
2655    for (&slot, &last_use) in region.input_slots.iter().zip(region.input_last_use.iter()) {
2656        if last_use {
2657            located[slot].clear();
2658            staged[slot].take();
2659        }
2660    }
2661    for &slot in &region.output_slots {
2662        let value = staged
2663            .get_mut(slot)
2664            .and_then(Option::take)
2665            .ok_or(tenferro_tensor::Error::MissingValue { slot })?;
2666        let values = located
2667            .get_mut(slot)
2668            .ok_or(tenferro_tensor::Error::MissingValue { slot })?;
2669        values.clear();
2670        values.push(LocatedExecSlot {
2671            location: location.clone(),
2672            value,
2673        });
2674    }
2675    Ok(())
2676}
2677
2678fn validate_instruction_outputs(
2679    instruction_index: usize,
2680    instruction: &ExecInstruction,
2681    location: &ExecutionLocation,
2682    staged: &[Option<ExecSlot<'_>>],
2683) -> Result<()> {
2684    for &output_slot in &instruction.output_slots {
2685        let output = staged
2686            .get(output_slot)
2687            .and_then(Option::as_ref)
2688            .ok_or(tenferro_tensor::Error::MissingValue { slot: output_slot })?;
2689        let output = output.as_read();
2690        if !location
2691            .witness()
2692            .owns_resident_tensor(&output, location.storage_class())
2693        {
2694            return Err(Error::runtime_state_source(
2695                "Runtime::run_compiled",
2696                ErrorPhase::Execution,
2697                super::EngineExecutionContractError::OutputResidencyMismatch {
2698                    instruction_index,
2699                    output_slot,
2700                    engine_id: location.engine_id().clone(),
2701                    storage_class: location.storage_class().clone(),
2702                    backend_family: output.backend_family(),
2703                    allocation_domain: output.allocation_domain(),
2704                },
2705            ));
2706        }
2707    }
2708    Ok(())
2709}
2710
2711pub(crate) fn retain_instruction_results<'input>(
2712    instruction: &ExecInstruction,
2713    location: &ExecutionLocation,
2714    located: &mut [Vec<LocatedExecSlot<'input>>],
2715    staged: &mut [Option<ExecSlot<'input>>],
2716) -> Result<()> {
2717    for &slot in &instruction.input_slots {
2718        let is_output = instruction.output_slots.contains(&slot);
2719        let is_last_use = instruction
2720            .input_slots
2721            .iter()
2722            .enumerate()
2723            .any(|(index, &candidate)| {
2724                candidate == slot && instruction.last_use.get(index).copied().unwrap_or(false)
2725            });
2726        if is_last_use {
2727            located[slot].clear();
2728            if !is_output {
2729                staged[slot].take();
2730            }
2731        } else if !is_output && let Some(value) = staged[slot].take() {
2732            located[slot].push(LocatedExecSlot {
2733                location: location.clone(),
2734                value,
2735            });
2736        }
2737    }
2738
2739    for &slot in &instruction.output_slots {
2740        let value = staged
2741            .get_mut(slot)
2742            .and_then(Option::take)
2743            .ok_or(tenferro_tensor::Error::MissingValue { slot })?;
2744        let values = located
2745            .get_mut(slot)
2746            .ok_or(tenferro_tensor::Error::MissingValue { slot })?;
2747        values.clear();
2748        values.push(LocatedExecSlot {
2749            location: location.clone(),
2750            value,
2751        });
2752    }
2753    Ok(())
2754}
2755
2756fn validate_runtime_input_ingress(
2757    location: &ExecutionLocation,
2758    input: &TensorRead<'_>,
2759    slot: usize,
2760) -> Result<()> {
2761    let accepted = location
2762        .witness()
2763        .accepts_runtime_input(input, location.storage_class());
2764    if accepted {
2765        return Ok(());
2766    }
2767    Err(Error::runtime_state_source(
2768        "Runtime::run_compiled",
2769        ErrorPhase::Execution,
2770        super::InputIngressContractError::ResidencyMismatch {
2771            input_slot: slot,
2772            ingress_engine_id: location.engine_id().clone(),
2773            ingress_storage_class: location.storage_class().clone(),
2774            placement: input.placement().clone(),
2775            backend_family: input.backend_family(),
2776            allocation_domain: input.allocation_domain(),
2777        },
2778    ))
2779}
2780
2781fn execute_scheduled_transfer<'input>(
2782    transfer: &ScheduledTransfer,
2783    located: &mut [Vec<LocatedExecSlot<'input>>],
2784) -> Result<()> {
2785    let source = transfer.source_location();
2786    let destination = transfer.destination_location();
2787    let provider = transfer.provider();
2788    let values =
2789        located
2790            .get_mut(transfer.value_slot())
2791            .ok_or(tenferro_tensor::Error::MissingValue {
2792                slot: transfer.value_slot(),
2793            })?;
2794    if values.iter().any(|value| &value.location == destination) {
2795        return Err(Error::runtime_state_source(
2796            "Runtime::run_compiled",
2797            ErrorPhase::Execution,
2798            DuplicateTransferDestinationError {
2799                value_slot: transfer.value_slot(),
2800                destination: destination.clone(),
2801            },
2802        ));
2803    }
2804    let transferred = {
2805        let source_value = values
2806            .iter()
2807            .find(|value| &value.location == source)
2808            .ok_or(tenferro_tensor::Error::MissingValue {
2809                slot: transfer.value_slot(),
2810            })?;
2811        let source_read = source_value.value.as_read();
2812        let expected_dtype = source_read.dtype();
2813        let expected_shape = source_read.shape().to_vec();
2814        let transferred =
2815            provider.transfer_blocking(TransferRequest::new(source, destination, source_read))?;
2816        validate_transfer_output(destination, expected_dtype, &expected_shape, &transferred)?;
2817        transferred
2818    };
2819    values.push(LocatedExecSlot {
2820        location: destination.clone(),
2821        value: ExecSlot::Owned(transferred),
2822    });
2823    Ok(())
2824}
2825
2826fn validate_transfer_output(
2827    destination: &ExecutionLocation,
2828    expected_dtype: tenferro_tensor::DType,
2829    expected_shape: &[usize],
2830    output: &Tensor,
2831) -> Result<()> {
2832    let expected_elements = checked_transfer_element_count(expected_shape).map_err(|source| {
2833        Error::runtime_state_source(
2834            "Runtime::run_compiled",
2835            ErrorPhase::Execution,
2836            TransferError::ProviderContract { source },
2837        )
2838    })?;
2839    let contract_error = if output.dtype() != expected_dtype {
2840        Some(TransferProviderContractError::DTypeMismatch {
2841            expected: expected_dtype,
2842            actual: output.dtype(),
2843        })
2844    } else if output.shape() != expected_shape {
2845        Some(TransferProviderContractError::ShapeMismatch {
2846            expected: expected_shape.to_vec(),
2847            actual: output.shape().to_vec(),
2848        })
2849    } else if tensor_buffer_len(output) != expected_elements {
2850        Some(TransferProviderContractError::InvalidBufferLength {
2851            expected: expected_elements,
2852            actual: tensor_buffer_len(output),
2853        })
2854    } else if !destination
2855        .witness()
2856        .accepts_input_placement(output.placement(), destination.storage_class())
2857    {
2858        Some(
2859            TransferProviderContractError::DestinationPlacementMismatch {
2860                destination_engine_id: destination.engine_id().clone(),
2861                destination_storage_class: destination.storage_class().clone(),
2862                actual: output.placement().clone(),
2863            },
2864        )
2865    } else if !destination.witness().owns_resident_tensor(
2866        &TensorRead::from_tensor(output),
2867        destination.storage_class(),
2868    ) {
2869        Some(
2870            TransferProviderContractError::DestinationResidencyMismatch {
2871                destination_engine_id: destination.engine_id().clone(),
2872                destination_storage_class: destination.storage_class().clone(),
2873                actual_backend_family: TensorRead::from_tensor(output).backend_family(),
2874                actual_allocation_domain: TensorRead::from_tensor(output).allocation_domain(),
2875            },
2876        )
2877    } else {
2878        None
2879    };
2880    match contract_error {
2881        None => Ok(()),
2882        Some(source) => Err(Error::runtime_state_source(
2883            "Runtime::run_compiled",
2884            ErrorPhase::Execution,
2885            TransferError::ProviderContract { source },
2886        )),
2887    }
2888}
2889
2890fn checked_transfer_element_count(
2891    shape: &[usize],
2892) -> std::result::Result<usize, TransferProviderContractError> {
2893    tenferro_tensor::validate::checked_shape_product(
2894        "Runtime::run_compiled",
2895        "transfer source shape",
2896        shape,
2897    )
2898    .map_err(|source| TransferProviderContractError::LogicalElementCount { source })
2899}
2900
2901fn tensor_buffer_len(tensor: &Tensor) -> usize {
2902    match tensor.dtype() {
2903        DType::F32 => tensor.as_typed::<f32>().map_or(0, |t| t.buffer().len()),
2904        DType::F64 => tensor.as_typed::<f64>().map_or(0, |t| t.buffer().len()),
2905        DType::I32 => tensor.as_typed::<i32>().map_or(0, |t| t.buffer().len()),
2906        DType::I64 => tensor.as_typed::<i64>().map_or(0, |t| t.buffer().len()),
2907        DType::Bool => tensor.as_typed::<bool>().map_or(0, |t| t.buffer().len()),
2908        DType::C32 => tensor
2909            .as_typed::<Complex32>()
2910            .map_or(0, |t| t.buffer().len()),
2911        DType::C64 => tensor
2912            .as_typed::<Complex64>()
2913            .map_or(0, |t| t.buffer().len()),
2914        // A caller-owned payload has no runtime buffer length to report, which is
2915        // also what the accessor falls back to when the tag table and the runtime
2916        // dtype disagree.
2917        DType::External(_) => 0,
2918    }
2919}
2920
2921#[cfg(test)]
2922mod transfer_validation_tests {
2923    use std::error::Error as _;
2924
2925    use super::checked_transfer_element_count;
2926    use crate::TransferProviderContractError;
2927
2928    #[test]
2929    fn transfer_element_count_overflow_is_typed_and_preserves_source() {
2930        let error = checked_transfer_element_count(&[usize::MAX, 2]).unwrap_err();
2931
2932        assert!(matches!(
2933            error,
2934            TransferProviderContractError::LogicalElementCount { .. }
2935        ));
2936        assert!(error.source().is_some());
2937    }
2938}
2939
2940fn collect_located_outputs<'input>(
2941    program: &ExecProgram,
2942    located: &mut [Vec<LocatedExecSlot<'input>>],
2943) -> Result<Vec<Option<LocatedExecSlot<'input>>>> {
2944    let mut outputs = (0..program.n_slots).map(|_| None).collect::<Vec<_>>();
2945    for &slot in &program.output_slots {
2946        if outputs[slot].is_some() {
2947            continue;
2948        }
2949        let values = located
2950            .get_mut(slot)
2951            .ok_or(tenferro_tensor::Error::MissingValue { slot })?;
2952        let value = values
2953            .pop()
2954            .ok_or(tenferro_tensor::Error::MissingValue { slot })?;
2955        values.clear();
2956        outputs[slot] = Some(value);
2957    }
2958    located.iter_mut().for_each(Vec::clear);
2959    Ok(outputs)
2960}
2961
2962pub(super) fn collect_tensor_outputs_with<'input>(
2963    program: &ExecProgram,
2964    outputs: &mut [Option<LocatedExecSlot<'input>>],
2965    mut materialize: impl FnMut(&ExecutionLocation, ExecSlot<'input>) -> Result<Tensor>,
2966) -> Result<Vec<Tensor>> {
2967    program
2968        .output_slots
2969        .iter()
2970        .map(|&slot| {
2971            let located = outputs
2972                .get_mut(slot)
2973                .and_then(Option::take)
2974                .ok_or(tenferro_tensor::Error::MissingValue { slot })?;
2975            materialize(&located.location, located.value)
2976        })
2977        .collect()
2978}
2979
2980fn collect_value_outputs_with<'input>(
2981    program: &ExecProgram,
2982    outputs: &mut [Option<LocatedExecSlot<'input>>],
2983    mut materialize: impl FnMut(&ExecutionLocation, ExecSlot<'input>) -> Result<TensorValue>,
2984) -> Result<Vec<TensorValue>> {
2985    program
2986        .output_slots
2987        .iter()
2988        .map(|&slot| {
2989            let located = outputs
2990                .get_mut(slot)
2991                .and_then(Option::take)
2992                .ok_or(tenferro_tensor::Error::MissingValue { slot })?;
2993            materialize(&located.location, located.value)
2994        })
2995        .collect()
2996}
2997
2998fn input_signature_reads(inputs: &[TensorRead<'_>]) -> Result<InputSignature> {
2999    InputSignature::from_reads(inputs).map_err(|source| prepare_error(Arc::new(source)))
3000}
3001
3002fn resolve_input_refs<'a>(
3003    program: &'a CompiledGraph,
3004    inputs: &'a [&'a Tensor],
3005) -> Result<RuntimeInputReads<'a>> {
3006    let expected = program.program().inputs().len();
3007    if inputs.len() > expected {
3008        return Err(Error::GraphInputCountMismatch {
3009            expected,
3010            actual: inputs.len(),
3011        });
3012    }
3013    let resolved = if inputs.is_empty() {
3014        semantic_default_inputs(program)?
3015    } else if inputs.len() == expected {
3016        inputs
3017            .iter()
3018            .map(|tensor| TensorRead::from_tensor(tensor))
3019            .collect()
3020    } else {
3021        let mut explicit = inputs.iter();
3022        let mut resolved = RuntimeInputReads::new();
3023        for value in program.program().inputs() {
3024            if let Some(retained) = program.bindings().tensor_ref_for_input(*value) {
3025                resolved.push(retained.tensor_read()?);
3026            } else {
3027                let tensor = explicit.next().ok_or_else(|| {
3028                    Error::invalid_argument(
3029                        "Runtime::resolve_input_refs",
3030                        ErrorPhase::Execution,
3031                        "inputs",
3032                        format!(
3033                            "expected {expected} inputs, or one explicit input for each unbound placeholder"
3034                        ),
3035                    )
3036                })?;
3037                resolved.push(TensorRead::from_tensor(tensor));
3038            }
3039        }
3040        if explicit.next().is_some() {
3041            return Err(Error::invalid_argument(
3042                "Runtime::resolve_input_refs",
3043                ErrorPhase::Execution,
3044                "inputs",
3045                format!("expected {expected} inputs, received {}", inputs.len()),
3046            ));
3047        }
3048        resolved
3049    };
3050    validate_ordered_input_metadata_reads(program, &resolved)?;
3051    Ok(resolved)
3052}
3053
3054fn semantic_default_inputs(program: &CompiledGraph) -> Result<RuntimeInputReads<'_>> {
3055    program
3056        .program()
3057        .inputs()
3058        .iter()
3059        .enumerate()
3060        .map(|(input_index, value)| {
3061            let retained = program
3062                .bindings()
3063                .tensor_ref_for_input(*value)
3064                .ok_or_else(|| Error::UnboundPlaceholder {
3065                    input_key: format!("semantic input {input_index}"),
3066                })?;
3067            retained.tensor_read()
3068        })
3069        .collect()
3070}
3071
3072fn validate_ordered_input_metadata_reads(
3073    program: &CompiledGraph,
3074    inputs: &[TensorRead<'_>],
3075) -> Result<()> {
3076    let actuals = inputs
3077        .iter()
3078        .map(|input| (input.dtype(), input.shape().to_vec()))
3079        .collect::<Vec<_>>();
3080    validate_ordered_input_metadata_values(program, &actuals, "Runtime::submit")
3081}
3082
3083fn validate_ordered_input_metadata_values(
3084    program: &CompiledGraph,
3085    actuals: &[(tenferro_tensor::DType, Vec<usize>)],
3086    caller: &'static str,
3087) -> Result<()> {
3088    let expected = program.input_count();
3089    if actuals.len() != expected {
3090        return Err(Error::GraphInputCountMismatch {
3091            expected,
3092            actual: actuals.len(),
3093        });
3094    }
3095    let input_shapes: RuntimeInputShapes<'_> =
3096        actuals.iter().map(|(_, shape)| shape.as_slice()).collect();
3097    for (input_value, (actual_dtype, actual_shape)) in
3098        program.program().inputs().iter().zip(actuals)
3099    {
3100        let metadata = program
3101            .program()
3102            .value_metadata(*input_value)
3103            .map_err(|source| Error::runtime_state_source(caller, ErrorPhase::Execution, source))?;
3104        if metadata.dtype() != *actual_dtype {
3105            return Err(Error::PlaceholderDtypeMismatch {
3106                expected: metadata.dtype(),
3107                actual: *actual_dtype,
3108            });
3109        }
3110        if metadata.shape().len() != actual_shape.len() {
3111            return Err(Error::PlaceholderRankMismatch {
3112                expected: metadata.shape().len(),
3113                actual: actual_shape.len(),
3114            });
3115        }
3116        let mut expected_shape: RuntimeShapeScratch = actual_shape.iter().copied().collect();
3117        let mut exact_mismatch = false;
3118        for (axis, (extent, actual_size)) in metadata.shape().iter().zip(actual_shape).enumerate() {
3119            match extent {
3120                ShapeExtent::Exact(expression) => {
3121                    let expected = expression.eval(&input_shapes).map_err(|source| {
3122                        Error::runtime_state_source(caller, ErrorPhase::Execution, source)
3123                    })?;
3124                    expected_shape[axis] = expected;
3125                    exact_mismatch |= expected != *actual_size;
3126                }
3127                ShapeExtent::UpperBound(expression) => {
3128                    let bound = expression.eval(&input_shapes).map_err(|source| {
3129                        Error::runtime_state_source(caller, ErrorPhase::Execution, source)
3130                    })?;
3131                    if *actual_size > bound {
3132                        return Err(Error::PlaceholderShapeBoundExceeded {
3133                            axis,
3134                            bound,
3135                            actual: *actual_size,
3136                        });
3137                    }
3138                }
3139                ShapeExtent::Unknown => {}
3140            }
3141        }
3142        if exact_mismatch {
3143            return Err(Error::PlaceholderShapeMismatch {
3144                expected: expected_shape.into_vec(),
3145                actual: actual_shape.clone(),
3146            });
3147        }
3148    }
3149    Ok(())
3150}
3151
3152fn validate_exec_input_count(program: &ExecProgram, actual: usize) -> Result<()> {
3153    let expected = program.input_slots.len();
3154    if actual != expected {
3155        return Err(Error::runtime_state(
3156            "Runtime::run_compiled",
3157            ErrorPhase::Execution,
3158            format!("expected {expected} inputs for execution program, got {actual}"),
3159        ));
3160    }
3161    Ok(())
3162}
3163
3164fn prepare_error(source: Arc<PrepareError>) -> Error {
3165    Error::runtime_state_source(
3166        "Runtime::run_compiled",
3167        ErrorPhase::Execution,
3168        SharedPrepareError(source),
3169    )
3170}
3171
3172#[derive(Clone, Debug)]
3173struct SharedPrepareError(Arc<PrepareError>);
3174
3175impl fmt::Display for SharedPrepareError {
3176    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
3177        write!(formatter, "{}", self.0)
3178    }
3179}
3180
3181impl StdError for SharedPrepareError {
3182    fn source(&self) -> Option<&(dyn StdError + 'static)> {
3183        Some(self.0.as_ref())
3184    }
3185}
3186
3187#[cfg(test)]
3188mod tests {
3189    use std::any::Any;
3190    use std::error::Error as StdError;
3191    use std::hash::Hasher;
3192    use std::num::NonZeroU64;
3193    use std::sync::atomic::{AtomicBool, Ordering};
3194    use std::sync::{Arc, TryLockError, Weak};
3195
3196    use tenferro_cpu::CpuBackend;
3197    use tenferro_ops::dim_expr::DimExpr;
3198    use tenferro_ops::ext_op::ExtensionOp;
3199    use tenferro_ops::SymDim;
3200    use tenferro_tensor::{
3201        BackendSession, BackendSessionHost, DType, Tensor, TensorBackend, TensorRead, TypedTensor,
3202    };
3203
3204    use crate::exec::{ExecInstruction, ExecOp, ExecProgram, ExecSlot};
3205    use crate::runtime::{
3206        CoreCapabilityBundle, EngineId, ErasedExecutionContext, EventDomainDriver, EventDomainId,
3207        ExecutableEngineContract, ExecutionContextIdentity, HardwareClassId, InputIngressContract,
3208        InputSignature, PreparedOperation, PreparedOperationBinding, PreparedOperationExecutor,
3209        PreparedOperationPlan, ProviderDeviceIdentity, ProviderId, RegistrationIdentity,
3210        RuntimeCacheOwner, RuntimeEpoch, RuntimeId, SpecializationProjection,
3211        SpecializationRequirements, StorageClass,
3212    };
3213    use crate::{Error, ErrorPhase, ExtensionCacheStore, Result};
3214
3215    use super::{
3216        DuplicateTransferDestinationError, ErasedTensorBackendExecutor, LocatedExecSlot,
3217        TensorBackendExecutor,
3218    };
3219    use crate::runtime::schedule::{
3220        EventCompletion, EventSlotId, ExecutionLocation, ScheduledTransfer,
3221    };
3222
3223    const LOCK_PROBE_FAMILY: &str = "runtime.lock-probe.v1";
3224    const REENTRANT_PROBE_FAMILY: &str = "runtime.reentrant-probe.v1";
3225
3226    #[derive(Clone, Debug)]
3227    struct LockProbeOp;
3228
3229    impl ExtensionOp for LockProbeOp {
3230        fn family_id(&self) -> &'static str {
3231            LOCK_PROBE_FAMILY
3232        }
3233
3234        fn payload_hash(&self, _hasher: &mut dyn Hasher) {}
3235
3236        fn payload_eq(&self, other: &dyn ExtensionOp) -> bool {
3237            other.as_any().downcast_ref::<Self>().is_some()
3238        }
3239
3240        fn clone_arc(&self) -> Arc<dyn ExtensionOp> {
3241            Arc::new(self.clone())
3242        }
3243
3244        fn as_any(&self) -> &dyn Any {
3245            self
3246        }
3247
3248        fn input_count(&self) -> usize {
3249            1
3250        }
3251
3252        fn output_count(&self) -> usize {
3253            1
3254        }
3255
3256        fn infer_output_meta(
3257            &self,
3258            ctx: &mut tenferro_ops::ExtensionShapeContext<'_>,
3259        ) -> tenferro_tensor::Result<Vec<(DType, Vec<SymDim>)>> {
3260            Ok(vec![(ctx.input_dtype(0)?, ctx.input_shape(0)?.to_vec())])
3261        }
3262    }
3263
3264    #[derive(Clone, Debug)]
3265    struct ReentrantProbeOp;
3266
3267    impl ExtensionOp for ReentrantProbeOp {
3268        fn family_id(&self) -> &'static str {
3269            REENTRANT_PROBE_FAMILY
3270        }
3271
3272        fn payload_hash(&self, _hasher: &mut dyn Hasher) {}
3273
3274        fn payload_eq(&self, other: &dyn ExtensionOp) -> bool {
3275            other.as_any().downcast_ref::<Self>().is_some()
3276        }
3277
3278        fn clone_arc(&self) -> Arc<dyn ExtensionOp> {
3279            Arc::new(self.clone())
3280        }
3281
3282        fn as_any(&self) -> &dyn Any {
3283            self
3284        }
3285
3286        fn input_count(&self) -> usize {
3287            1
3288        }
3289
3290        fn output_count(&self) -> usize {
3291            1
3292        }
3293
3294        fn infer_output_meta(
3295            &self,
3296            ctx: &mut tenferro_ops::ExtensionShapeContext<'_>,
3297        ) -> tenferro_tensor::Result<Vec<(DType, Vec<SymDim>)>> {
3298            Ok(vec![(ctx.input_dtype(0)?, ctx.input_shape(0)?.to_vec())])
3299        }
3300    }
3301
3302    #[derive(Debug)]
3303    struct LockProbePreparedOperation {
3304        binding: PreparedOperationBinding,
3305        specialization: SpecializationProjection,
3306        executor: Weak<TensorBackendExecutor<CpuBackend>>,
3307        observed_unlocked_state: Arc<AtomicBool>,
3308    }
3309
3310    impl PreparedOperation for LockProbePreparedOperation {
3311        fn binding(&self) -> &PreparedOperationBinding {
3312            &self.binding
3313        }
3314
3315        fn specialization(&self) -> &SpecializationProjection {
3316            &self.specialization
3317        }
3318
3319        fn retained_bytes(&self) -> usize {
3320            0
3321        }
3322    }
3323
3324    impl PreparedOperationExecutor for LockProbePreparedOperation {
3325        fn execute(
3326            &self,
3327            context: &mut ErasedExecutionContext<'_>,
3328            _extension_caches: &mut ExtensionCacheStore,
3329            inputs: &[TensorRead<'_>],
3330        ) -> Result<Vec<Tensor>> {
3331            let executor = self.executor.upgrade().expect("executor still alive");
3332            let unlocked = match executor.state.try_lock() {
3333                Ok(_guard) => true,
3334                Err(TryLockError::WouldBlock) => false,
3335                Err(TryLockError::Poisoned(_)) => false,
3336            };
3337            self.observed_unlocked_state
3338                .store(unlocked, Ordering::SeqCst);
3339            let backend = context
3340                .downcast_mut::<CpuBackend>(self.binding.context_identity())
3341                .map_err(|source| {
3342                    Error::runtime_state_source("lock_probe", ErrorPhase::Execution, source)
3343                })?;
3344            Ok(vec![backend.with_backend_session(|exec| {
3345                exec.to_contiguous_read(inputs[0].clone())
3346            })?])
3347        }
3348    }
3349
3350    #[derive(Debug)]
3351    struct ReentrantProbePreparedOperation {
3352        binding: PreparedOperationBinding,
3353        specialization: SpecializationProjection,
3354        executor: Weak<TensorBackendExecutor<CpuBackend>>,
3355        observed_reentrant_error: Arc<AtomicBool>,
3356    }
3357
3358    #[derive(Debug)]
3359    struct SessionProbePreparedOperation {
3360        binding: PreparedOperationBinding,
3361        specialization: SpecializationProjection,
3362        observed_session: Arc<AtomicBool>,
3363    }
3364
3365    impl PreparedOperation for SessionProbePreparedOperation {
3366        fn binding(&self) -> &PreparedOperationBinding {
3367            &self.binding
3368        }
3369
3370        fn specialization(&self) -> &SpecializationProjection {
3371            &self.specialization
3372        }
3373
3374        fn retained_bytes(&self) -> usize {
3375            0
3376        }
3377    }
3378
3379    impl PreparedOperationExecutor for SessionProbePreparedOperation {
3380        fn execute(
3381            &self,
3382            _context: &mut ErasedExecutionContext<'_>,
3383            _extension_caches: &mut ExtensionCacheStore,
3384            _inputs: &[TensorRead<'_>],
3385        ) -> Result<Vec<Tensor>> {
3386            Err(Error::unsupported(
3387                "session_probe",
3388                ErrorPhase::Execution,
3389                "session probe must use the scheduler-owned session path",
3390            ))
3391        }
3392
3393        fn supports_session(&self) -> bool {
3394            true
3395        }
3396
3397        fn execute_in_session(
3398            &self,
3399            session: &mut dyn BackendSession,
3400            _extension_caches: &mut ExtensionCacheStore,
3401            inputs: &[TensorRead<'_>],
3402        ) -> Result<Vec<Tensor>> {
3403            self.observed_session.store(true, Ordering::SeqCst);
3404            Ok(vec![session.to_contiguous_read(inputs[0].clone())?])
3405        }
3406    }
3407
3408    impl ReentrantProbePreparedOperation {
3409        fn probe_reentrant_call(&self, input: Tensor) {
3410            let executor = self.executor.upgrade().expect("executor still alive");
3411            let nested = ErasedTensorBackendExecutor::execute(
3412                executor.as_ref(),
3413                &passthrough_program(),
3414                &[],
3415                vec![input],
3416            );
3417            let observed = nested.is_err_and(|error| {
3418                error
3419                    .to_string()
3420                    .contains("reentrant tensor backend executor call would deadlock")
3421            });
3422            self.observed_reentrant_error
3423                .store(observed, Ordering::SeqCst);
3424        }
3425    }
3426
3427    impl PreparedOperation for ReentrantProbePreparedOperation {
3428        fn binding(&self) -> &PreparedOperationBinding {
3429            &self.binding
3430        }
3431
3432        fn specialization(&self) -> &SpecializationProjection {
3433            &self.specialization
3434        }
3435
3436        fn retained_bytes(&self) -> usize {
3437            0
3438        }
3439    }
3440
3441    impl PreparedOperationExecutor for ReentrantProbePreparedOperation {
3442        fn execute(
3443            &self,
3444            context: &mut ErasedExecutionContext<'_>,
3445            _extension_caches: &mut ExtensionCacheStore,
3446            inputs: &[TensorRead<'_>],
3447        ) -> Result<Vec<Tensor>> {
3448            let backend = context
3449                .downcast_mut::<CpuBackend>(self.binding.context_identity())
3450                .map_err(|source| {
3451                    Error::runtime_state_source("reentrant_probe", ErrorPhase::Execution, source)
3452                })?;
3453            let materialized =
3454                backend.with_backend_session(|exec| exec.to_contiguous_read(inputs[0].clone()))?;
3455            self.probe_reentrant_call(materialized.duplicate()?);
3456            Ok(vec![materialized])
3457        }
3458    }
3459
3460    fn lock_probe_program() -> ExecProgram {
3461        ExecProgram {
3462            instructions: vec![ExecInstruction {
3463                op: ExecOp::Extension(Arc::new(LockProbeOp)),
3464                semantic_operation_index: Some(0),
3465                input_slots: vec![0],
3466                output_slots: vec![1],
3467                dtype: DType::F64,
3468                output_shapes: vec![vec![DimExpr::InputDim {
3469                    input_idx: 0,
3470                    axis: 0,
3471                }]]
3472                .into(),
3473                output_extents: vec![vec![]].into(),
3474                last_use: vec![false],
3475            }],
3476            input_slots: vec![0],
3477            output_slots: vec![1],
3478            n_slots: 2,
3479            shape_guards: vec![],
3480        }
3481    }
3482
3483    fn partial_session_probe_program() -> ExecProgram {
3484        let output_shape = || {
3485            vec![DimExpr::InputDim {
3486                input_idx: 0,
3487                axis: 0,
3488            }]
3489        };
3490        ExecProgram {
3491            instructions: vec![
3492                ExecInstruction {
3493                    op: ExecOp::Extension(Arc::new(LockProbeOp)),
3494                    semantic_operation_index: Some(0),
3495                    input_slots: vec![0],
3496                    output_slots: vec![1],
3497                    dtype: DType::F64,
3498                    output_shapes: vec![output_shape()].into(),
3499                    output_extents: vec![vec![]].into(),
3500                    last_use: vec![false],
3501                },
3502                ExecInstruction {
3503                    op: ExecOp::Extension(Arc::new(LockProbeOp)),
3504                    semantic_operation_index: Some(1),
3505                    input_slots: vec![1],
3506                    output_slots: vec![2],
3507                    dtype: DType::F64,
3508                    output_shapes: vec![output_shape()].into(),
3509                    output_extents: vec![vec![]].into(),
3510                    last_use: vec![false],
3511                },
3512            ],
3513            input_slots: vec![0],
3514            output_slots: vec![2],
3515            n_slots: 3,
3516            shape_guards: vec![],
3517        }
3518    }
3519
3520    fn reentrant_probe_program() -> ExecProgram {
3521        ExecProgram {
3522            instructions: vec![ExecInstruction {
3523                op: ExecOp::Extension(Arc::new(ReentrantProbeOp)),
3524                semantic_operation_index: Some(0),
3525                input_slots: vec![0],
3526                output_slots: vec![1],
3527                dtype: DType::F64,
3528                output_shapes: vec![vec![DimExpr::InputDim {
3529                    input_idx: 0,
3530                    axis: 0,
3531                }]]
3532                .into(),
3533                output_extents: vec![vec![]].into(),
3534                last_use: vec![false],
3535            }],
3536            input_slots: vec![0],
3537            output_slots: vec![1],
3538            n_slots: 2,
3539            shape_guards: vec![],
3540        }
3541    }
3542
3543    fn passthrough_program() -> ExecProgram {
3544        ExecProgram {
3545            instructions: vec![],
3546            input_slots: vec![0],
3547            output_slots: vec![0],
3548            n_slots: 1,
3549            shape_guards: vec![],
3550        }
3551    }
3552
3553    fn f64_zeros(shape: Vec<usize>) -> Tensor {
3554        Tensor::from_typed::<f64>(TypedTensor::zeros(shape).unwrap())
3555    }
3556
3557    fn nz(value: u64) -> NonZeroU64 {
3558        NonZeroU64::new(value).unwrap_or(NonZeroU64::MIN)
3559    }
3560
3561    fn probe_binding() -> PreparedOperationBinding {
3562        PreparedOperationBinding::new(
3563            RuntimeId::from_nonzero(nz(1)),
3564            RuntimeEpoch::from_nonzero(nz(2)),
3565            EngineId::new("tenferro.cpu").unwrap(),
3566            RegistrationIdentity::new(nz(3), nz(4)),
3567            ExecutionContextIdentity::of::<CpuBackend>(),
3568            HardwareClassId::new("tenferro.cpu.host").unwrap(),
3569        )
3570    }
3571
3572    fn probe_specialization() -> SpecializationProjection {
3573        SpecializationRequirements::polymorphic(0)
3574            .project(&InputSignature::new(Vec::new()))
3575            .unwrap()
3576    }
3577
3578    #[test]
3579    fn tensor_backend_executor_releases_state_lock_during_extension_execution() {
3580        let executor = Arc::new(TensorBackendExecutor::<CpuBackend>::new(CpuBackend::new()));
3581        let observed_unlocked_state = Arc::new(AtomicBool::new(false));
3582        let prepared = Arc::new(LockProbePreparedOperation {
3583            binding: probe_binding(),
3584            specialization: probe_specialization(),
3585            executor: Arc::downgrade(&executor),
3586            observed_unlocked_state: Arc::clone(&observed_unlocked_state),
3587        });
3588        let operations = vec![PreparedOperationPlan::executable(
3589            prepared.clone(),
3590            prepared,
3591        )];
3592
3593        let output = ErasedTensorBackendExecutor::execute(
3594            executor.as_ref(),
3595            &lock_probe_program(),
3596            &operations,
3597            vec![f64_zeros(vec![2])],
3598        )
3599        .expect("extension executes");
3600
3601        assert_eq!(output[0].shape(), &[2]);
3602        assert!(
3603            observed_unlocked_state.load(Ordering::SeqCst),
3604            "executor state lock must not be held while extension runtime callbacks execute"
3605        );
3606    }
3607
3608    #[test]
3609    fn scheduled_execution_cleanup_error_preserves_primary_error() {
3610        let primary = Error::runtime_state(
3611            "primary-execution",
3612            ErrorPhase::Execution,
3613            "primary execution failure",
3614        );
3615        let cleanup = Error::runtime_state(
3616            "event-domain-cleanup",
3617            ErrorPhase::Execution,
3618            "cleanup failure",
3619        );
3620        let primary_display = primary.to_string();
3621
3622        let combined = super::scheduled_execution_cleanup_error(primary, cleanup);
3623        let primary_error = combined.primary().expect("primary execution error");
3624        let cleanup_error = combined.suppressed().expect("cleanup error");
3625        assert_eq!(primary_error.to_string(), primary_display);
3626        assert_eq!(
3627            cleanup_error.to_string(),
3628            "event-domain-cleanup (Execution): runtime state failure: cleanup failure"
3629        );
3630        assert_eq!(
3631            StdError::source(&combined)
3632                .expect("primary error in the standard source chain")
3633                .to_string(),
3634            primary_display
3635        );
3636    }
3637
3638    #[test]
3639    fn duplicate_scheduled_transfer_destination_reports_typed_fields() {
3640        let domain = EventDomainId::runtime_created_for_test(
3641            RuntimeId::from_nonzero(nz(1)),
3642            RuntimeEpoch::from_nonzero(nz(1)),
3643            RegistrationIdentity::new(nz(1), nz(1)),
3644        );
3645        let source = ExecutionLocation::new(
3646            EngineId::new("tenferro-test.transfer-source").expect("source engine"),
3647            ProviderDeviceIdentity::new(
3648                ProviderId::new("tenferro-test.transfer").expect("provider id"),
3649                "source",
3650            )
3651            .expect("source provider target"),
3652            domain,
3653            StorageClass::new("tenferro-test.transfer-source").expect("source storage"),
3654        );
3655        let destination = ExecutionLocation::new(
3656            EngineId::new("tenferro-test.transfer-destination").expect("destination engine"),
3657            ProviderDeviceIdentity::new(
3658                ProviderId::new("tenferro-test.transfer").expect("provider id"),
3659                "destination",
3660            )
3661            .expect("destination provider target"),
3662            domain,
3663            StorageClass::new("tenferro-test.transfer-destination").expect("destination storage"),
3664        );
3665        let transfer = ScheduledTransfer::new(
3666            3,
3667            source,
3668            destination.clone(),
3669            [],
3670            EventCompletion::new(domain, EventSlotId::new(0), 0),
3671        );
3672        let mut located = vec![
3673            Vec::new(),
3674            Vec::new(),
3675            Vec::new(),
3676            vec![LocatedExecSlot {
3677                location: destination.clone(),
3678                value: ExecSlot::Owned(f64_zeros(vec![1])),
3679            }],
3680        ];
3681
3682        let error = super::execute_scheduled_transfer(&transfer, &mut located)
3683            .expect_err("duplicate transfer destination");
3684        let Error::RuntimeStateSource { source, .. } = error else {
3685            panic!("duplicate transfer destination must retain a typed source");
3686        };
3687        let duplicate = source
3688            .downcast_ref::<DuplicateTransferDestinationError>()
3689            .expect("typed duplicate transfer destination source");
3690        assert_eq!(duplicate.value_slot, 3);
3691        assert_eq!(duplicate.destination, destination);
3692    }
3693
3694    #[test]
3695    fn tensor_backend_executor_reentrant_call_returns_error_instead_of_deadlocking() {
3696        let executor = Arc::new(TensorBackendExecutor::<CpuBackend>::new(CpuBackend::new()));
3697        let observed_reentrant_error = Arc::new(AtomicBool::new(false));
3698        let prepared = Arc::new(ReentrantProbePreparedOperation {
3699            binding: probe_binding(),
3700            specialization: probe_specialization(),
3701            executor: Arc::downgrade(&executor),
3702            observed_reentrant_error: Arc::clone(&observed_reentrant_error),
3703        });
3704        let operations = vec![PreparedOperationPlan::executable(
3705            prepared.clone(),
3706            prepared,
3707        )];
3708
3709        let output = ErasedTensorBackendExecutor::execute(
3710            executor.as_ref(),
3711            &reentrant_probe_program(),
3712            &operations,
3713            vec![f64_zeros(vec![2])],
3714        )
3715        .expect("outer extension executes");
3716
3717        assert_eq!(output[0].shape(), &[2]);
3718        assert!(
3719            observed_reentrant_error.load(Ordering::SeqCst),
3720            "same-thread reentrant executor call must fail immediately instead of deadlocking"
3721        );
3722    }
3723
3724    #[test]
3725    fn tensor_backend_executor_dispatches_session_capable_extension_in_one_session_path() {
3726        let executor = TensorBackendExecutor::<CpuBackend>::new(CpuBackend::new());
3727        let observed_session = Arc::new(AtomicBool::new(false));
3728        let prepared = Arc::new(SessionProbePreparedOperation {
3729            binding: probe_binding(),
3730            specialization: probe_specialization(),
3731            observed_session: Arc::clone(&observed_session),
3732        });
3733        let operations = vec![PreparedOperationPlan::executable(
3734            prepared.clone(),
3735            prepared,
3736        )];
3737
3738        let output = ErasedTensorBackendExecutor::execute(
3739            &executor,
3740            &lock_probe_program(),
3741            &operations,
3742            vec![f64_zeros(vec![2])],
3743        )
3744        .expect("session-capable extension executes");
3745
3746        assert_eq!(output[0].shape(), &[2]);
3747        assert!(observed_session.load(Ordering::SeqCst));
3748    }
3749
3750    #[test]
3751    fn tensor_backend_executor_batches_session_capable_region_after_boundary() {
3752        let executor = Arc::new(TensorBackendExecutor::<CpuBackend>::new(CpuBackend::new()));
3753        let observed_unlocked_state = Arc::new(AtomicBool::new(false));
3754        let observed_session = Arc::new(AtomicBool::new(false));
3755        let ordinary = Arc::new(LockProbePreparedOperation {
3756            binding: probe_binding(),
3757            specialization: probe_specialization(),
3758            executor: Arc::downgrade(&executor),
3759            observed_unlocked_state: Arc::clone(&observed_unlocked_state),
3760        });
3761        let session = Arc::new(SessionProbePreparedOperation {
3762            binding: probe_binding(),
3763            specialization: probe_specialization(),
3764            observed_session: Arc::clone(&observed_session),
3765        });
3766        let operations = vec![
3767            PreparedOperationPlan::executable(ordinary.clone(), ordinary),
3768            PreparedOperationPlan::executable(session.clone(), session),
3769        ];
3770
3771        let output = ErasedTensorBackendExecutor::execute(
3772            executor.as_ref(),
3773            &partial_session_probe_program(),
3774            &operations,
3775            vec![f64_zeros(vec![2])],
3776        )
3777        .expect("mixed session regions execute");
3778
3779        assert_eq!(output[0].shape(), &[2]);
3780        assert!(observed_unlocked_state.load(Ordering::SeqCst));
3781        assert!(observed_session.load(Ordering::SeqCst));
3782
3783        let values = ErasedTensorBackendExecutor::execute_values(
3784            executor.as_ref(),
3785            &partial_session_probe_program(),
3786            &operations,
3787            vec![f64_zeros(vec![2])],
3788        )
3789        .expect("mixed session regions execute in value mode");
3790        assert_eq!(values[0].shape(), &[2]);
3791    }
3792
3793    #[test]
3794    fn tensor_backend_executor_bridge_does_not_require_clone_source_contract() {
3795        type ContractConstructor<B> = fn(
3796            ProviderDeviceIdentity,
3797            CoreCapabilityBundle,
3798            B,
3799            Arc<dyn EventDomainDriver>,
3800            InputIngressContract,
3801            Option<Arc<dyn RuntimeCacheOwner>>,
3802        ) -> ExecutableEngineContract;
3803
3804        fn factory_accepts_backend_without_clone_bound<B>()
3805        where
3806            B: TensorBackend + Send + Sync + 'static,
3807        {
3808            let _factory: fn(B) -> Arc<dyn ErasedTensorBackendExecutor> =
3809                super::erased_tensor_backend_executor::<B>;
3810        }
3811
3812        fn contract_accepts_backend_without_clone_bound<B>()
3813        where
3814            B: TensorBackend + Send + Sync + 'static,
3815        {
3816            let _constructor: ContractConstructor<B> = ExecutableEngineContract::new::<B>;
3817        }
3818
3819        factory_accepts_backend_without_clone_bound::<CpuBackend>();
3820        contract_accepts_backend_without_clone_bound::<CpuBackend>();
3821    }
3822}