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}