Skip to main content

prns_runtime_embassy/runtime/node_facade/node_lifecycle/
mod.rs

1use core::future::Future;
2use core::mem::MaybeUninit;
3
4use embassy_futures::join::join;
5use embassy_sync::blocking_mutex::raw::RawMutex;
6use embassy_sync::channel::{Channel, Receiver};
7use embedded_storage_async::nor_flash::NorFlash;
8use heapless::Vec as HeaplessVec;
9use static_cell::StaticCell;
10
11use crate::engine::{IssuedCommand, Journaled, MAX_SEND_REQUEST_DATA_LEN};
12use crate::interfaces::{InterfaceDescriptor, InterfaceId, InterfaceIfac};
13use crate::manifold::driver::{
14    run_pooled, InterfaceLifecycle, PooledEgress, PooledWiring, ResumableHost,
15};
16use crate::manifold::grant::ManifoldLaneReader;
17use crate::manifold::Host;
18use crate::storage::StorageLayout;
19
20use super::super::request_endpoints::RequestEndpointSet;
21use super::super::request_runner::{run_router, RunnerRequest};
22use super::super::{
23    EmbassyInterfaceStore, EmbeddedFlashPersistence, EmbeddedPersistenceDiagnostic,
24    EmbeddedPersistenceRestoreReport, InterfaceInspectionStore, ManifoldPersistence,
25    ManuallyAttached, NoInterfaceInspectionStore, NoManifoldPersistence, PreConfiguredDestination,
26    PrnsEvent, PrnsNodeRecipe, RouteSnapshotKeys,
27};
28use super::command_handle::PrnsNodeHandle;
29use prns_runtime::runtime::placement::assemble_node_in_place;
30use prns_runtime::runtime::{assemble_node, AssembledNode, NoPersistence};
31
32pub struct ManifoldWiring<
33    M,
34    const LANE_COUNT: usize,
35    const NOTIFY: usize,
36    const COMMANDS: usize,
37    const LIFECYCLE: usize,
38    const COMPLETIONS: usize,
39> where
40    M: RawMutex + 'static,
41{
42    pub(super) inbound: HeaplessVec<(InterfaceId, &'static mut dyn ManifoldLaneReader), LANE_COUNT>,
43    pub(super) egress: PooledEgress<LANE_COUNT>,
44    pub(super) initial: HeaplessVec<InterfaceDescriptor, LANE_COUNT>,
45    pub(super) ifacs: HeaplessVec<InterfaceIfac, LANE_COUNT>,
46    pub(super) notify: Receiver<'static, M, InterfaceId, NOTIFY>,
47    pub(super) commands: Receiver<'static, M, IssuedCommand, COMMANDS>,
48    pub(super) lifecycle: Receiver<'static, M, InterfaceLifecycle, LIFECYCLE>,
49    pub(super) handle: PrnsNodeHandle<'static, M, COMMANDS, COMPLETIONS>,
50}
51
52pub struct PrnsNode<
53    St,
54    R,
55    F,
56    S,
57    H,
58    M,
59    const LANE_COUNT: usize,
60    const INTERFACE_CAPACITY: usize,
61    const NOTIFY: usize,
62    const COMMANDS: usize,
63    const LIFECYCLE: usize,
64    const COMPLETIONS: usize,
65    const ROUTED_REQUESTS: usize = 4,
66    const ROUTED_REQUEST_BYTES: usize = MAX_SEND_REQUEST_DATA_LEN,
67> where
68    S: StorageLayout,
69    M: RawMutex + 'static,
70{
71    node: AssembledNode<St, R, F, S>,
72    inbound: HeaplessVec<(InterfaceId, &'static mut dyn ManifoldLaneReader), LANE_COUNT>,
73    egress: PooledEgress<LANE_COUNT>,
74    notify: Receiver<'static, M, InterfaceId, NOTIFY>,
75    commands: Receiver<'static, M, IssuedCommand, COMMANDS>,
76    lifecycle: Receiver<'static, M, InterfaceLifecycle, LIFECYCLE>,
77    handle: PrnsNodeHandle<'static, M, COMMANDS, COMPLETIONS>,
78    host: H,
79    descriptors: HeaplessVec<InterfaceDescriptor, INTERFACE_CAPACITY>,
80    ifacs: HeaplessVec<InterfaceIfac, LANE_COUNT>,
81}
82
83pub struct RequestRoutingCapacity<const REQUESTS: usize, const REQUEST_BYTES: usize>;
84
85impl<const REQUESTS: usize, const REQUEST_BYTES: usize> Default
86    for RequestRoutingCapacity<REQUESTS, REQUEST_BYTES>
87{
88    fn default() -> Self {
89        Self::new()
90    }
91}
92
93impl<const REQUESTS: usize, const REQUEST_BYTES: usize>
94    RequestRoutingCapacity<REQUESTS, REQUEST_BYTES>
95{
96    #[must_use]
97    pub const fn new() -> Self {
98        Self
99    }
100}
101
102impl<
103        St,
104        R,
105        F,
106        S,
107        H,
108        M,
109        const LANE_COUNT: usize,
110        const INTERFACE_CAPACITY: usize,
111        const NOTIFY: usize,
112        const COMMANDS: usize,
113        const LIFECYCLE: usize,
114        const COMPLETIONS: usize,
115    >
116    PrnsNode<
117        St,
118        R,
119        F,
120        S,
121        H,
122        M,
123        LANE_COUNT,
124        INTERFACE_CAPACITY,
125        NOTIFY,
126        COMMANDS,
127        LIFECYCLE,
128        COMPLETIONS,
129        4,
130        MAX_SEND_REQUEST_DATA_LEN,
131    >
132where
133    R: RequestEndpointSet<St>,
134    F: FnMut(PrnsEvent<'_>, &St),
135    S: StorageLayout,
136    H: Host,
137    M: RawMutex + 'static,
138{
139    pub fn new<'d, D>(
140        recipe: PrnsNodeRecipe<D, St, R, F, ManuallyAttached, S>,
141        wiring: ManifoldWiring<M, LANE_COUNT, NOTIFY, COMMANDS, LIFECYCLE, COMPLETIONS>,
142        host: H,
143    ) -> Self
144    where
145        D: IntoIterator<Item = PreConfiguredDestination<'d>>,
146    {
147        Self::build(recipe, wiring, host)
148    }
149}
150
151impl<
152        St,
153        R,
154        F,
155        S,
156        H,
157        M,
158        const LANE_COUNT: usize,
159        const INTERFACE_CAPACITY: usize,
160        const NOTIFY: usize,
161        const COMMANDS: usize,
162        const LIFECYCLE: usize,
163        const COMPLETIONS: usize,
164        const ROUTED_REQUESTS: usize,
165        const ROUTED_REQUEST_BYTES: usize,
166    >
167    PrnsNode<
168        St,
169        R,
170        F,
171        S,
172        H,
173        M,
174        LANE_COUNT,
175        INTERFACE_CAPACITY,
176        NOTIFY,
177        COMMANDS,
178        LIFECYCLE,
179        COMPLETIONS,
180        ROUTED_REQUESTS,
181        ROUTED_REQUEST_BYTES,
182    >
183where
184    R: RequestEndpointSet<St>,
185    F: FnMut(PrnsEvent<'_>, &St),
186    S: StorageLayout,
187    H: Host,
188    M: RawMutex + 'static,
189{
190    pub fn init_static<'d, D>(
191        cell: &'static StaticCell<Self>,
192        recipe: PrnsNodeRecipe<D, St, R, F, ManuallyAttached, S>,
193        wiring: ManifoldWiring<M, LANE_COUNT, NOTIFY, COMMANDS, LIFECYCLE, COMPLETIONS>,
194        host: H,
195    ) -> &'static mut Self
196    where
197        D: IntoIterator<Item = PreConfiguredDestination<'d>>,
198    {
199        let (node, NoPersistence) = Self::init_static_with_persistence(cell, recipe, wiring, host);
200        node
201    }
202
203    #[expect(
204        unsafe_code,
205        clippy::undocumented_unsafe_blocks,
206        clippy::mut_from_ref,
207        reason = "every PrnsNode field is initialized before the slot is exposed"
208    )]
209    pub fn init_static_with_persistence<'d, D, P>(
210        cell: &'static StaticCell<Self>,
211        recipe: PrnsNodeRecipe<D, St, R, F, ManuallyAttached, S, P>,
212        wiring: ManifoldWiring<M, LANE_COUNT, NOTIFY, COMMANDS, LIFECYCLE, COMPLETIONS>,
213        host: H,
214    ) -> (&'static mut Self, P)
215    where
216        D: IntoIterator<Item = PreConfiguredDestination<'d>>,
217    {
218        const {
219            assert!(
220                INTERFACE_CAPACITY >= LANE_COUNT,
221                "PrnsNode INTERFACE_CAPACITY must cover every manifold lane"
222            );
223        }
224        let slot = cell.uninit();
225        let ManifoldWiring {
226            inbound,
227            egress,
228            initial,
229            ifacs,
230            notify,
231            commands,
232            lifecycle,
233            handle,
234        } = wiring;
235        let node = slot.as_mut_ptr();
236        let persistence = unsafe {
237            let assembled = &mut *core::ptr::addr_of_mut!((*node).node)
238                .cast::<MaybeUninit<AssembledNode<St, R, F, S>>>();
239            let (_, ManuallyAttached, persistence) = assemble_node_in_place(assembled, recipe);
240            core::ptr::addr_of_mut!((*node).inbound).write(inbound);
241            core::ptr::addr_of_mut!((*node).egress).write(egress);
242            core::ptr::addr_of_mut!((*node).notify).write(notify);
243            core::ptr::addr_of_mut!((*node).commands).write(commands);
244            core::ptr::addr_of_mut!((*node).lifecycle).write(lifecycle);
245            core::ptr::addr_of_mut!((*node).handle).write(handle);
246            core::ptr::addr_of_mut!((*node).host).write(host);
247            core::ptr::addr_of_mut!((*node).descriptors).write(HeaplessVec::new());
248            core::ptr::addr_of_mut!((*node).ifacs).write(ifacs);
249            persistence
250        };
251        let node = unsafe { slot.assume_init_mut() };
252        for descriptor in initial {
253            if node.descriptors.push(descriptor).is_err() {
254                unreachable!()
255            }
256        }
257        (node, persistence)
258    }
259
260    pub fn new_with_request_capacity<'d, D>(
261        recipe: PrnsNodeRecipe<D, St, R, F, ManuallyAttached, S>,
262        wiring: ManifoldWiring<M, LANE_COUNT, NOTIFY, COMMANDS, LIFECYCLE, COMPLETIONS>,
263        host: H,
264        _capacity: RequestRoutingCapacity<ROUTED_REQUESTS, ROUTED_REQUEST_BYTES>,
265    ) -> Self
266    where
267        D: IntoIterator<Item = PreConfiguredDestination<'d>>,
268    {
269        Self::build(recipe, wiring, host)
270    }
271
272    fn build<'d, D>(
273        recipe: PrnsNodeRecipe<D, St, R, F, ManuallyAttached, S>,
274        wiring: ManifoldWiring<M, LANE_COUNT, NOTIFY, COMMANDS, LIFECYCLE, COMPLETIONS>,
275        host: H,
276    ) -> Self
277    where
278        D: IntoIterator<Item = PreConfiguredDestination<'d>>,
279    {
280        const {
281            assert!(
282                INTERFACE_CAPACITY >= LANE_COUNT,
283                "PrnsNode INTERFACE_CAPACITY must cover every manifold lane"
284            );
285        }
286        let (node, ManuallyAttached, NoPersistence) = assemble_node(recipe);
287        let mut descriptors = HeaplessVec::new();
288        for descriptor in wiring.initial {
289            if descriptors.push(descriptor).is_err() {
290                unreachable!()
291            }
292        }
293
294        PrnsNode {
295            node,
296            inbound: wiring.inbound,
297            egress: wiring.egress,
298            notify: wiring.notify,
299            commands: wiring.commands,
300            lifecycle: wiring.lifecycle,
301            handle: wiring.handle,
302            host,
303            descriptors,
304            ifacs: wiring.ifacs,
305        }
306    }
307
308    pub fn set_protocol_policy(&mut self, policy: crate::engine::EngineProtocolPolicy) {
309        self.node.engine.set_protocol_policy(policy);
310    }
311
312    #[must_use]
313    pub fn handle(&self) -> PrnsNodeHandle<'static, M, COMMANDS, COMPLETIONS> {
314        self.handle
315    }
316
317    /// Runs the manifold with the caller's interface and supervisor tasks.
318    pub async fn run(self, drive: impl Future<Output = ()>) {
319        self.run_with_inspection_store(&NoInterfaceInspectionStore, drive)
320            .await;
321    }
322
323    pub async fn run_with_interface_store<
324        const INTERFACES: usize,
325        const PACKET_PHY_CAPACITY: usize,
326        const PACKET_PHY_INDEX_BUCKETS: usize,
327    >(
328        self,
329        store: &EmbassyInterfaceStore<M, INTERFACES, PACKET_PHY_CAPACITY, PACKET_PHY_INDEX_BUCKETS>,
330        drive: impl Future<Output = ()>,
331    ) where
332        M: Sync,
333    {
334        const {
335            assert!(
336                INTERFACES >= INTERFACE_CAPACITY,
337                "EmbassyInterfaceStore INTERFACES must cover PrnsNode INTERFACE_CAPACITY"
338            );
339        }
340        self.run_with_inspection_store(store, drive).await;
341    }
342
343    async fn run_with_inspection_store<Store>(self, store: &Store, drive: impl Future<Output = ()>)
344    where
345        Store: InterfaceInspectionStore,
346    {
347        let PrnsNode {
348            node,
349            mut inbound,
350            mut egress,
351            notify,
352            commands,
353            lifecycle,
354            handle,
355            mut host,
356            mut descriptors,
357            mut ifacs,
358        } = self;
359        let AssembledNode {
360            mut engine,
361            state,
362            mut on_event,
363            request_endpoints: _,
364        } = node;
365        let request_channel =
366            Channel::<M, RunnerRequest<ROUTED_REQUEST_BYTES>, ROUTED_REQUESTS>::new();
367        let request_sender = request_channel.sender();
368        let mut persistence = NoManifoldPersistence;
369        let manifold = run_pooled(
370            &mut engine,
371            &mut host,
372            PooledWiring {
373                descriptors: &mut descriptors,
374                ifacs: &mut ifacs,
375                inbound: &mut inbound,
376                egress: &mut egress,
377                notify,
378                commands,
379                lifecycle,
380            },
381            |journaled| {
382                if let Journaled::CommandSettled { id, settlement } = &journaled {
383                    if handle.settle(*id, settlement.clone()) {
384                        return;
385                    }
386                }
387                if let Some(request) = RunnerRequest::copy_from(&journaled) {
388                    let _ = request_sender.try_send(request);
389                }
390                on_event(PrnsEvent::from(journaled), &state);
391            },
392            crate::manifold::decline_all(),
393            store,
394            &mut persistence,
395        );
396        let router =
397            run_router::<St, R, M, COMMANDS, COMPLETIONS, ROUTED_REQUESTS, ROUTED_REQUEST_BYTES>(
398                &state,
399                request_channel.receiver(),
400                handle,
401            );
402        join(join(manifold, router), drive).await;
403    }
404
405    /// Runs only the manifold for boards that schedule interfaces separately.
406    pub async fn run_manifold(&mut self) {
407        self.run_manifold_with_inspection_store(&NoInterfaceInspectionStore)
408            .await;
409    }
410
411    pub async fn run_manifold_with_interface_store<
412        const INTERFACES: usize,
413        const PACKET_PHY_CAPACITY: usize,
414        const PACKET_PHY_INDEX_BUCKETS: usize,
415    >(
416        &mut self,
417        store: &EmbassyInterfaceStore<M, INTERFACES, PACKET_PHY_CAPACITY, PACKET_PHY_INDEX_BUCKETS>,
418    ) where
419        M: Sync,
420    {
421        const {
422            assert!(
423                INTERFACES >= INTERFACE_CAPACITY,
424                "EmbassyInterfaceStore INTERFACES must cover PrnsNode INTERFACE_CAPACITY"
425            );
426        }
427        self.run_manifold_with_inspection_store(store).await;
428    }
429
430    async fn run_manifold_with_inspection_store<Store>(&mut self, store: &Store)
431    where
432        Store: InterfaceInspectionStore,
433    {
434        let mut persistence = NoManifoldPersistence;
435        self.run_manifold_with_inspection_store_and_persistence(store, &mut persistence)
436            .await;
437    }
438
439    pub async fn run_manifold_with_persistence_and_interface_store<
440        Fl,
441        Keys,
442        Observe,
443        const PENDING: usize,
444        const INTERFACES: usize,
445        const PACKET_PHY_CAPACITY: usize,
446        const PACKET_PHY_INDEX_BUCKETS: usize,
447    >(
448        &mut self,
449        store: &EmbassyInterfaceStore<M, INTERFACES, PACKET_PHY_CAPACITY, PACKET_PHY_INDEX_BUCKETS>,
450        persistence: &mut EmbeddedFlashPersistence<Fl, Keys, Observe, PENDING>,
451    ) where
452        M: Sync,
453        Fl: NorFlash,
454        Keys: RouteSnapshotKeys,
455        Observe: FnMut(EmbeddedPersistenceDiagnostic),
456    {
457        const {
458            assert!(
459                INTERFACES >= INTERFACE_CAPACITY,
460                "EmbassyInterfaceStore INTERFACES must cover PrnsNode INTERFACE_CAPACITY"
461            );
462        }
463        self.run_manifold_with_inspection_store_and_persistence(store, persistence)
464            .await;
465    }
466
467    pub async fn restore_embedded_persistence<Fl, Keys, Observe, const PENDING: usize>(
468        &mut self,
469        persistence: &mut EmbeddedFlashPersistence<Fl, Keys, Observe, PENDING>,
470    ) -> EmbeddedPersistenceRestoreReport
471    where
472        Fl: NorFlash,
473        Keys: RouteSnapshotKeys,
474        Observe: FnMut(EmbeddedPersistenceDiagnostic),
475        H: ResumableHost,
476    {
477        let report = persistence
478            .restore(&mut self.node.engine, self.host.now())
479            .await;
480        self.host.resume_at(report.logical_start);
481        report
482    }
483
484    async fn run_manifold_with_inspection_store_and_persistence<Store, P>(
485        &mut self,
486        store: &Store,
487        persistence: &mut P,
488    ) where
489        Store: InterfaceInspectionStore,
490        P: ManifoldPersistence<S>,
491    {
492        let PrnsNode {
493            node,
494            inbound,
495            egress,
496            notify,
497            commands,
498            lifecycle,
499            handle,
500            host,
501            descriptors,
502            ifacs,
503        } = self;
504        let AssembledNode {
505            engine,
506            state,
507            on_event,
508            request_endpoints: _,
509        } = node;
510        let request_channel =
511            Channel::<M, RunnerRequest<ROUTED_REQUEST_BYTES>, ROUTED_REQUESTS>::new();
512        let request_sender = request_channel.sender();
513        let manifold = run_pooled(
514            engine,
515            host,
516            PooledWiring {
517                descriptors,
518                ifacs,
519                inbound,
520                egress,
521                notify: *notify,
522                commands: *commands,
523                lifecycle: *lifecycle,
524            },
525            |journaled| {
526                if let Journaled::CommandSettled { id, settlement } = &journaled {
527                    if handle.settle(*id, settlement.clone()) {
528                        return;
529                    }
530                }
531                if let Some(request) = RunnerRequest::copy_from(&journaled) {
532                    let _ = request_sender.try_send(request);
533                }
534                on_event(PrnsEvent::from(journaled), state);
535            },
536            crate::manifold::decline_all(),
537            store,
538            persistence,
539        );
540        let router =
541            run_router::<St, R, M, COMMANDS, COMPLETIONS, ROUTED_REQUESTS, ROUTED_REQUEST_BYTES>(
542                state,
543                request_channel.receiver(),
544                *handle,
545            );
546        join(manifold, router).await;
547    }
548}
549
550#[cfg(test)]
551mod tests;