vyre_runtime/
persistent_executor.rs1use std::collections::BTreeMap;
4
5use vyre_driver::{BackendError, BackendRegistration, Completion, DeviceIdentity};
6use vyre_foundation::diagnostics::RetryClass;
7use vyre_megakernel::{ArtifactValueId, Digest};
8
9use crate::artifact_admission::{ArtifactSession, ArtifactSessionError, RetainedArtifactSession};
10use crate::recovery::classify_backend_error;
11
12#[derive(Clone, Debug, PartialEq, Eq)]
14pub struct ResidentQueueState {
15 pub control: Vec<u8>,
17 pub ring: Vec<u8>,
19 pub debug_log: Vec<u8>,
21 pub io_queue: Vec<u8>,
23}
24
25#[derive(Clone, Debug, PartialEq, Eq)]
27pub struct ResidentQueueCompletion {
28 pub state: ResidentQueueState,
30 pub device_ns: Option<u64>,
32}
33
34#[derive(Clone, Copy)]
35struct ResidentQueueAbi {
36 control: ArtifactValueId,
37 ring: ArtifactValueId,
38 debug_log: ArtifactValueId,
39 io_queue: ArtifactValueId,
40}
41
42impl ResidentQueueAbi {
43 fn resolve(session: &ArtifactSession) -> Result<Self, ArtifactSessionError> {
44 Ok(Self {
45 control: session.resource("control")?,
46 ring: session.resource("ring_buffer")?,
47 debug_log: session.resource("debug_log")?,
48 io_queue: session.resource("io_queue")?,
49 })
50 }
51
52 fn bind(self, state: ResidentQueueState) -> BTreeMap<ArtifactValueId, Vec<u8>> {
53 BTreeMap::from([
54 (self.control, state.control),
55 (self.ring, state.ring),
56 (self.debug_log, state.debug_log),
57 (self.io_queue, state.io_queue),
58 ])
59 }
60
61 fn completion(
62 self,
63 completion: Completion,
64 ) -> Result<ResidentQueueCompletion, ArtifactSessionError> {
65 let mut retained = completion.retained;
66 let mut take = |value: ArtifactValueId,
67 name: &str|
68 -> Result<Vec<u8>, ArtifactSessionError> {
69 retained.remove(&value).ok_or_else(|| {
70 BackendError::InvalidProgram {
71 fix: format!(
72 "Fix: persistent artifact completion must return retained queue resource `{name}`."
73 ),
74 }
75 .into()
76 })
77 };
78 let state = ResidentQueueState {
79 control: take(self.control, "control")?,
80 ring: take(self.ring, "ring_buffer")?,
81 debug_log: take(self.debug_log, "debug_log")?,
82 io_queue: take(self.io_queue, "io_queue")?,
83 };
84 if !retained.is_empty() {
85 return Err(BackendError::InvalidProgram {
86 fix: "Fix: persistent queue artifacts must expose exactly control, ring_buffer, debug_log, and io_queue as retained values.".to_string(),
87 }
88 .into());
89 }
90 Ok(ResidentQueueCompletion {
91 state,
92 device_ns: completion.device_ns,
93 })
94 }
95}
96
97pub struct PersistentExecutor {
99 session: RetainedArtifactSession,
100 abi: ResidentQueueAbi,
101}
102
103impl PersistentExecutor {
104 pub fn from_bytes(
106 registration: &'static BackendRegistration,
107 envelope_bytes: &[u8],
108 initial: ResidentQueueState,
109 ) -> Result<Self, ArtifactSessionError> {
110 let session = ArtifactSession::from_bytes(registration, envelope_bytes)?;
111 let abi = ResidentQueueAbi::resolve(&session)?;
112 let session = RetainedArtifactSession::new(session, abi.bind(initial))?;
113 Ok(Self { session, abi })
114 }
115
116 pub fn artifact(&self) -> Result<Digest, ArtifactSessionError> {
118 self.session.artifact()
119 }
120
121 pub fn device(&self) -> Result<DeviceIdentity, ArtifactSessionError> {
123 self.session.device()
124 }
125
126 pub fn submit_and_wait(
128 &self,
129 state: ResidentQueueState,
130 ) -> Result<ResidentQueueCompletion, ArtifactSessionError> {
131 self.session.replace_retained(self.abi.bind(state))?;
132 let bindings = self.session.bindings()?;
133 let completion = self.session.submit_and_wait(bindings)?;
134 self.abi.completion(completion)
135 }
136
137 pub fn recover(&self, failure: BackendError) -> Result<DeviceIdentity, ArtifactSessionError> {
143 if classify_backend_error(&failure) != RetryClass::NewDevice {
144 return Err(failure.into());
145 }
146 self.session.rematerialize()
147 }
148}