Skip to main content

vyre_runtime/megakernel/
resident.rs

1//! Host mirrors for resident work-queue runtime buffers.
2
3use super::io;
4use super::planner::ResidentWorkItem;
5use super::protocol;
6use super::protocol_api::{validate_control_bytes, validate_debug_log_bytes};
7use super::readback::ResidentQueueReadback;
8use super::scheduler::write_default_priority_offsets;
9use super::ResidentWorkQueue;
10use crate::PipelineError;
11
12/// Host-side mirror of the four buffers kept resident by the persistent
13/// megakernel runtime: control, ring, debug log, and IO queue.
14#[derive(Debug)]
15pub struct ResidentQueueBuffers {
16    control_bytes: Vec<u8>,
17    ring_bytes: Vec<u8>,
18    debug_log_bytes: Vec<u8>,
19    io_queue_bytes: Vec<u8>,
20    slot_count: u32,
21}
22
23impl Clone for ResidentQueueBuffers {
24    fn clone(&self) -> Self {
25        Self {
26            control_bytes: self.control_bytes.clone(),
27            ring_bytes: self.ring_bytes.clone(),
28            debug_log_bytes: self.debug_log_bytes.clone(),
29            io_queue_bytes: self.io_queue_bytes.clone(),
30            slot_count: self.slot_count,
31        }
32    }
33}
34
35impl PartialEq for ResidentQueueBuffers {
36    fn eq(&self, other: &Self) -> bool {
37        self.control_bytes == other.control_bytes
38            && self.ring_bytes == other.ring_bytes
39            && self.debug_log_bytes == other.debug_log_bytes
40            && self.io_queue_bytes == other.io_queue_bytes
41            && self.slot_count == other.slot_count
42    }
43}
44
45impl Eq for ResidentQueueBuffers {}
46
47impl ResidentQueueBuffers {
48    /// Allocate a fresh host mirror for a megakernel's resident buffers.
49    ///
50    /// # Errors
51    ///
52    /// Returns [`PipelineError`] when any runtime buffer size overflows.
53    pub fn new(
54        slot_count: u32,
55        tenant_count: u32,
56        observable_slots: u32,
57    ) -> Result<Self, PipelineError> {
58        let control_capacity = protocol::control_byte_len(observable_slots).ok_or_else(|| {
59            PipelineError::Backend(
60                "megakernel resident control byte length overflowed usize. Fix: shard observable resident buffers before allocation."
61                    .to_string(),
62            )
63        })?;
64        let ring_capacity = protocol::ring_byte_len(slot_count).ok_or_else(|| {
65            PipelineError::Backend(
66                "megakernel resident ring byte length overflowed usize. Fix: shard resident rings before allocation."
67                    .to_string(),
68            )
69        })?;
70        let debug_log_capacity =
71            protocol::debug_log_byte_len(protocol::debug::RECORD_CAPACITY).ok_or_else(|| {
72                PipelineError::Backend(
73                    "megakernel resident debug-log byte length overflowed usize. Fix: reduce debug record capacity before allocation."
74                        .to_string(),
75                )
76        })?;
77        let io_queue_capacity = io::empty_io_queue_byte_len(io::IO_SLOT_COUNT)?;
78        let mut control_bytes = Vec::new();
79        reserve_resident_bytes(
80            &mut control_bytes,
81            control_capacity,
82            "control",
83            "shard observable resident buffers before allocation",
84        )?;
85        let mut ring_bytes = Vec::new();
86        reserve_resident_bytes(
87            &mut ring_bytes,
88            ring_capacity,
89            "ring",
90            "shard resident rings before allocation",
91        )?;
92        let mut debug_log_bytes = Vec::new();
93        reserve_resident_bytes(
94            &mut debug_log_bytes,
95            debug_log_capacity,
96            "debug-log",
97            "reduce debug record capacity before allocation",
98        )?;
99        let mut io_queue_bytes = Vec::new();
100        reserve_resident_bytes(
101            &mut io_queue_bytes,
102            io_queue_capacity,
103            "io-queue",
104            "reduce resident IO queue capacity before allocation",
105        )?;
106        let mut buffers = Self {
107            control_bytes,
108            ring_bytes,
109            debug_log_bytes,
110            io_queue_bytes,
111            slot_count,
112        };
113        buffers.reset(tenant_count, observable_slots)?;
114        Ok(buffers)
115    }
116
117    /// Reinitialize this host mirror in place for the same resident geometry.
118    ///
119    /// # Errors
120    ///
121    /// Returns [`PipelineError`] when any runtime buffer size overflows.
122    pub fn reset(&mut self, tenant_count: u32, observable_slots: u32) -> Result<(), PipelineError> {
123        ResidentWorkQueue::try_encode_control_into(
124            false,
125            tenant_count,
126            observable_slots,
127            &mut self.control_bytes,
128        )?;
129        write_default_priority_offsets(&mut self.control_bytes, self.slot_count)?;
130        ResidentWorkQueue::try_encode_empty_ring_into(self.slot_count, &mut self.ring_bytes)?;
131        ResidentWorkQueue::try_encode_empty_debug_log_into(
132            protocol::debug::RECORD_CAPACITY,
133            &mut self.debug_log_bytes,
134        )?;
135        io::try_encode_empty_io_queue_into(io::IO_SLOT_COUNT, &mut self.io_queue_bytes)?;
136        Ok(())
137    }
138
139    /// Build a resident-buffer mirror from caller-owned byte buffers.
140    ///
141    /// # Errors
142    ///
143    /// Returns [`PipelineError`] when any buffer violates the megakernel ABI.
144    pub fn from_parts(
145        slot_count: u32,
146        control_bytes: Vec<u8>,
147        ring_bytes: Vec<u8>,
148        debug_log_bytes: Vec<u8>,
149        io_queue_bytes: Vec<u8>,
150    ) -> Result<Self, PipelineError> {
151        validate_control_bytes(&control_bytes)?;
152        validate_debug_log_bytes(&debug_log_bytes)?;
153        io::validate_io_queue_bytes(&io_queue_bytes)?;
154        let expected_ring_bytes = protocol::ring_byte_len(slot_count).ok_or_else(|| {
155            PipelineError::Backend(
156                "megakernel resident ring byte length overflowed usize. Fix: shard resident rings before allocation."
157                    .to_string(),
158            )
159        })?;
160        if ring_bytes.len() != expected_ring_bytes {
161            return Err(PipelineError::Backend(format!(
162                "megakernel resident ring has {} bytes, expected {expected_ring_bytes}. Fix: build resident rings with the same slot_count as the Megakernel handle.",
163                ring_bytes.len()
164            )));
165        }
166        Ok(Self {
167            control_bytes,
168            ring_bytes,
169            debug_log_bytes,
170            io_queue_bytes,
171            slot_count,
172        })
173    }
174
175    /// Publish one work slot into the resident ring mirror.
176    ///
177    /// # Errors
178    ///
179    /// Returns [`PipelineError::QueueFull`] when the slot is out of bounds or
180    /// still in flight.
181    pub fn publish_slot(
182        &mut self,
183        slot_idx: u32,
184        tenant_id: u32,
185        opcode: u32,
186        args: &[u32],
187    ) -> Result<(), PipelineError> {
188        ResidentWorkQueue::publish_slot(&mut self.ring_bytes, slot_idx, tenant_id, opcode, args)
189    }
190
191    /// Publish a contiguous fixed-ABI work-item window into the resident ring
192    /// mirror without resetting unrelated slots.
193    ///
194    /// # Errors
195    ///
196    /// Returns [`PipelineError::QueueFull`] when the target slots are outside
197    /// the resident ring, still in flight, or contain an unpublished opcode.
198    pub fn publish_work_items(
199        &mut self,
200        start_slot: u32,
201        tenant_id: u32,
202        items: &[ResidentWorkItem],
203    ) -> Result<u32, PipelineError> {
204        ResidentWorkQueue::publish_work_items(&mut self.ring_bytes, start_slot, tenant_id, items)
205    }
206
207    /// Apply a strict dispatch readback to the resident host mirror.
208    pub fn apply_readback(&mut self, readback: ResidentQueueReadback) {
209        self.control_bytes = readback.control_bytes;
210        self.ring_bytes = readback.ring_bytes;
211        self.debug_log_bytes = readback.debug_log_bytes;
212        self.io_queue_bytes = readback.io_queue_bytes;
213    }
214
215    /// Clone the current host mirror into a strict readback record.
216    #[must_use]
217    pub fn snapshot_readback(&self) -> ResidentQueueReadback {
218        ResidentQueueReadback {
219            control_bytes: self.control_bytes.clone(),
220            ring_bytes: self.ring_bytes.clone(),
221            debug_log_bytes: self.debug_log_bytes.clone(),
222            io_queue_bytes: self.io_queue_bytes.clone(),
223        }
224    }
225
226    /// Clone the current host mirror into caller-owned readback storage.
227    pub fn snapshot_readback_into(&self, out: &mut ResidentQueueReadback) {
228        out.control_bytes.clone_from(&self.control_bytes);
229        out.ring_bytes.clone_from(&self.ring_bytes);
230        out.debug_log_bytes.clone_from(&self.debug_log_bytes);
231        out.io_queue_bytes.clone_from(&self.io_queue_bytes);
232    }
233
234    /// Control-buffer mirror bytes.
235    #[must_use]
236    pub fn control_bytes(&self) -> &[u8] {
237        &self.control_bytes
238    }
239
240    /// Ring-buffer mirror bytes.
241    #[must_use]
242    pub fn ring_bytes(&self) -> &[u8] {
243        &self.ring_bytes
244    }
245
246    /// Mutable ring-buffer mirror bytes.
247    #[must_use]
248    pub fn ring_bytes_mut(&mut self) -> &mut [u8] {
249        &mut self.ring_bytes
250    }
251
252    /// Debug-log mirror bytes.
253    #[must_use]
254    pub fn debug_log_bytes(&self) -> &[u8] {
255        &self.debug_log_bytes
256    }
257
258    /// IO-queue mirror bytes.
259    #[must_use]
260    pub fn io_queue_bytes(&self) -> &[u8] {
261        &self.io_queue_bytes
262    }
263
264    /// Resident ring slot count.
265    #[must_use]
266    pub const fn slot_count(&self) -> u32 {
267        self.slot_count
268    }
269}
270
271fn reserve_resident_bytes(
272    bytes: &mut Vec<u8>,
273    capacity: usize,
274    label: &'static str,
275    fix: &'static str,
276) -> Result<(), PipelineError> {
277    vyre_foundation::allocation::try_reserve_vec_to_capacity(bytes, capacity).map_err(|error| {
278        PipelineError::Backend(format!(
279            "megakernel resident {label} byte reservation failed for {capacity} bytes: {error}. Fix: {fix}."
280        ))
281    })
282}