Skip to main content

harmos_sdk/operations/
serving.rs

1/* ***
2 *
3 * Harmos
4 *
5 * SPDX-License-Identifier: Apache-2.0
6 *
7 * ***
8 */
9
10/* === Modules === */
11
12mod codec;
13mod owned;
14#[cfg(feature = "grpc")]
15mod protobuf;
16#[cfg(feature = "streaming")]
17mod streams;
18
19/* === Re-Exports === */
20
21pub use codec::WireCodecError;
22#[cfg(feature = "streaming")]
23pub use streams::{WireEndpoints, WireStreamLimits, WireStreams};
24
25/* === Imports === */
26
27// Standard Library Imports
28use std::collections::BTreeMap;
29use std::future::Future;
30use std::marker::PhantomData;
31use std::num::NonZeroUsize;
32use std::pin::Pin;
33use std::sync::{Arc, Mutex, Weak};
34use std::task::{Poll, Waker};
35// Third Party Imports
36use serde::{Deserialize, Serialize};
37// Local Imports
38use super::{AuthoredDeclarations, Contract, ContractError, Definition, FailureKind, Operation};
39use crate::catalog::{Catalog, Member, Projection, Visitor};
40
41/* === Constants === */
42
43pub const WIRE_METHOD: &str = "harmos.operation.v2";
44
45pub const CHILD_METHOD: &str = "harmos.operation.child.v2";
46
47/* === Macros === */
48
49/* === Type Aliases === */
50
51type Answer<'a> = Pin<Box<dyn Future<Output = Result<Vec<u8>, ServingError>> + Send + 'a>>;
52type Finished<'a> = Pin<Box<dyn Future<Output = Result<(), ServingError>> + Send + 'a>>;
53type Finalization<S> = Pin<Box<dyn Future<Output = (S, Result<(), ServingError>)> + Send + 'static>>;
54type Prepare<C> = Box<dyn Fn(Vec<u8>, bool) -> Result<Box<dyn Prepared<C>>, ServingError> + Send + Sync>;
55type Install<H, C> = fn(&mut WireTable<C>, Arc<H>) -> Result<(), ServingError>;
56
57/* === Structs === */
58
59#[derive(Debug, Serialize, Deserialize)]
60#[serde(deny_unknown_fields)]
61pub struct WireChildCall {
62    pub context: [u8; 32],
63
64    #[serde(default, skip_serializing_if = "Option::is_none")]
65    pub generation_context: Option<[u8; 32]>,
66
67    #[serde(default, skip_serializing_if = "Vec::is_empty")]
68    pub values: Vec<super::CallValue>,
69
70    pub operation: String,
71    pub payload: Vec<u8>,
72
73    // Submitted children answer their instance id at admission and remain
74    // owned by the calling attempt until joined, released or detached.
75    #[serde(default)]
76    pub submit: bool,
77}
78
79// Drives one child the same attempt submitted; the host resolves the instance
80// only inside the attempt context that submitted it.
81#[derive(Debug, Serialize, Deserialize)]
82#[serde(deny_unknown_fields)]
83pub struct WireChildControl {
84    pub context: [u8; 32],
85    pub instance: u64,
86    pub action: WireChildAction,
87}
88
89pub struct WireTable<C> {
90    handlers: BTreeMap<String, Handler<C>>,
91    prepared: BTreeMap<u64, Arc<Slot<C>>>,
92    capacity: NonZeroUsize,
93    closed: bool,
94    ordinary_closed: bool,
95
96    #[cfg(feature = "streaming")]
97    stream_limits: Option<WireStreamLimits>,
98}
99
100struct Slot<C> {
101    lifecycle: bool,
102    resident: bool,
103
104    #[cfg(feature = "streaming")]
105    streams: Option<WireStreams>,
106
107    state: Mutex<SlotState<C>>,
108}
109
110struct SlotState<C> {
111    prepared: Option<Box<dyn Prepared<C>>>,
112    finished: bool,
113    waiters: Vec<Weak<Mutex<Option<Waker>>>>,
114}
115
116struct Loan<C> {
117    slot: Arc<Slot<C>>,
118    prepared: Option<Box<dyn Prepared<C>>>,
119    finished: bool,
120}
121
122struct Handler<C> {
123    lifecycle: bool,
124    // Lifecycle actions and the observe control are built in: they serve the
125    // plugin's machinery and never appear in its declared catalog.
126    builtin: bool,
127    // A resident control runs until cancelled and is not charged against the
128    // ordinary invocation capacity.
129    resident: bool,
130    definition: Definition,
131    contract: Option<Contract>,
132    prepare: Prepare<C>,
133}
134
135pub struct Serves<H, C>(PhantomData<fn() -> (H, C)>);
136
137pub struct WireBinding<H, C> {
138    install: Install<H, C>,
139}
140
141/* === Enums === */
142
143// Answers: `Join` the child's response payload, `Status` and `Cancel` an
144// encoded `builtin::InstanceState`, `Release` and `Detach` an empty payload.
145// Releasing cancels the child; detaching leaves it running without a handle.
146#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
147#[serde(rename_all = "snake_case")]
148pub enum WireChildAction {
149    Join,
150    Status,
151    Cancel,
152    Release,
153    Detach,
154}
155
156#[derive(Debug)]
157pub enum WireChild {
158    Call(WireChildCall),
159    Control(WireChildControl),
160}
161
162#[derive(Debug, Serialize, Deserialize)]
163#[serde(tag = "phase", rename_all = "snake_case", deny_unknown_fields)]
164pub enum WireCommand {
165    Prepare { invocation: u64, operation: String, payload: Vec<u8> },
166    Initialize { invocation: u64, context: [u8; 32] },
167    Attempt { invocation: u64, context: [u8; 32], values: Vec<super::CallValue> },
168    Finalize { invocation: u64 },
169    StreamAttempt { invocation: u64, epoch: u64, context: [u8; 32], values: Vec<super::CallValue> },
170    StreamSend { invocation: u64, epoch: u64, payload: Vec<u8> },
171    StreamFinish { invocation: u64, epoch: u64 },
172    StreamReceive { invocation: u64, epoch: u64 },
173    PrepareGrpc { invocation: u64, operation: String, payload: Vec<u8> },
174}
175
176#[derive(Debug, Serialize, Deserialize)]
177#[serde(tag = "status", rename_all = "snake_case", deny_unknown_fields)]
178pub enum WireAnswer {
179    Stream { epoch: u64, transferred: bool, payload: Option<Vec<u8>> },
180    Ready { payload: Vec<u8> },
181    Failed { failure: crate::Failure },
182}
183
184#[derive(Debug, thiserror::Error)]
185pub enum ServingError {
186    #[error("carrier invocation does not support stream commands")]
187    StreamUnsupported,
188
189    #[cfg(feature = "streaming")]
190    #[error("streaming catalog requires explicit provider stream limits")]
191    StreamLimits,
192
193    #[cfg(feature = "streaming")]
194    #[error("carrier stream failed: {0}")]
195    Stream(#[source] super::streaming::Error),
196
197    #[cfg(feature = "streaming")]
198    #[error("carrier stream direction already has an outstanding request")]
199    StreamBusy,
200
201    #[cfg(feature = "streaming")]
202    #[error("carrier stream direction or attempt is not available")]
203    StreamPhase,
204
205    #[error(transparent)]
206    Lifecycle(#[from] crate::operations::lifecycle::Error),
207
208    #[error(transparent)]
209    Contract(#[from] ContractError),
210
211    #[error(transparent)]
212    Declaration(#[from] crate::Error),
213
214    #[error("operation `{operation}` has no bound carrier handler")]
215    Unknown { operation: String },
216
217    #[error("operation `{operation}` requires streaming endpoints unsupported by this carrier binding")]
218    Streaming { operation: String },
219
220    #[error("carrier invocation `{invocation}` is already prepared")]
221    Duplicate { invocation: u64 },
222
223    #[error("carrier invocation `{invocation}` has not been prepared")]
224    Missing { invocation: u64 },
225
226    #[error("carrier operation has not completed initialization")]
227    Uninitialized,
228
229    #[error("carrier operation initialization already started and cannot be replayed")]
230    InitializationStarted,
231
232    #[error("carrier invocation capacity is exhausted")]
233    Capacity,
234
235    #[error("carrier ordinary invocations have not finished settlement")]
236    Unsettled,
237
238    #[error("carrier operation admission is closing")]
239    Closing,
240
241    #[error("carrier handler definitions differ from the selected operation catalog")]
242    Catalog,
243
244    #[error("operation payload codec failed")]
245    Codec(#[source] crate::MessageError),
246
247    #[error("carrier handler panicked")]
248    Panicked,
249
250    // The handler's failure, written once at its origin.
251    #[error(transparent)]
252    Handler(crate::Failure),
253}
254
255/* === Traits === */
256
257trait Prepared<C>: Send {
258    #[cfg(feature = "streaming")]
259    fn streams(&self) -> Option<WireStreams> {
260        None
261    }
262
263    // The grant the prepared request needs, answered to the host with Prepare.
264    fn grant(&self) -> Option<crate::plugin::Grant> {
265        None
266    }
267    fn initialize<'a>(&'a mut self, context: &'a C) -> Finished<'a>;
268    fn attempt<'a>(&'a mut self, context: &'a C) -> Answer<'a>;
269    fn finalize(&mut self) -> Finished<'_>;
270}
271
272/* === Implementations === */
273
274impl<C: Send + Sync + 'static> WireTable<C> {
275    pub fn new(capacity: NonZeroUsize) -> Self {
276        Self {
277            handlers: BTreeMap::new(),
278            prepared: BTreeMap::new(),
279            capacity,
280            closed: false,
281            ordinary_closed: false,
282            #[cfg(feature = "streaming")]
283            stream_limits: None,
284        }
285    }
286
287    pub fn of<T, R>(resources: Arc<R>, capacity: NonZeroUsize) -> Result<Self, ServingError>
288    where
289        T: Catalog<AuthoredDeclarations> + Catalog<Serves<R, C>>,
290        R: Send + Sync + 'static,
291    {
292        Self::project::<T, R>(Self::new(capacity), resources)
293    }
294
295    fn project<T, R>(mut table: Self, resources: Arc<R>) -> Result<Self, ServingError>
296    where
297        T: Catalog<AuthoredDeclarations> + Catalog<Serves<R, C>>,
298        R: Send + Sync + 'static,
299    {
300        let definitions = AuthoredDeclarations::of::<T>()?;
301        <T as Catalog<Serves<R, C>>>::ENTRIES.try_for_each(|binding| (binding.install)(&mut table, Arc::clone(&resources)))?;
302        table.validate(&definitions)?;
303        Ok(table)
304    }
305
306    pub fn bind_operations<T, R>(&mut self, resources: Arc<R>) -> Result<(), ServingError>
307    where
308        T: Catalog<AuthoredDeclarations> + Catalog<Serves<R, C>>,
309        R: Send + Sync + 'static,
310    {
311        if self.closed || self.ordinary_closed {
312            return Err(ServingError::Closing);
313        }
314        let staged = Self::of::<T, R>(resources, self.capacity)?;
315        self.extend(staged)
316    }
317
318    #[cfg(feature = "streaming")]
319    pub fn bind_streaming_operations<T, R>(&mut self, resources: Arc<R>, limits: WireStreamLimits) -> Result<(), ServingError>
320    where
321        T: Catalog<AuthoredDeclarations> + Catalog<Serves<R, C>>,
322        R: Send + Sync + 'static,
323    {
324        if self.closed || self.ordinary_closed {
325            return Err(ServingError::Closing);
326        }
327        limits.validate()?;
328        let mut staged = Self::new(self.capacity);
329        staged.stream_limits = Some(limits);
330        let staged = Self::project::<T, R>(staged, resources)?;
331        self.extend(staged)
332    }
333
334    fn extend(&mut self, mut staged: Self) -> Result<(), ServingError> {
335        for id in staged.handlers.keys() {
336            if self.handlers.contains_key(id) {
337                return Err(crate::Error::DuplicateOperation { id: id.clone() }.into());
338            }
339        }
340        self.handlers.append(&mut staged.handlers);
341        Ok(())
342    }
343
344    pub fn definitions(&self) -> Vec<Definition> {
345        self.handlers.values().filter(|handler| !handler.builtin).map(|handler| handler.definition.clone()).collect()
346    }
347
348    pub fn validate(&self, definitions: &[Definition]) -> Result<(), ServingError> {
349        if definitions.len() != self.handlers.values().filter(|handler| !handler.builtin).count() {
350            return Err(ServingError::Catalog);
351        }
352        for definition in definitions {
353            if self.handlers.get(definition.id.as_str()).is_none_or(|bound| bound.builtin || bound.definition != *definition) {
354                return Err(ServingError::Catalog);
355            }
356        }
357        let unique: std::collections::BTreeSet<_> = definitions.iter().map(|definition| &definition.id).collect();
358        if unique.len() != definitions.len() {
359            return Err(ServingError::Catalog);
360        }
361        Ok(())
362    }
363
364    pub fn validate_contracts(&self, contracts: &[Contract]) -> Result<(), ServingError> {
365        if contracts.len() != self.handlers.values().filter(|handler| !handler.builtin).count() {
366            return Err(ServingError::Catalog);
367        }
368        let mut unique = std::collections::BTreeSet::new();
369        for contract in contracts {
370            let id = &contract.operation().id;
371            if !unique.insert(id)
372                || self.handlers.get(id.as_str()).is_none_or(|bound| bound.builtin || bound.contract.as_ref() != Some(contract))
373            {
374                return Err(ServingError::Catalog);
375            }
376        }
377        Ok(())
378    }
379
380    // Answers the request's grant as the Prepare payload.
381    pub fn prepare(&mut self, invocation: u64, operation: &str, payload: Vec<u8>) -> Result<Vec<u8>, ServingError> {
382        self.prepare_codec(invocation, operation, payload, false)
383    }
384
385    fn prepare_codec(&mut self, invocation: u64, operation: &str, payload: Vec<u8>, grpc: bool) -> Result<Vec<u8>, ServingError> {
386        if self.closed {
387            return Err(ServingError::Closing);
388        }
389        self.prepared.retain(|_, slot| !slot.is_finished());
390        if self.prepared.contains_key(&invocation) {
391            return Err(ServingError::Duplicate { invocation });
392        }
393        let handler = self.handlers.get(operation).ok_or_else(|| ServingError::Unknown { operation: operation.to_owned() })?;
394        if self.ordinary_closed && !handler.lifecycle {
395            return Err(ServingError::Closing);
396        }
397        let occupied = self.prepared.values().filter(|slot| slot.lifecycle == handler.lifecycle && !slot.resident).count();
398        // One accounted reserved lifecycle slot remains available even when
399        // ordinary admission is full. Failed SDK finalization retains that slot.
400        let capacity = if handler.lifecycle { 1 } else { self.capacity.get() };
401        if occupied >= capacity && !handler.resident {
402            return Err(ServingError::Capacity);
403        }
404        let prepared = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| (handler.prepare)(payload, grpc)))
405            .map_err(|_| ServingError::Panicked)??;
406        let answer = encode_prepared_grant(prepared.grant().as_ref());
407        let slot = Slot {
408            lifecycle: handler.lifecycle,
409            resident: handler.resident,
410            #[cfg(feature = "streaming")]
411            streams: prepared.streams(),
412            state: Mutex::new(SlotState { prepared: Some(prepared), finished: false, waiters: Vec::new() }),
413        };
414        self.prepared.insert(invocation, Arc::new(slot));
415        Ok(answer)
416    }
417
418    #[cfg(feature = "streaming")]
419    pub fn streams(&self, invocation: u64) -> Result<WireStreams, ServingError> {
420        let slot = self.prepared.get(&invocation).ok_or(ServingError::Missing { invocation })?;
421        if self.closed || (self.ordinary_closed && !slot.lifecycle) || slot.is_finished() {
422            return Err(ServingError::Closing);
423        }
424        slot.streams.clone().ok_or(ServingError::StreamPhase)
425    }
426
427    pub async fn initialize(&mut self, invocation: u64, context: &C) -> Result<(), ServingError> {
428        if self.closed {
429            return Err(ServingError::Closing);
430        }
431        let slot = self.prepared.get(&invocation).ok_or(ServingError::Missing { invocation })?;
432        if self.ordinary_closed && !slot.lifecycle {
433            return Err(ServingError::Closing);
434        }
435        slot.initialize(invocation, context).await
436    }
437
438    pub async fn attempt(&mut self, invocation: u64, context: &C) -> Result<Vec<u8>, ServingError> {
439        if self.closed {
440            return Err(ServingError::Closing);
441        }
442        let slot = self.prepared.get(&invocation).ok_or(ServingError::Missing { invocation })?;
443        if self.ordinary_closed && !slot.lifecycle {
444            return Err(ServingError::Closing);
445        }
446        slot.attempt(invocation, context, None).await
447    }
448
449    pub async fn finalize(&mut self, invocation: u64) -> Result<(), ServingError> {
450        if let Some(slot) = self.prepared.get(&invocation) {
451            slot.finalize().await?;
452        }
453        self.prepared.remove(&invocation);
454        Ok(())
455    }
456
457    fn traffic(&self, command: WireCommand) -> Pin<Box<dyn Future<Output = WireAnswer> + Send + 'static>> {
458        #[cfg(not(feature = "streaming"))]
459        {
460            let _ = command;
461            Box::pin(async { wire_answer(Err(ServingError::StreamUnsupported)) })
462        }
463        #[cfg(feature = "streaming")]
464        {
465            let invocation = match &command {
466                WireCommand::StreamSend { invocation, .. }
467                | WireCommand::StreamFinish { invocation, .. }
468                | WireCommand::StreamReceive { invocation, .. } => *invocation,
469                _ => unreachable!("only stream traffic bypasses the phase loan"),
470            };
471            let streams = self.streams(invocation);
472            Box::pin(async move {
473                let streams = match streams {
474                    Ok(streams) => streams,
475                    Err(error) => return wire_answer(Err(error)),
476                };
477                match command {
478                    WireCommand::StreamSend { epoch, payload, .. } => {
479                        wire_answer(guarded(async { streams.send(epoch, &payload).await.map(|()| Vec::new()) }).await)
480                    }
481                    WireCommand::StreamFinish { epoch, .. } => wire_answer(streams.finish_input(epoch).map(|()| Vec::new())),
482                    WireCommand::StreamReceive { epoch, .. } => match guarded(streams.receive(epoch)).await {
483                        Ok(payload) => WireAnswer::Stream { epoch, transferred: streams.transferred(), payload },
484                        Err(error) => wire_answer(Err(error)),
485                    },
486                    _ => unreachable!("only stream traffic bypasses the phase loan"),
487                }
488            })
489        }
490    }
491
492    pub fn begin(&mut self, command: WireCommand, context: Arc<C>) -> Pin<Box<dyn Future<Output = WireAnswer> + Send + 'static>> {
493        if command.is_stream_traffic() {
494            return self.traffic(command);
495        }
496        let slot = match &command {
497            WireCommand::Initialize { invocation, .. }
498            | WireCommand::Attempt { invocation, .. }
499            | WireCommand::StreamAttempt { invocation, .. }
500            | WireCommand::Finalize { invocation } => self.prepared.get(invocation).cloned(),
501            WireCommand::Prepare { .. } | WireCommand::PrepareGrpc { .. } => None,
502            WireCommand::StreamSend { .. } | WireCommand::StreamFinish { .. } | WireCommand::StreamReceive { .. } => {
503                unreachable!("stream traffic already dispatched")
504            }
505        };
506        let command = match command {
507            WireCommand::PrepareGrpc { invocation, operation, payload } => {
508                let result = self.prepare_codec(invocation, &operation, payload, true);
509                return Box::pin(async move { wire_answer(result) });
510            }
511            WireCommand::Prepare { invocation, operation, payload } => {
512                let result = self.prepare(invocation, &operation, payload);
513                return Box::pin(async move { wire_answer(result) });
514            }
515            command => command,
516        };
517        let refused = self.closed || (self.ordinary_closed && slot.as_ref().is_some_and(|slot| !slot.lifecycle));
518        if refused && matches!(command, WireCommand::Initialize { .. } | WireCommand::Attempt { .. } | WireCommand::StreamAttempt { .. }) {
519            return Box::pin(async { wire_answer(Err(ServingError::Closing)) });
520        }
521        Box::pin(async move {
522            let result = match command {
523                WireCommand::Initialize { invocation, .. } => match slot {
524                    Some(slot) => slot.initialize(invocation, &context).await.map(|()| Vec::new()),
525                    None => Err(ServingError::Missing { invocation }),
526                },
527                WireCommand::Attempt { invocation, .. } => match slot {
528                    Some(slot) => slot.attempt(invocation, &context, None).await,
529                    None => Err(ServingError::Missing { invocation }),
530                },
531                WireCommand::StreamAttempt { invocation, epoch, .. } => match slot {
532                    Some(slot) => slot.attempt(invocation, &context, Some(epoch)).await,
533                    None => Err(ServingError::Missing { invocation }),
534                },
535                WireCommand::StreamSend { .. } | WireCommand::StreamFinish { .. } | WireCommand::StreamReceive { .. } => {
536                    unreachable!("stream traffic already dispatched")
537                }
538                WireCommand::Finalize { .. } => match slot {
539                    Some(slot) => slot.finalize().await.map(|()| Vec::new()),
540                    None => Ok(Vec::new()),
541                },
542                WireCommand::Prepare { .. } | WireCommand::PrepareGrpc { .. } => {
543                    unreachable!("preparation is synchronous")
544                }
545            };
546            wire_answer(result)
547        })
548    }
549
550    pub fn close_ordinary(&mut self) -> Pin<Box<dyn Future<Output = Result<(), ServingError>> + Send + 'static>> {
551        self.ordinary_closed = true;
552        let slots: Vec<_> = self.prepared.values().filter(|slot| !slot.lifecycle).cloned().collect();
553        #[cfg(feature = "streaming")]
554        for slot in &slots {
555            if let Some(streams) = &slot.streams {
556                streams.close();
557            }
558        }
559        Box::pin(async move {
560            for slot in slots {
561                slot.finalize().await?;
562            }
563            Ok(())
564        })
565    }
566
567    pub fn release_ordinary(&mut self) -> Result<impl Send + use<C>, ServingError> {
568        if !self.ordinary_closed || self.prepared.values().any(|slot| !slot.lifecycle && !slot.is_finished()) {
569            return Err(ServingError::Unsettled);
570        }
571        // Hand Off Retired Owners So The Caller Can Drop Them Outside Its Lock
572        let retired_slots = self.prepared.extract_if(.., |_, slot| !slot.lifecycle).collect::<Vec<_>>();
573        let retired_handlers = self.handlers.extract_if(.., |_, handler| !handler.lifecycle).collect::<Vec<_>>();
574        Ok((retired_slots, retired_handlers))
575    }
576
577    pub async fn settle_ordinary(&mut self) -> Result<(), ServingError> {
578        self.close_ordinary().await?;
579        let retired = self.release_ordinary()?;
580        drop(retired);
581        Ok(())
582    }
583
584    pub fn close(&mut self) -> Pin<Box<dyn Future<Output = Result<(), ServingError>> + Send + 'static>> {
585        self.closed = true;
586        let slots: Vec<_> = self.prepared.values().cloned().collect();
587        #[cfg(feature = "streaming")]
588        for slot in &slots {
589            if let Some(streams) = &slot.streams {
590                streams.close();
591            }
592        }
593        Box::pin(async move {
594            for slot in slots {
595                slot.finalize().await?;
596            }
597            Ok(())
598        })
599    }
600
601    pub fn release(&mut self) -> Result<impl Send + use<C>, ServingError> {
602        if !self.closed || self.prepared.values().any(|slot| !slot.is_finished()) {
603            return Err(ServingError::Unsettled);
604        }
605        Ok((std::mem::take(&mut self.prepared), std::mem::take(&mut self.handlers)))
606    }
607
608    pub async fn settle(&mut self) -> Result<(), ServingError> {
609        self.close().await?;
610        let retired = self.release()?;
611        drop(retired);
612        Ok(())
613    }
614
615    pub fn is_settled(&self) -> bool {
616        self.closed && self.prepared.values().all(|slot| slot.is_finished())
617    }
618
619    pub fn is_closed(&self) -> bool {
620        self.closed
621    }
622
623    pub async fn answer(&mut self, command: WireCommand, context: &C) -> WireAnswer {
624        if command.is_stream_traffic() {
625            return self.traffic(command).await;
626        }
627        if let WireCommand::StreamAttempt { invocation, epoch, .. } = command {
628            if self.closed || (self.ordinary_closed && self.prepared.get(&invocation).is_some_and(|slot| !slot.lifecycle)) {
629                return wire_answer(Err(ServingError::Closing));
630            }
631            let Some(slot) = self.prepared.get(&invocation) else {
632                return wire_answer(Err(ServingError::Missing { invocation }));
633            };
634            return wire_answer(slot.attempt(invocation, context, Some(epoch)).await);
635        }
636        let result = match command {
637            WireCommand::PrepareGrpc { invocation, operation, payload } => self.prepare_codec(invocation, &operation, payload, true),
638            WireCommand::Prepare { invocation, operation, payload } => self.prepare(invocation, &operation, payload),
639            WireCommand::Initialize { invocation, .. } => self.initialize(invocation, context).await.map(|()| Vec::new()),
640            WireCommand::Attempt { invocation, .. } => self.attempt(invocation, context).await,
641            WireCommand::StreamAttempt { .. }
642            | WireCommand::StreamSend { .. }
643            | WireCommand::StreamFinish { .. }
644            | WireCommand::StreamReceive { .. } => {
645                unreachable!("stream command already dispatched")
646            }
647            WireCommand::Finalize { invocation } => self.finalize(invocation).await.map(|()| Vec::new()),
648        };
649        wire_answer(result)
650    }
651}
652
653impl<C> Slot<C> {
654    fn is_finished(&self) -> bool {
655        self.state.lock().expect("invocation state lock").finished
656    }
657
658    async fn borrow(self: &Arc<Self>) -> Option<Loan<C>> {
659        let waiter = Arc::new(Mutex::new(None));
660        std::future::poll_fn(|context| {
661            let mut state = self.state.lock().expect("invocation state lock");
662            if state.finished {
663                return Poll::Ready(None);
664            }
665            if let Some(prepared) = state.prepared.take() {
666                return Poll::Ready(Some(Loan { slot: Arc::clone(self), prepared: Some(prepared), finished: false }));
667            }
668            *waiter.lock().expect("invocation waiter lock") = Some(context.waker().clone());
669            state.waiters.retain(|entry| entry.strong_count() != 0);
670            if !state.waiters.iter().any(|entry| entry.ptr_eq(&Arc::downgrade(&waiter))) {
671                state.waiters.push(Arc::downgrade(&waiter));
672            }
673            Poll::Pending
674        })
675        .await
676    }
677}
678
679impl<C: Send + Sync + 'static> Slot<C> {
680    async fn initialize(self: &Arc<Self>, invocation: u64, context: &C) -> Result<(), ServingError> {
681        let mut loan = self.borrow().await.ok_or(ServingError::Missing { invocation })?;
682        loan.prepared.as_mut().expect("loaned invocation state").initialize(context).await
683    }
684
685    async fn attempt(self: &Arc<Self>, invocation: u64, context: &C, epoch: Option<u64>) -> Result<Vec<u8>, ServingError> {
686        let mut loan = self.borrow().await.ok_or(ServingError::Missing { invocation })?;
687        #[cfg(feature = "streaming")]
688        let attempt = match (&self.streams, epoch) {
689            (Some(streams), Some(epoch)) => Some(streams.start(epoch)?),
690            (None, None) => None,
691            _ => return Err(ServingError::StreamUnsupported),
692        };
693        #[cfg(not(feature = "streaming"))]
694        if epoch.is_some() {
695            return Err(ServingError::StreamUnsupported);
696        }
697        let result = loan.prepared.as_mut().expect("loaned invocation state").attempt(context).await;
698        #[cfg(feature = "streaming")]
699        if let Some(attempt) = attempt {
700            attempt.end();
701        }
702        result
703    }
704
705    async fn finalize(self: &Arc<Self>) -> Result<(), ServingError> {
706        #[cfg(feature = "streaming")]
707        if let Some(streams) = &self.streams {
708            streams.close();
709        }
710        let Some(mut loan) = self.borrow().await else {
711            return Ok(());
712        };
713        loan.prepared.as_mut().expect("loaned invocation state").finalize().await?;
714        // Drop Physical State Before Releasing Its Capacity
715        loan.finished = true;
716        let prepared = loan.prepared.take();
717        drop(prepared);
718        Ok(())
719    }
720}
721
722impl<C> Drop for Loan<C> {
723    fn drop(&mut self) {
724        let waiters = {
725            let mut state = self.slot.state.lock().expect("invocation state lock");
726            state.prepared = self.prepared.take();
727            state.finished = self.finished;
728            std::mem::take(&mut state.waiters)
729        };
730        for waiter in waiters {
731            if let Some(waiter) = waiter.upgrade() {
732                let wake = waiter.lock().expect("invocation waiter lock").take();
733                if let Some(wake) = wake {
734                    wake.wake();
735                }
736            }
737        }
738    }
739}
740
741impl WireCommand {
742    pub const LIMIT: usize = codec::LIMIT;
743    fn is_stream_traffic(&self) -> bool {
744        matches!(self, Self::StreamSend { .. } | Self::StreamFinish { .. } | Self::StreamReceive { .. })
745    }
746}
747
748impl ServingError {
749    pub fn classification(&self) -> FailureKind {
750        match self {
751            Self::Handler(failure) => failure.code(),
752            Self::Panicked => FailureKind::Ambiguous,
753            Self::Codec(_) | Self::Declaration(_) | Self::Lifecycle(_) | Self::Contract(_) | Self::Catalog | Self::Streaming { .. } => {
754                FailureKind::Invalid
755            }
756            _ => FailureKind::Permanent,
757        }
758    }
759}
760
761impl<C: Send + Sync + 'static, H: Send + Sync + 'static> Visitor for Serves<H, C> {
762    type Entry = WireBinding<H, C>;
763}
764
765#[cfg(not(feature = "streaming"))]
766impl<T, R, C> Member<T> for Serves<R, C>
767where
768    C: Send + Sync + 'static,
769    R: Send + Sync + 'static,
770    T: for<'a> Operation<Resources = R, Context<'a> = &'a C, Endpoints = ()>,
771{
772    const ENTRY: WireBinding<R, C> = WireBinding { install: WireTable::<C>::bind_operation::<T> };
773}
774
775#[cfg(feature = "streaming")]
776impl<T, R, C> Member<T> for Serves<R, C>
777where
778    C: Send + Sync + 'static,
779    R: Send + Sync + 'static,
780    T: for<'a> Operation<Resources = R, Context<'a> = &'a C>,
781    T::Endpoints: WireEndpoints,
782{
783    const ENTRY: WireBinding<R, C> = WireBinding { install: WireTable::<C>::bind_catalog_operation::<T> };
784}
785
786/* === Public Functions === */
787
788// A Prepare answer is empty when the request needs no grant, else the grant as JSON.
789pub fn encode_prepared_grant(grant: Option<&crate::plugin::Grant>) -> Vec<u8> {
790    grant.map_or_else(Vec::new, |grant| serde_json::to_vec(grant).expect("a grant serializes to JSON"))
791}
792
793pub fn decode_prepared_grant(payload: &[u8]) -> Result<Option<crate::plugin::Grant>, serde_json::Error> {
794    if payload.is_empty() {
795        return Ok(None);
796    }
797    serde_json::from_slice(payload).map(Some)
798}
799
800/* === Private Functions === */
801
802fn wire_answer(result: Result<Vec<u8>, ServingError>) -> WireAnswer {
803    match result {
804        Ok(payload) => WireAnswer::Ready { payload },
805        Err(ServingError::Handler(failure)) => WireAnswer::Failed { failure },
806        Err(error) => {
807            WireAnswer::Failed { failure: crate::Failure::originate(error.classification(), &error, crate::failure::Origin::Plugin) }
808        }
809    }
810}
811
812async fn guarded<T>(future: impl Future<Output = Result<T, ServingError>>) -> Result<T, ServingError> {
813    let mut future = std::pin::pin!(future);
814    std::future::poll_fn(|context| match std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| future.as_mut().poll(context))) {
815        Ok(result) => result,
816        Err(_) => std::task::Poll::Ready(Err(ServingError::Panicked)),
817    })
818    .await
819}
820
821/* === Tests === */