1mod codec;
13mod configuration;
14mod owned;
15#[cfg(feature = "grpc")]
16pub(crate) mod protobuf;
17#[cfg(feature = "streaming")]
18mod streams;
19
20pub use codec::WireCodecError;
23#[cfg(feature = "streaming")]
24pub use streams::{WireEndpoints, WireStreamLimits, WireStreams};
25
26use std::collections::BTreeMap;
30use std::future::Future;
31use std::num::NonZeroUsize;
32use std::pin::Pin;
33use std::sync::{Arc, Mutex, Weak};
34use std::task::{Poll, Waker};
35use serde::{Deserialize, Serialize};
37use tracing::Instrument;
38use super::{Contract, ContractError, ExecutionContext, FailureKind, OperationDefinition, RegistrationBatch, StartupError};
40use crate::QualifiedId;
41
42pub const WIRE_METHOD: &str = "harmos.operation.v2";
45
46pub const CHILD_METHOD: &str = "harmos.operation.child.v2";
47
48type Answer<'a> = Pin<Box<dyn Future<Output = Result<Vec<u8>, ServingError>> + Send + 'a>>;
53type Finished<'a> = Pin<Box<dyn Future<Output = Result<(), ServingError>> + Send + 'a>>;
54type Finalization<S> = Pin<Box<dyn Future<Output = (S, Result<(), ServingError>)> + Send + 'static>>;
55type Prepare = Box<dyn Fn(Vec<u8>, bool) -> Result<Box<dyn Prepared>, ServingError> + Send + Sync>;
56
57struct Contextual<F: std::future::Future> {
60 future: Option<std::pin::Pin<Box<F>>>,
61 dispatcher: tracing::Dispatch,
62}
63
64#[derive(Debug, Serialize, Deserialize)]
65#[serde(deny_unknown_fields)]
66pub struct WireChildCall {
67 pub context: [u8; 32],
68
69 #[serde(default, skip_serializing_if = "Option::is_none")]
70 pub generation_context: Option<[u8; 32]>,
71
72 #[serde(default, skip_serializing_if = "Vec::is_empty")]
73 pub values: Vec<super::CallValue>,
74
75 pub operation: String,
76 pub fingerprint: [u8; 32],
77 pub generation: u64,
78 pub payload: Vec<u8>,
79
80 #[serde(default)]
83 pub submit: bool,
84}
85
86#[derive(Debug, Serialize, Deserialize)]
89#[serde(deny_unknown_fields)]
90pub struct WireChildControl {
91 pub context: [u8; 32],
92 pub instance: u64,
93 pub action: WireChildAction,
94}
95
96pub struct WireTable {
97 owner: String,
98 resources: super::ResourceSet,
99 resources_frozen: bool,
100 trusted_resources: super::ResourceSet,
101 pending: Vec<RegistrationBatch>,
102 catalogs: std::collections::HashSet<std::any::TypeId>,
103 configuration_errors: Vec<StartupError>,
104 binding_failed: bool,
105 handlers: BTreeMap<String, Route>,
106 retired: BTreeMap<String, u64>,
107 services: BTreeMap<String, super::registration::ServiceFactory>,
108 prepared: BTreeMap<u64, Arc<Slot>>,
109 capacity: Option<NonZeroUsize>,
110 closed: bool,
111 ordinary_closed: bool,
112 provided: Vec<std::any::TypeId>,
113
114 #[cfg(feature = "streaming")]
115 stream_limits: Option<WireStreamLimits>,
116}
117
118struct Slot {
119 operation: String,
120 lifecycle: bool,
121 resident: bool,
122
123 #[cfg(feature = "streaming")]
124 streams: Option<WireStreams>,
125
126 state: Mutex<SlotState>,
127}
128
129struct SlotState {
130 prepared: Option<Box<dyn Prepared>>,
131 finished: bool,
132 cancelled: bool,
133 waiters: Vec<Weak<Mutex<Option<Waker>>>>,
134}
135
136struct Loan {
137 slot: Arc<Slot>,
138 prepared: Option<Box<dyn Prepared>>,
139 finished: bool,
140}
141
142struct Route {
143 callees: &'static [super::WireDeclaration],
144 lifecycle: bool,
145 builtin: bool,
148 resident: bool,
151 definition: OperationDefinition,
152 generation: Option<u64>,
153 closing: bool,
154 contract: Option<Contract>,
155 prepare: Prepare,
156}
157
158#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
164#[serde(rename_all = "snake_case")]
165pub enum WireChildAction {
166 Join,
167 Status,
168 Cancel,
169 Release,
170 Detach,
171}
172
173#[derive(Debug)]
174pub enum WireChild {
175 Call(WireChildCall),
176 Control(WireChildControl),
177}
178
179#[derive(Debug, Serialize, Deserialize)]
180#[serde(tag = "phase", rename_all = "snake_case", deny_unknown_fields)]
181pub enum WireCommand {
182 Prepare { invocation: u64, operation: String, fingerprint: [u8; 32], generation: u64, payload: Vec<u8> },
183 Initialize { invocation: u64, context: [u8; 32] },
184 Attempt { invocation: u64, context: [u8; 32], values: Vec<super::CallValue> },
185 Finalize { invocation: u64 },
186 StreamAttempt { invocation: u64, epoch: u64, context: [u8; 32], values: Vec<super::CallValue> },
187 StreamSend { invocation: u64, epoch: u64, payload: Vec<u8> },
188 StreamFinish { invocation: u64, epoch: u64 },
189 StreamReceive { invocation: u64, epoch: u64 },
190 PrepareGrpc { invocation: u64, operation: String, fingerprint: [u8; 32], generation: u64, payload: Vec<u8> },
191}
192
193#[derive(Debug, Serialize, Deserialize)]
194#[serde(tag = "status", rename_all = "snake_case", deny_unknown_fields)]
195pub enum WireAnswer {
196 Stream { epoch: u64, transferred: bool, payload: Option<Vec<u8>> },
197 Ready { payload: Vec<u8> },
198 Failed { failure: crate::Failure },
199}
200
201#[derive(Debug, thiserror::Error)]
202pub enum ServingError {
203 #[error(transparent)]
204 Startup(#[from] Box<StartupError>),
205
206 #[error("the carrier resource store is frozen")]
207 StoreFrozen,
208
209 #[error("caller may not invoke operation `{operation}` without a host assignment")]
210 Unassigned { operation: &'static str },
211
212 #[error("operation endpoint types differ from the declaration")]
213 EndpointTypes,
214
215 #[error("carrier invocation does not support stream commands")]
216 StreamUnsupported,
217
218 #[cfg(feature = "streaming")]
219 #[error("streaming catalog requires explicit provider stream limits")]
220 StreamLimits,
221
222 #[cfg(feature = "streaming")]
223 #[error("carrier stream failed: {0}")]
224 Stream(#[source] super::streaming::Error),
225
226 #[cfg(feature = "streaming")]
227 #[error("carrier stream direction already has an outstanding request")]
228 StreamBusy,
229
230 #[cfg(feature = "streaming")]
231 #[error("carrier stream direction or attempt is not available")]
232 StreamPhase,
233
234 #[error(transparent)]
235 Lifecycle(#[from] harmos_domain::model::operations::lifecycle::Error),
236
237 #[error(transparent)]
238 Contract(#[from] ContractError),
239
240 #[error(transparent)]
241 Declaration(#[from] crate::Error),
242
243 #[error("operation `{operation}` declares resource `{resource}` that its registration does not provide")]
244 Resource { operation: String, resource: &'static str },
245
246 #[error("operation `{operation}` has no bound carrier handler")]
247 Unknown { operation: String },
248
249 #[error("operation `{operation}` requires streaming endpoints unsupported by this carrier binding")]
250 Streaming { operation: String },
251
252 #[error("operation `{operation}` has a mismatched fingerprint or stale generation")]
253 Identity { operation: String },
254
255 #[error("carrier invocation `{invocation}` is already prepared")]
256 Duplicate { invocation: u64 },
257
258 #[error("carrier invocation `{invocation}` has not been prepared")]
259 Missing { invocation: u64 },
260
261 #[error("carrier operation has not completed initialization")]
262 Uninitialized,
263
264 #[error("carrier operation initialization already started and cannot be replayed")]
265 InitializationStarted,
266
267 #[error("carrier invocation capacity is exhausted")]
268 Capacity,
269
270 #[error("carrier ordinary invocations have not finished settlement")]
271 Unsettled,
272
273 #[error("carrier operation admission is closing")]
274 Closing,
275
276 #[error("carrier handler definitions differ from the selected operation catalog")]
277 Catalog,
278
279 #[error("operation payload codec failed")]
280 Codec(#[source] crate::MessageError),
281
282 #[error("carrier handler panicked")]
283 Panicked,
284
285 #[error(transparent)]
287 Handler(crate::Failure),
288
289 #[error("native handler failed: {0}")]
290 Native(#[source] std::sync::Arc<dyn std::error::Error + Send + Sync>),
291}
292
293trait InContext: std::future::Future + Sized {
296 fn in_context(self, dispatcher: tracing::Dispatch) -> Contextual<Self> {
297 Contextual { future: Some(Box::pin(self)), dispatcher }
298 }
299
300 fn in_current_context(self) -> Contextual<Self> {
301 self.in_context(tracing::dispatcher::get_default(Clone::clone))
302 }
303}
304
305trait Prepared: Send {
306 #[cfg(feature = "streaming")]
307 fn streams(&self) -> Option<WireStreams> {
308 None
309 }
310
311 fn grant(&self) -> Option<crate::plugin::Grant> {
313 None
314 }
315 fn bind<'a>(&'a mut self, context: &'a ExecutionContext) -> Finished<'a>;
316 fn attempt<'a>(&'a mut self, context: &'a ExecutionContext) -> Answer<'a>;
317 fn finalize(&mut self) -> Finished<'_>;
318}
319
320impl<F: std::future::Future> InContext for F {}
323
324impl<F: std::future::Future> std::future::Future for Contextual<F> {
325 type Output = F::Output;
326
327 fn poll(self: std::pin::Pin<&mut Self>, context: &mut std::task::Context<'_>) -> std::task::Poll<Self::Output> {
328 let owner = self.get_mut();
329 let _dispatcher = tracing::dispatcher::set_default(&owner.dispatcher);
330 owner.future.as_mut().expect("context future remains until drop").as_mut().poll(context)
331 }
332}
333
334impl<F: std::future::Future> Drop for Contextual<F> {
335 fn drop(&mut self) {
336 tracing::dispatcher::with_default(&self.dispatcher, || drop(self.future.take()));
337 }
338}
339
340impl WireTable {
341 pub fn new(owner: &str) -> Result<Self, ServingError> {
342 if !harmos_domain::identity::segment(owner) {
343 return Err(ServingError::Catalog);
344 }
345 Ok(Self {
346 owner: owner.to_owned(),
347 resources: super::ResourceSet::new(),
348 resources_frozen: false,
349 trusted_resources: super::ResourceSet::new(),
350 pending: Vec::new(),
351 catalogs: std::collections::HashSet::new(),
352 configuration_errors: Vec::new(),
353 binding_failed: false,
354 services: BTreeMap::new(),
355 handlers: BTreeMap::new(),
356 retired: BTreeMap::new(),
357 prepared: BTreeMap::new(),
358 capacity: None,
359 closed: false,
360 ordinary_closed: false,
361 provided: Vec::new(),
362 #[cfg(feature = "streaming")]
363 stream_limits: None,
364 })
365 }
366
367 pub fn provide(&mut self, resource: std::any::TypeId) {
369 self.provided.push(resource);
370 }
371
372 pub fn provided(&self) -> &[std::any::TypeId] {
373 &self.provided
374 }
375
376 pub fn capacity(&mut self, capacity: NonZeroUsize) {
377 self.capacity = Some(capacity);
378 }
379
380 #[cfg(feature = "streaming")]
381 pub fn stream_limits(&mut self, limits: WireStreamLimits) -> Result<(), ServingError> {
382 limits.validate()?;
383 self.stream_limits = Some(limits);
384 Ok(())
385 }
386
387 #[doc(hidden)]
388 pub fn register(&mut self, batch: RegistrationBatch) -> Result<(), ServingError> {
389 if self.closed || self.ordinary_closed {
390 return Err(ServingError::Closing);
391 }
392 if !self.resources_frozen {
393 self.pending.push(batch);
394 return Ok(());
395 }
396 self.register_now(batch)
397 }
398
399 fn register_now(&mut self, batch: RegistrationBatch) -> Result<(), ServingError> {
400 if self.closed || self.ordinary_closed {
401 return Err(ServingError::Closing);
402 }
403 let mut staged = Self::new(&self.owner)?;
404 staged.resources = self.resources.clone();
405 staged.trusted_resources = self.trusted_resources.clone();
406 staged.capacity = self.capacity;
407 staged.provided.clone_from(&self.provided);
408 #[cfg(feature = "streaming")]
409 {
410 staged.stream_limits = self.stream_limits;
411 }
412 for factory in batch.operations {
413 factory.install(&mut staged)?;
414 }
415 for factory in batch.services {
416 factory.policy.validate().map_err(|_| ServingError::Catalog)?;
417 let id = QualifiedId::qualify(&self.owner, &factory.local_id).map_err(|_| ServingError::Catalog)?;
418 let id = id.as_str().to_owned();
419 if staged.handlers.contains_key(&id) || staged.services.insert(id, factory).is_some() {
420 return Err(ServingError::Catalog);
421 }
422 }
423 if staged.handlers.keys().chain(staged.services.keys()).any(|id| self.handlers.contains_key(id) || self.services.contains_key(id)) {
424 return Err(ServingError::Catalog);
425 }
426 self.handlers.append(&mut staged.handlers);
427 self.services.append(&mut staged.services);
428 self.resources_frozen = true;
429 Ok(())
430 }
431
432 pub async fn unregister(&mut self, id: &QualifiedId) -> Result<(), ServingError> {
433 let key = id.as_str();
434 if !self.handlers.contains_key(key) && !self.services.contains_key(key) {
435 return Err(ServingError::Unknown { operation: key.to_owned() });
436 }
437 if let Some(handler) = self.handlers.get_mut(key) {
438 handler.closing = true;
439 }
440 let slots: Vec<_> =
441 self.prepared.iter().filter(|(_, slot)| slot.operation == key).map(|(invocation, slot)| (*invocation, slot.clone())).collect();
442 for (_, slot) in &slots {
443 slot.cancel();
444 }
445 for (_, slot) in &slots {
446 slot.finalize().await?;
447 }
448 for (invocation, _) in slots {
449 self.prepared.remove(&invocation);
450 }
451 if let Some(handler) = self.handlers.remove(key)
452 && let Some(generation) = handler.generation
453 {
454 self.retired.insert(key.to_owned(), generation);
455 }
456 self.services.remove(key);
457 Ok(())
458 }
459
460 pub fn definitions(&self) -> Vec<OperationDefinition> {
461 self.handlers.values().filter(|handler| !handler.builtin).map(|handler| handler.definition.clone()).collect()
462 }
463
464 pub fn validate(&self, definitions: &[OperationDefinition]) -> Result<(), ServingError> {
465 if definitions.len() != self.handlers.values().filter(|handler| !handler.builtin).count() {
466 return Err(ServingError::Catalog);
467 }
468 for definition in definitions {
469 if self
470 .handlers
471 .get(QualifiedId::qualify(&self.owner, definition.id.as_str()).map_err(|_| ServingError::Catalog)?.as_str())
472 .is_none_or(|bound| bound.builtin || bound.definition != *definition)
473 {
474 return Err(ServingError::Catalog);
475 }
476 }
477 let unique: std::collections::BTreeSet<_> = definitions.iter().map(|definition| &definition.id).collect();
478 if unique.len() != definitions.len() {
479 return Err(ServingError::Catalog);
480 }
481 Ok(())
482 }
483
484 pub fn validate_callees(&self, assignments: &[u8]) -> Result<(), ServingError> {
485 let assignments: Vec<super::AssignedOperation> =
486 if assignments.is_empty() { Vec::new() } else { postcard::from_bytes(assignments).map_err(|_| ServingError::Catalog)? };
487 for route in self.handlers.values() {
488 for callee in route.callees {
489 let id = callee.operation.id;
490 let fingerprint = (callee.operation.fingerprint)().map_err(|_| ServingError::Catalog)?;
491 let local = QualifiedId::qualify(&self.owner, id).map_err(|_| ServingError::Catalog)?;
492 let actual = self
493 .handlers
494 .get(local.as_str())
495 .map(|entry| entry.definition.fingerprint.0)
496 .or_else(|| {
497 assignments
498 .iter()
499 .find(|entry| entry.operation.split_once('.').is_some_and(|(_, name)| name == id))
500 .map(|entry| entry.fingerprint)
501 })
502 .ok_or(ServingError::Unassigned { operation: id })?;
503 if actual != fingerprint.0 {
504 return Err(ServingError::Identity { operation: id.to_owned() });
505 }
506 }
507 }
508 Ok(())
509 }
510
511 pub fn validate_contracts(&self, contracts: &[Contract]) -> Result<(), ServingError> {
512 if contracts.len() != self.handlers.values().filter(|handler| !handler.builtin).count() {
513 return Err(ServingError::Catalog);
514 }
515 let mut unique = std::collections::BTreeSet::new();
516 for contract in contracts {
517 let id = &contract.operation().id;
518 if !unique.insert(id)
519 || self
520 .handlers
521 .get(QualifiedId::qualify(&self.owner, id.as_str()).map_err(|_| ServingError::Catalog)?.as_str())
522 .is_none_or(|bound| bound.builtin || bound.contract.as_ref() != Some(contract))
523 {
524 return Err(ServingError::Catalog);
525 }
526 }
527 Ok(())
528 }
529
530 pub fn prepare(
532 &mut self, invocation: u64, operation: &str, fingerprint: [u8; 32], generation: u64, payload: Vec<u8>,
533 ) -> Result<Vec<u8>, ServingError> {
534 self.prepare_codec(invocation, operation, fingerprint, generation, payload, false)
535 }
536
537 fn prepare_codec(
538 &mut self, invocation: u64, operation: &str, fingerprint: [u8; 32], generation: u64, payload: Vec<u8>, grpc: bool,
539 ) -> Result<Vec<u8>, ServingError> {
540 if self.closed {
541 return Err(ServingError::Closing);
542 }
543 self.prepared.retain(|_, slot| !slot.is_finished());
544 if self.prepared.contains_key(&invocation) {
545 return Err(ServingError::Duplicate { invocation });
546 }
547 let handler = self.handlers.get_mut(operation).ok_or_else(|| ServingError::Unknown { operation: operation.to_owned() })?;
548 if handler.definition.fingerprint.0 != fingerprint
549 || generation == 0
550 || handler.generation.is_some_and(|bound| bound != generation)
551 || self.retired.get(operation).is_some_and(|retired| generation <= *retired)
552 {
553 return Err(ServingError::Identity { operation: operation.to_owned() });
554 }
555 handler.generation = Some(generation);
558 if handler.closing || (self.ordinary_closed && !handler.lifecycle) {
559 return Err(ServingError::Closing);
560 }
561 let occupied = self.prepared.values().filter(|slot| slot.lifecycle == handler.lifecycle && !slot.resident).count();
562 let capacity = if handler.lifecycle { 1 } else { self.capacity.ok_or(ServingError::Capacity)?.get() };
565 if occupied >= capacity && !handler.resident {
566 return Err(ServingError::Capacity);
567 }
568 let prepared = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| (handler.prepare)(payload, grpc)))
569 .map_err(|_| ServingError::Panicked)??;
570 let answer = encode_prepared_grant(prepared.grant().as_ref());
571 let slot = Slot {
572 operation: operation.to_owned(),
573 lifecycle: handler.lifecycle,
574 resident: handler.resident,
575 #[cfg(feature = "streaming")]
576 streams: prepared.streams(),
577 state: Mutex::new(SlotState {
578 prepared: Some(prepared),
579 finished: false,
580 cancelled: false,
581 waiters: Vec::new(),
582 }),
583 };
584 self.prepared.insert(invocation, Arc::new(slot));
585 Ok(answer)
586 }
587
588 #[cfg(feature = "streaming")]
589 pub fn streams(&self, invocation: u64) -> Result<WireStreams, ServingError> {
590 let slot = self.prepared.get(&invocation).ok_or(ServingError::Missing { invocation })?;
591 if self.closed || (self.ordinary_closed && !slot.lifecycle) || slot.is_finished() {
592 return Err(ServingError::Closing);
593 }
594 slot.streams.clone().ok_or(ServingError::StreamPhase)
595 }
596
597 #[tracing::instrument(target = "harmos::streams", name = "streams.initialize", level = "trace", skip_all, fields(invocation))]
598 pub async fn bind(&mut self, invocation: u64, context: &ExecutionContext) -> Result<(), ServingError> {
599 if self.closed {
600 return Err(ServingError::Closing);
601 }
602 let slot = self.prepared.get(&invocation).ok_or(ServingError::Missing { invocation })?;
603 if self.ordinary_closed && !slot.lifecycle {
604 return Err(ServingError::Closing);
605 }
606 slot.bind(invocation, context).await
607 }
608
609 #[tracing::instrument(target = "harmos::streams", name = "streams.attempt", level = "trace", skip_all, fields(invocation))]
610 pub async fn attempt(&mut self, invocation: u64, context: &ExecutionContext) -> Result<Vec<u8>, ServingError> {
611 if self.closed {
612 return Err(ServingError::Closing);
613 }
614 let slot = self.prepared.get(&invocation).ok_or(ServingError::Missing { invocation })?;
615 if self.ordinary_closed && !slot.lifecycle {
616 return Err(ServingError::Closing);
617 }
618 slot.attempt(invocation, context, None).await
619 }
620
621 #[tracing::instrument(target = "harmos::streams", name = "streams.finalize", level = "trace", skip_all, fields(invocation))]
622 pub async fn finalize(&mut self, invocation: u64) -> Result<(), ServingError> {
623 if let Some(slot) = self.prepared.get(&invocation) {
624 slot.finalize().await?;
625 }
626 self.prepared.remove(&invocation);
627 Ok(())
628 }
629
630 fn traffic(&self, command: WireCommand) -> Pin<Box<dyn Future<Output = WireAnswer> + Send + 'static>> {
631 #[cfg(not(feature = "streaming"))]
632 {
633 let _ = command;
634 Box::pin(async { wire_answer(Err(ServingError::StreamUnsupported)) })
635 }
636 #[cfg(feature = "streaming")]
637 {
638 let invocation = match &command {
639 WireCommand::StreamSend { invocation, .. }
640 | WireCommand::StreamFinish { invocation, .. }
641 | WireCommand::StreamReceive { invocation, .. } => *invocation,
642 _ => unreachable!("only stream traffic bypasses the phase loan"),
643 };
644 let streams = self.streams(invocation);
645 Box::pin(async move {
646 let streams = match streams {
647 Ok(streams) => streams,
648 Err(error) => return wire_answer(Err(error)),
649 };
650 match command {
651 WireCommand::StreamSend { epoch, payload, .. } => {
652 wire_answer(guarded(async { streams.send(epoch, &payload).await.map(|()| Vec::new()) }).await)
653 }
654 WireCommand::StreamFinish { epoch, .. } => wire_answer(streams.finish_input(epoch).map(|()| Vec::new())),
655 WireCommand::StreamReceive { epoch, .. } => match guarded(streams.receive(epoch)).await {
656 Ok(payload) => WireAnswer::Stream {
657 epoch,
658 transferred: streams.transferred(),
659 payload,
660 },
661 Err(error) => wire_answer(Err(error)),
662 },
663 _ => unreachable!("only stream traffic bypasses the phase loan"),
664 }
665 })
666 }
667 }
668
669 pub fn begin(
670 &mut self, command: WireCommand, context: Arc<ExecutionContext>,
671 ) -> Pin<Box<dyn Future<Output = WireAnswer> + Send + 'static>> {
672 let (invocation, phase) = command_identity(&command);
673 let span = tracing::debug_span!(target: "harmos::carrier", "carrier.receive", invocation, phase, outcome = tracing::field::Empty);
674 if command.is_stream_traffic() {
675 return Box::pin(self.traffic(command).instrument(span).in_current_context());
676 }
677 let slot = match &command {
678 WireCommand::Initialize { invocation, .. }
679 | WireCommand::Attempt { invocation, .. }
680 | WireCommand::StreamAttempt { invocation, .. }
681 | WireCommand::Finalize { invocation } => self.prepared.get(invocation).cloned(),
682 WireCommand::Prepare { .. } | WireCommand::PrepareGrpc { .. } => None,
683 WireCommand::StreamSend { .. } | WireCommand::StreamFinish { .. } | WireCommand::StreamReceive { .. } => {
684 unreachable!("stream traffic already dispatched")
685 }
686 };
687 let command = match command {
688 WireCommand::PrepareGrpc {
689 invocation,
690 operation,
691 fingerprint,
692 generation,
693 payload,
694 } => {
695 let result = span.in_scope(|| self.prepare_codec(invocation, &operation, fingerprint, generation, payload, true));
696 return Box::pin(async move { wire_answer(result) }.instrument(span).in_current_context());
697 }
698 WireCommand::Prepare {
699 invocation,
700 operation,
701 fingerprint,
702 generation,
703 payload,
704 } => {
705 let result = span.in_scope(|| self.prepare(invocation, &operation, fingerprint, generation, payload));
706 return Box::pin(async move { wire_answer(result) }.instrument(span).in_current_context());
707 }
708 command => command,
709 };
710 let refused = self.closed || (self.ordinary_closed && slot.as_ref().is_some_and(|slot| !slot.lifecycle));
711 if refused && matches!(command, WireCommand::Initialize { .. } | WireCommand::Attempt { .. } | WireCommand::StreamAttempt { .. }) {
712 return Box::pin(async { wire_answer(Err(ServingError::Closing)) }.instrument(span).in_current_context());
713 }
714 Box::pin(
715 async move {
716 let result = match command {
717 WireCommand::Initialize { invocation, .. } => match slot {
718 Some(slot) => slot.bind(invocation, &context).await.map(|()| Vec::new()),
719 None => Err(ServingError::Missing { invocation }),
720 },
721 WireCommand::Attempt { invocation, .. } => match slot {
722 Some(slot) => slot.attempt(invocation, &context, None).await,
723 None => Err(ServingError::Missing { invocation }),
724 },
725 WireCommand::StreamAttempt { invocation, epoch, .. } => match slot {
726 Some(slot) => slot.attempt(invocation, &context, Some(epoch)).await,
727 None => Err(ServingError::Missing { invocation }),
728 },
729 WireCommand::StreamSend { .. } | WireCommand::StreamFinish { .. } | WireCommand::StreamReceive { .. } => {
730 unreachable!("stream traffic already dispatched")
731 }
732 WireCommand::Finalize { .. } => match slot {
733 Some(slot) => slot.finalize().await.map(|()| Vec::new()),
734 None => Ok(Vec::new()),
735 },
736 WireCommand::Prepare { .. } | WireCommand::PrepareGrpc { .. } => {
737 unreachable!("preparation is synchronous")
738 }
739 };
740 wire_answer(result)
741 }
742 .instrument(span)
743 .in_current_context(),
744 )
745 }
746
747 pub fn close_ordinary(&mut self) -> Pin<Box<dyn Future<Output = Result<(), ServingError>> + Send + 'static>> {
748 self.ordinary_closed = true;
749 let slots: Vec<_> = self.prepared.values().filter(|slot| !slot.lifecycle).cloned().collect();
750 for slot in &slots {
751 slot.cancel();
752 }
753 Box::pin(async move {
754 for slot in slots {
755 slot.finalize().await?;
756 }
757 Ok(())
758 })
759 }
760
761 pub fn release_ordinary(&mut self) -> Result<impl Send + use<>, ServingError> {
762 if !self.ordinary_closed || self.prepared.values().any(|slot| !slot.lifecycle && !slot.is_finished()) {
763 return Err(ServingError::Unsettled);
764 }
765 let retired_slots = self.prepared.extract_if(.., |_, slot| !slot.lifecycle).collect::<Vec<_>>();
767 let retired_handlers = self.handlers.extract_if(.., |_, handler| !handler.lifecycle).collect::<Vec<_>>();
768 let retired_services = std::mem::take(&mut self.services);
769 Ok((retired_slots, retired_handlers, retired_services))
770 }
771
772 pub async fn settle_ordinary(&mut self) -> Result<(), ServingError> {
773 self.close_ordinary().await?;
774 let retired = self.release_ordinary()?;
775 drop(retired);
776 Ok(())
777 }
778
779 pub fn close(&mut self) -> Pin<Box<dyn Future<Output = Result<(), ServingError>> + Send + 'static>> {
780 self.closed = true;
781 let slots: Vec<_> = self.prepared.values().cloned().collect();
782 for slot in &slots {
783 slot.cancel();
784 }
785 Box::pin(async move {
786 for slot in slots {
787 slot.finalize().await?;
788 }
789 Ok(())
790 })
791 }
792
793 pub fn release(&mut self) -> Result<impl Send + use<>, ServingError> {
794 if !self.closed || self.prepared.values().any(|slot| !slot.is_finished()) {
795 return Err(ServingError::Unsettled);
796 }
797 Ok((std::mem::take(&mut self.prepared), std::mem::take(&mut self.handlers), std::mem::take(&mut self.services)))
798 }
799
800 pub async fn settle(&mut self) -> Result<(), ServingError> {
801 self.close().await?;
802 let retired = self.release()?;
803 drop(retired);
804 Ok(())
805 }
806
807 pub fn is_settled(&self) -> bool {
808 self.closed && self.prepared.values().all(|slot| slot.is_finished())
809 }
810
811 pub fn is_closed(&self) -> bool {
812 self.closed
813 }
814
815 #[tracing::instrument(target = "harmos::carrier", name = "carrier.answer", level = "debug", skip_all, fields(invocation = command_identity(&command).0, phase = command_identity(&command).1))]
816 pub async fn answer(&mut self, command: WireCommand, context: &ExecutionContext) -> WireAnswer {
817 if command.is_stream_traffic() {
818 return self.traffic(command).await;
819 }
820 if let WireCommand::StreamAttempt { invocation, epoch, .. } = command {
821 if self.closed || (self.ordinary_closed && self.prepared.get(&invocation).is_some_and(|slot| !slot.lifecycle)) {
822 return wire_answer(Err(ServingError::Closing));
823 }
824 let Some(slot) = self.prepared.get(&invocation) else {
825 return wire_answer(Err(ServingError::Missing { invocation }));
826 };
827 return wire_answer(slot.attempt(invocation, context, Some(epoch)).await);
828 }
829 let result = match command {
830 WireCommand::PrepareGrpc {
831 invocation,
832 operation,
833 fingerprint,
834 generation,
835 payload,
836 } => self.prepare_codec(invocation, &operation, fingerprint, generation, payload, true),
837 WireCommand::Prepare {
838 invocation,
839 operation,
840 fingerprint,
841 generation,
842 payload,
843 } => self.prepare(invocation, &operation, fingerprint, generation, payload),
844 WireCommand::Initialize { invocation, .. } => self.bind(invocation, context).await.map(|()| Vec::new()),
845 WireCommand::Attempt { invocation, .. } => self.attempt(invocation, context).await,
846 WireCommand::StreamAttempt { .. }
847 | WireCommand::StreamSend { .. }
848 | WireCommand::StreamFinish { .. }
849 | WireCommand::StreamReceive { .. } => {
850 unreachable!("stream command already dispatched")
851 }
852 WireCommand::Finalize { invocation } => self.finalize(invocation).await.map(|()| Vec::new()),
853 };
854 wire_answer(result)
855 }
856}
857
858impl Slot {
859 fn cancel(&self) {
860 let waiters = {
861 let mut state = self.state.lock().expect("invocation state lock");
862 state.cancelled = true;
863 std::mem::take(&mut state.waiters)
864 };
865 #[cfg(feature = "streaming")]
866 if let Some(streams) = &self.streams {
867 streams.close();
868 }
869 for waiter in waiters {
870 if let Some(waiter) = waiter.upgrade() {
871 let wake = waiter.lock().expect("invocation waiter lock").take();
872 if let Some(wake) = wake {
873 wake.wake();
874 }
875 }
876 }
877 }
878
879 async fn active<T>(&self, future: impl Future<Output = Result<T, ServingError>>) -> Result<T, ServingError> {
880 let mut future = std::pin::pin!(future);
881 let waiter = Arc::new(Mutex::new(None));
882 std::future::poll_fn(|context| {
883 {
884 let mut state = self.state.lock().expect("invocation state lock");
885 if state.cancelled {
886 return Poll::Ready(Err(ServingError::Closing));
887 }
888 *waiter.lock().expect("invocation waiter lock") = Some(context.waker().clone());
889 state.waiters.retain(|entry| entry.strong_count() != 0);
890 if !state.waiters.iter().any(|entry| entry.ptr_eq(&Arc::downgrade(&waiter))) {
891 state.waiters.push(Arc::downgrade(&waiter));
892 }
893 }
894 future.as_mut().poll(context)
895 })
896 .await
897 }
898
899 fn is_finished(&self) -> bool {
900 self.state.lock().expect("invocation state lock").finished
901 }
902
903 async fn borrow(self: &Arc<Self>) -> Option<Loan> {
904 let waiter = Arc::new(Mutex::new(None));
905 std::future::poll_fn(|context| {
906 let mut state = self.state.lock().expect("invocation state lock");
907 if state.finished {
908 return Poll::Ready(None);
909 }
910 if let Some(prepared) = state.prepared.take() {
911 return Poll::Ready(Some(Loan {
912 slot: Arc::clone(self),
913 prepared: Some(prepared),
914 finished: false,
915 }));
916 }
917 *waiter.lock().expect("invocation waiter lock") = Some(context.waker().clone());
918 state.waiters.retain(|entry| entry.strong_count() != 0);
919 if !state.waiters.iter().any(|entry| entry.ptr_eq(&Arc::downgrade(&waiter))) {
920 state.waiters.push(Arc::downgrade(&waiter));
921 }
922 Poll::Pending
923 })
924 .await
925 }
926}
927
928impl Slot {
929 #[tracing::instrument(target = "harmos::streams", name = "streams.initialize", level = "trace", skip_all, fields(invocation))]
930 async fn bind(self: &Arc<Self>, invocation: u64, context: &ExecutionContext) -> Result<(), ServingError> {
931 let mut loan = self.borrow().await.ok_or(ServingError::Missing { invocation })?;
932 self.active(loan.prepared.as_mut().expect("loaned invocation state").bind(context)).await
933 }
934
935 #[tracing::instrument(target = "harmos::streams", name = "streams.attempt", level = "trace", skip_all, fields(invocation))]
936 async fn attempt(self: &Arc<Self>, invocation: u64, context: &ExecutionContext, epoch: Option<u64>) -> Result<Vec<u8>, ServingError> {
937 let mut loan = self.borrow().await.ok_or(ServingError::Missing { invocation })?;
938 #[cfg(feature = "streaming")]
939 let attempt = match (&self.streams, epoch) {
940 (Some(streams), Some(epoch)) => Some(streams.start(epoch)?),
941 (None, None) => None,
942 _ => return Err(ServingError::StreamUnsupported),
943 };
944 #[cfg(not(feature = "streaming"))]
945 if epoch.is_some() {
946 return Err(ServingError::StreamUnsupported);
947 }
948 let result = self.active(loan.prepared.as_mut().expect("loaned invocation state").attempt(context)).await;
949 #[cfg(feature = "streaming")]
950 if let Some(attempt) = attempt {
951 attempt.end();
952 }
953 result
954 }
955
956 #[tracing::instrument(target = "harmos::carrier", name = "carrier.finalize", level = "debug", skip_all)]
957 async fn finalize(self: &Arc<Self>) -> Result<(), ServingError> {
958 #[cfg(feature = "streaming")]
959 if let Some(streams) = &self.streams {
960 streams.close();
961 }
962 let Some(mut loan) = self.borrow().await else {
963 return Ok(());
964 };
965 loan.prepared.as_mut().expect("loaned invocation state").finalize().await?;
966 loan.finished = true;
968 let prepared = loan.prepared.take();
969 drop(prepared);
970 Ok(())
971 }
972}
973
974impl Drop for Loan {
975 fn drop(&mut self) {
976 let waiters = {
977 let mut state = self.slot.state.lock().expect("invocation state lock");
978 state.prepared = self.prepared.take();
979 state.finished = self.finished;
980 std::mem::take(&mut state.waiters)
981 };
982 for waiter in waiters {
983 if let Some(waiter) = waiter.upgrade() {
984 let wake = waiter.lock().expect("invocation waiter lock").take();
985 if let Some(wake) = wake {
986 wake.wake();
987 }
988 }
989 }
990 }
991}
992
993impl WireCommand {
994 pub const LIMIT: usize = codec::LIMIT;
995 fn is_stream_traffic(&self) -> bool {
996 matches!(self, Self::StreamSend { .. } | Self::StreamFinish { .. } | Self::StreamReceive { .. })
997 }
998}
999
1000impl ServingError {
1001 pub fn classification(&self) -> FailureKind {
1002 match self {
1003 Self::Handler(failure) => failure.code(),
1004 Self::Panicked => FailureKind::Ambiguous,
1005 Self::Codec(_) | Self::Declaration(_) | Self::Lifecycle(_) | Self::Contract(_) | Self::Catalog | Self::Streaming { .. } => {
1006 FailureKind::Invalid
1007 }
1008 _ => FailureKind::Permanent,
1009 }
1010 }
1011}
1012
1013pub fn encode_prepared_grant(grant: Option<&crate::plugin::Grant>) -> Vec<u8> {
1017 grant.map_or_else(Vec::new, |grant| serde_json::to_vec(grant).expect("a grant serializes to JSON"))
1018}
1019
1020pub fn decode_prepared_grant(payload: &[u8]) -> Result<Option<crate::plugin::Grant>, serde_json::Error> {
1021 if payload.is_empty() {
1022 return Ok(None);
1023 }
1024 serde_json::from_slice(payload).map(Some)
1025}
1026
1027fn command_identity(command: &WireCommand) -> (u64, &'static str) {
1030 match command {
1031 WireCommand::Prepare { invocation, .. } => (*invocation, "prepare"),
1032 WireCommand::PrepareGrpc { invocation, .. } => (*invocation, "prepare_grpc"),
1033 WireCommand::Initialize { invocation, .. } => (*invocation, "initialize"),
1034 WireCommand::Attempt { invocation, .. } => (*invocation, "attempt"),
1035 WireCommand::Finalize { invocation, .. } => (*invocation, "finalize"),
1036 WireCommand::StreamAttempt { invocation, .. } => (*invocation, "stream_attempt"),
1037 WireCommand::StreamSend { invocation, .. } => (*invocation, "stream_send"),
1038 WireCommand::StreamFinish { invocation, .. } => (*invocation, "stream_finish"),
1039 WireCommand::StreamReceive { invocation, .. } => (*invocation, "stream_receive"),
1040 }
1041}
1042
1043fn wire_answer(result: Result<Vec<u8>, ServingError>) -> WireAnswer {
1044 tracing::Span::current().record("outcome", if result.is_ok() { "ready" } else { "failed" });
1045 match result {
1046 Ok(payload) => WireAnswer::Ready { payload },
1047 Err(ServingError::Handler(failure)) => WireAnswer::Failed { failure },
1048 Err(error) => WireAnswer::Failed {
1049 failure: crate::Failure::originate(error.classification(), &error, crate::failure::Origin::Plugin),
1050 },
1051 }
1052}
1053
1054async fn guarded<T>(future: impl Future<Output = Result<T, ServingError>>) -> Result<T, ServingError> {
1055 let mut future = std::pin::pin!(future);
1056 std::future::poll_fn(|context| match std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| future.as_mut().poll(context))) {
1057 Ok(result) => result,
1058 Err(_) => std::task::Poll::Ready(Err(ServingError::Panicked)),
1059 })
1060 .await
1061}
1062
1063#[cfg(test)]
1066mod tests {
1067 use super::*;
1068
1069 #[test]
1070 fn withdrawal_cancels_a_pending_active_future() {
1071 let slot = Slot {
1072 operation: "host.read".to_owned(),
1073 lifecycle: false,
1074 resident: false,
1075 #[cfg(feature = "streaming")]
1076 streams: None,
1077 state: Mutex::new(SlotState {
1078 prepared: None,
1079 finished: false,
1080 cancelled: false,
1081 waiters: Vec::new(),
1082 }),
1083 };
1084 let mut active = Box::pin(slot.active(std::future::pending::<Result<(), ServingError>>()));
1085 let mut context = std::task::Context::from_waker(Waker::noop());
1086 assert!(active.as_mut().poll(&mut context).is_pending());
1087 slot.cancel();
1088 assert!(matches!(active.as_mut().poll(&mut context), Poll::Ready(Err(ServingError::Closing))));
1089 }
1090}