Skip to main content

bb_runtime/
completion.rs

1//! `CompletionHandle` for async Contract methods. Implementations
2//! call [`CompletionHandle::complete`]; the handle routes through a
3//! [`CompletionSink`] (typically `IngressQueue`), which the engine
4//! drains on its next poll to unpark the suspended op.
5
6use std::marker::PhantomData;
7use std::sync::Arc;
8
9use crate::ids::CommandId;
10
11/// Cross-thread completion delivery. Sinks take borrowed slices and
12/// must copy into framework-owned storage before returning (Principle
13/// 1a: external byte payloads are ephemeral).
14pub trait CompletionSink: Send + Sync {
15    /// Deliver a successful completion. Implementation copies bytes.
16    fn complete(&self, cmd_id: CommandId, result_bytes: &[u8]);
17    /// Deliver a failure with `Display` rendering of the error.
18    fn fail(&self, cmd_id: CommandId, detail: &str);
19}
20
21/// Async handle the Contract method holds. Carries the [`CommandId`]
22/// + shared completion sink.
23pub struct CompletionHandle<R, E> {
24    cmd_id: CommandId,
25    sink: Arc<dyn CompletionSink>,
26    _marker: PhantomData<fn() -> (R, E)>,
27}
28
29impl<R, E> CompletionHandle<R, E> {
30    /// Construct a fresh handle.
31    pub fn new(cmd_id: CommandId, sink: Arc<dyn CompletionSink>) -> Self {
32        Self {
33            cmd_id,
34            sink,
35            _marker: PhantomData,
36        }
37    }
38
39    /// The parked op's `CommandId`. Read by the dispatch arm before
40    /// returning `DispatchResult::Async(cmd_id)`.
41    pub fn cmd_id(&self) -> CommandId {
42        self.cmd_id
43    }
44}
45
46impl<R, E> CompletionHandle<R, E>
47where
48    R: serde::Serialize,
49    E: std::fmt::Display,
50{
51    /// Complete the parked op. `Ok(value)` serializes via bincode;
52    /// `Err(e)` delivers the `Display` rendering. Local buffers drop
53    /// at end of call (sink copies).
54    pub fn complete(self, result: Result<R, E>) {
55        match result {
56            Ok(value) => {
57                let bytes = bincode::serialize(&value).unwrap_or_default();
58                self.sink.complete(self.cmd_id, &bytes);
59            }
60            Err(e) => {
61                let detail = e.to_string();
62                self.sink.fail(self.cmd_id, &detail);
63            }
64        }
65    }
66}
67
68/// Contract-layer mirror of [`crate::atomic::DispatchResult`].
69/// `Now(Ok)` → `Immediate` (boxed straight into the slot table);
70/// `Now(Err)` → dispatch error; `Later` → `Async(cmd_id)`.
71pub enum ContractResponse<R, E> {
72    /// Result ready inline; the `CompletionHandle` is unused.
73    Now(Result<R, E>),
74    /// Handle retained for off-thread completion.
75    Later,
76}