1use std::{
2 any::Any,
3 collections::BTreeMap,
4 fmt,
5 future::Future,
6 marker::PhantomData,
7 pin::Pin,
8 rc::Rc,
9 sync::{
10 Arc,
11 atomic::{AtomicU8, Ordering},
12 },
13 task::{Context, Poll},
14 time::Instant,
15};
16
17use super::{LaneRoute, LaneTask};
18use futures::{future::LocalBoxFuture, task::AtomicWaker};
19use lenso_app_plan::ResolvedAppPlan;
20use lenso_kernel::{
21 CancellationToken, InvocationContext, NativeRequestEndpoint, NativeRequestFuture,
22 RequestCapability, RuntimeFailure, TypedNativeRequestEndpoint,
23};
24
25trait RequestTransferFactory: fmt::Debug + Send + Sync {
26 fn endpoint(&self, provider_lane: LaneRoute, epoch: Instant) -> Rc<dyn NativeRequestEndpoint>;
27}
28
29struct TypedRequestTransferFactory<C: RequestCapability> {
30 operations: &'static [&'static str],
31 capability: PhantomData<fn() -> C>,
32}
33
34impl<C: RequestCapability> fmt::Debug for TypedRequestTransferFactory<C> {
35 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
36 formatter
37 .debug_struct("TypedRequestTransferFactory")
38 .field("capability", &C::ID)
39 .finish_non_exhaustive()
40 }
41}
42
43impl<C> RequestTransferFactory for TypedRequestTransferFactory<C>
44where
45 C: RequestCapability,
46 C::Request: Send,
47 C::Response: Send,
48 C::DomainError: Send,
49{
50 fn endpoint(&self, provider_lane: LaneRoute, epoch: Instant) -> Rc<dyn NativeRequestEndpoint> {
51 let typed_provider_lane = provider_lane.clone();
52 let operations = self.operations;
53 Rc::new(CrossLaneRequestEndpoint::<C> {
54 operations: self.operations,
55 typed: TypedNativeRequestEndpoint::new(move |operation, request, context| {
56 let Some(operation) = operations
57 .iter()
58 .copied()
59 .find(|candidate| *candidate == operation)
60 else {
61 return Box::pin(futures::future::ready(Err(
62 RuntimeFailure::UnknownOperation {
63 capability: C::ID,
64 operation: operation.to_owned(),
65 },
66 )));
67 };
68 invoke_cross_lane::<C>(
69 typed_provider_lane.clone(),
70 epoch,
71 operation,
72 request,
73 context,
74 )
75 }),
76 })
77 }
78}
79
80#[derive(Clone, Debug, Default)]
82pub struct CrossLaneRequestCatalog {
83 factories: BTreeMap<&'static str, Arc<dyn RequestTransferFactory>>,
84}
85
86impl CrossLaneRequestCatalog {
87 pub fn new() -> Self {
89 Self::default()
90 }
91
92 #[must_use]
94 pub fn with_request<C>(mut self, operations: &'static [&'static str]) -> Self
95 where
96 C: RequestCapability,
97 C::Request: Send,
98 C::Response: Send,
99 C::DomainError: Send,
100 {
101 self.factories.insert(
102 C::ID,
103 Arc::new(TypedRequestTransferFactory::<C> {
104 operations,
105 capability: PhantomData,
106 }),
107 );
108 self
109 }
110
111 pub(super) fn contains(&self, capability_id: &str) -> bool {
112 self.factories.contains_key(capability_id)
113 }
114
115 pub(super) fn validate_plan(
116 &self,
117 plan: &ResolvedAppPlan,
118 ) -> Result<(), super::ReplicatedRunnerError> {
119 for binding in plan.capability_bindings() {
120 let consumer = plan
121 .plugin_instance(binding.consumer_instance())
122 .expect("validated binding consumer should exist");
123 let provider = plan
124 .plugin_instance(binding.provider_instance())
125 .expect("validated binding provider should exist");
126 let endpoint = provider
127 .provided_capabilities()
128 .iter()
129 .find(|endpoint| endpoint.capability_id() == binding.capability_id())
130 .expect("validated provider endpoint should exist");
131 if consumer.execution_lane() != provider.execution_lane()
132 && !endpoint.request_operations().is_empty()
133 && !self.contains(binding.capability_id())
134 {
135 return Err(
136 super::ReplicatedRunnerError::MissingCrossLaneRequestTransfer {
137 capability: binding.capability_id().to_owned(),
138 },
139 );
140 }
141 }
142 Ok(())
143 }
144
145 pub(super) fn endpoint(
146 &self,
147 capability_id: &str,
148 provider_lane: LaneRoute,
149 epoch: Instant,
150 ) -> Option<Rc<dyn NativeRequestEndpoint>> {
151 self.factories
152 .get(capability_id)
153 .map(|factory| factory.endpoint(provider_lane, epoch))
154 }
155}
156
157struct CrossLaneRequestEndpoint<C: RequestCapability> {
158 operations: &'static [&'static str],
159 typed: TypedNativeRequestEndpoint<C>,
160}
161
162impl<C: RequestCapability> fmt::Debug for CrossLaneRequestEndpoint<C> {
163 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
164 formatter
165 .debug_struct("CrossLaneRequestEndpoint")
166 .field("capability", &C::ID)
167 .finish_non_exhaustive()
168 }
169}
170
171impl<C> NativeRequestEndpoint for CrossLaneRequestEndpoint<C>
172where
173 C: RequestCapability,
174 C::Request: Send,
175 C::Response: Send,
176 C::DomainError: Send,
177{
178 fn capability_id(&self) -> &'static str {
179 C::ID
180 }
181
182 fn descriptor_version(&self) -> &'static str {
183 C::DESCRIPTOR_VERSION
184 }
185
186 fn operations(&self) -> &'static [&'static str] {
187 self.operations
188 }
189
190 fn typed_endpoint(&self) -> Option<&dyn Any> {
191 Some(&self.typed)
192 }
193
194 fn invoke(
195 &self,
196 operation: &str,
197 request: Box<dyn Any>,
198 context: InvocationContext,
199 ) -> LocalBoxFuture<'static, Result<Result<Box<dyn Any>, Box<dyn Any>>, RuntimeFailure>> {
200 let Ok(request) = request.downcast::<C::Request>() else {
201 return Box::pin(futures::future::ready(Err(
202 RuntimeFailure::ProtocolViolation { capability: C::ID },
203 )));
204 };
205 let invocation = self.typed.invoke(operation, *request, context);
206 Box::pin(async move {
207 let result = invocation.await?;
208 Ok(result
209 .map(|response| Box::new(response) as Box<dyn Any>)
210 .map_err(|error| Box::new(error) as Box<dyn Any>))
211 })
212 }
213}
214
215#[allow(clippy::too_many_lines)]
216fn invoke_cross_lane<C>(
217 provider_lane: LaneRoute,
218 epoch: Instant,
219 operation: &'static str,
220 request: C::Request,
221 context: InvocationContext,
222) -> NativeRequestFuture<C>
223where
224 C: RequestCapability,
225 C::Request: Send,
226 C::Response: Send,
227 C::DomainError: Send,
228{
229 Box::pin(async move {
230 let provider_lane = provider_lane.upgrade().ok_or_else(lane_unavailable::<C>)?;
231 let caller_instance = context
232 .caller_instance()
233 .ok_or_else(|| RuntimeFailure::InvalidResolvedPlan {
234 detail: format!("cross-lane invocation of `{}` has no planned caller", C::ID),
235 })?
236 .to_owned();
237 let cancellation = context.cancellation();
238 let deadline = context.deadline();
239 let request_id = context.request_id();
240 let (transferred, cancellation_signal) = TransferredInvocationContext::capture(&context);
241 let mut cancellation_guard = TransferredCancellationGuard::new(cancellation_signal);
242 let (completed, completion) = futures::channel::oneshot::channel();
243 let command: LaneTask = Box::new(move |lane| {
244 tokio::task::spawn_local(async move {
245 let local_cancellation = CancellationToken::new();
246 let (context, transferred_cancellation) =
247 transferred.restore(local_cancellation.clone());
248 if transferred_cancellation.is_cancelled() {
249 local_cancellation.cancel();
250 }
251 let handle = match lane.request_handle::<C>(&caller_instance) {
252 Ok(handle) => handle,
253 Err(error) => {
254 let _ = completed.send(Err(error));
255 return;
256 }
257 };
258 let invocation = handle.invoke_with_context(operation, context, request);
259 tokio::pin!(invocation);
260 let result = if local_cancellation.is_cancelled() {
261 invocation.await
262 } else {
263 tokio::select! {
264 result = &mut invocation => result,
265 () = transferred_cancellation.cancelled() => {
266 local_cancellation.cancel();
267 invocation.await
268 }
269 }
270 };
271 let _ = completed.send(result);
272 });
273 });
274
275 let cancelled = cancellation.cancelled();
276 tokio::pin!(cancelled);
277 let result = if let Some(deadline) = deadline {
278 let sleep = tokio::time::sleep_until((epoch + deadline).into());
279 tokio::pin!(sleep);
280 if let Err(error) = provider_lane.try_send(command) {
281 let command = error.into_inner();
282 let send = provider_lane.send(command);
283 tokio::pin!(send);
284 tokio::select! {
285 result = &mut send => result.map_err(|_| lane_unavailable::<C>())?,
286 () = &mut cancelled => {
287 cancellation_guard.cancel();
288 return Err(RuntimeFailure::Cancelled { request_id });
289 }
290 () = &mut sleep => {
291 cancellation_guard.cancel();
292 return Err(RuntimeFailure::DeadlineExceeded { request_id });
293 }
294 }
295 }
296 tokio::select! {
297 biased;
298 result = completion => result.map_err(|_| lane_unavailable::<C>())?,
299 () = &mut cancelled => {
300 cancellation_guard.cancel();
301 Err(RuntimeFailure::Cancelled { request_id })
302 }
303 () = &mut sleep => {
304 cancellation_guard.cancel();
305 Err(RuntimeFailure::DeadlineExceeded { request_id })
306 }
307 }
308 } else {
309 if let Err(error) = provider_lane.try_send(command) {
310 let send = provider_lane.send(error.into_inner());
311 tokio::pin!(send);
312 tokio::select! {
313 result = &mut send => result.map_err(|_| lane_unavailable::<C>())?,
314 () = &mut cancelled => {
315 cancellation_guard.cancel();
316 return Err(RuntimeFailure::Cancelled { request_id });
317 }
318 }
319 }
320 tokio::select! {
321 biased;
322 result = completion => result.map_err(|_| lane_unavailable::<C>())?,
323 () = &mut cancelled => {
324 cancellation_guard.cancel();
325 Err(RuntimeFailure::Cancelled { request_id })
326 }
327 }
328 };
329 cancellation_guard.disarm();
330 result
331 })
332}
333
334fn lane_unavailable<C: RequestCapability>() -> RuntimeFailure {
335 RuntimeFailure::Internal {
336 detail: format!("provider lane for `{}` is unavailable", C::ID),
337 }
338}
339
340#[derive(Debug)]
341pub(super) struct TransferredInvocationContext {
342 request_id: u64,
343 deadline: Option<std::time::Duration>,
344 cancellation: Arc<TransferredCancellation>,
345 extensions: Vec<lenso_kernel::InvocationExtension>,
346 sealed_extensions: Vec<lenso_kernel::SealedInvocationExtension>,
347}
348
349impl TransferredInvocationContext {
350 pub(super) fn capture(context: &InvocationContext) -> (Self, Arc<TransferredCancellation>) {
351 let cancellation = Arc::new(TransferredCancellation::new(context.is_cancelled()));
352 (
353 Self {
354 request_id: context.request_id(),
355 deadline: context.deadline(),
356 cancellation: Arc::clone(&cancellation),
357 extensions: context.extensions().cloned().collect(),
358 sealed_extensions: context.sealed_extensions().cloned().collect(),
359 },
360 cancellation,
361 )
362 }
363
364 pub(super) fn restore(
365 self,
366 cancellation: CancellationToken,
367 ) -> (InvocationContext, Arc<TransferredCancellation>) {
368 let mut context = InvocationContext::new(self.request_id, self.deadline, cancellation);
369 for extension in self.extensions {
370 context = context
371 .with_extension(extension.key(), extension.value().to_vec())
372 .expect("captured ordinary Invocation Context extension remains valid");
373 }
374 for extension in self.sealed_extensions {
375 context = context
376 .with_sealed_extension(extension)
377 .expect("captured sealed Invocation Context extension remains valid");
378 }
379 (context, self.cancellation)
380 }
381}
382
383#[derive(Debug)]
384pub(super) struct TransferredCancellation {
385 state: AtomicU8,
387 waker: AtomicWaker,
388}
389
390const TRANSFER_PENDING: u8 = 0;
391const TRANSFER_CANCELLED: u8 = 1;
392const TRANSFER_ACCEPTED: u8 = 2;
393
394#[derive(Debug)]
396pub(super) struct TransferredCancellationGuard {
397 cancellation: Arc<TransferredCancellation>,
398 armed: bool,
399}
400
401impl TransferredCancellationGuard {
402 pub(super) fn new(cancellation: Arc<TransferredCancellation>) -> Self {
403 Self {
404 cancellation,
405 armed: true,
406 }
407 }
408
409 pub(super) fn cancellation(&self) -> Arc<TransferredCancellation> {
410 Arc::clone(&self.cancellation)
411 }
412
413 pub(super) fn cancel(&self) {
414 self.cancellation.cancel();
415 }
416
417 pub(super) fn disarm(&mut self) {
418 self.armed = false;
419 }
420}
421
422impl Drop for TransferredCancellationGuard {
423 fn drop(&mut self) {
424 if self.armed {
425 self.cancellation.cancel();
426 }
427 }
428}
429
430impl TransferredCancellation {
431 fn new(cancelled: bool) -> Self {
432 Self {
433 state: AtomicU8::new(if cancelled {
434 TRANSFER_CANCELLED
435 } else {
436 TRANSFER_PENDING
437 }),
438 waker: AtomicWaker::new(),
439 }
440 }
441
442 pub(super) fn is_cancelled(&self) -> bool {
443 self.state.load(Ordering::Acquire) == TRANSFER_CANCELLED
444 }
445
446 pub(super) fn cancel(&self) {
447 if self.state.swap(TRANSFER_CANCELLED, Ordering::AcqRel) != TRANSFER_CANCELLED {
448 self.waker.wake();
449 }
450 }
451
452 pub(super) fn accept(&self) {
453 if self
454 .state
455 .compare_exchange(
456 TRANSFER_PENDING,
457 TRANSFER_ACCEPTED,
458 Ordering::AcqRel,
459 Ordering::Acquire,
460 )
461 .is_ok()
462 {
463 self.waker.wake();
464 }
465 }
466
467 pub(super) fn cancelled(&self) -> TransferredCancellationFuture<'_> {
468 TransferredCancellationFuture { cancellation: self }
469 }
470
471 pub(super) fn settled(&self) -> TransferredSettlementFuture<'_> {
472 TransferredSettlementFuture { cancellation: self }
473 }
474}
475
476pub(super) struct TransferredCancellationFuture<'a> {
477 cancellation: &'a TransferredCancellation,
478}
479
480pub(super) struct TransferredSettlementFuture<'a> {
481 cancellation: &'a TransferredCancellation,
482}
483
484impl Future for TransferredSettlementFuture<'_> {
485 type Output = bool;
486
487 fn poll(self: Pin<&mut Self>, context: &mut Context<'_>) -> Poll<Self::Output> {
488 let state = self.cancellation.state.load(Ordering::Acquire);
489 if state != TRANSFER_PENDING {
490 return Poll::Ready(state == TRANSFER_CANCELLED);
491 }
492 self.cancellation.waker.register(context.waker());
493 let state = self.cancellation.state.load(Ordering::Acquire);
494 if state == TRANSFER_PENDING {
495 Poll::Pending
496 } else {
497 Poll::Ready(state == TRANSFER_CANCELLED)
498 }
499 }
500}
501
502impl Future for TransferredCancellationFuture<'_> {
503 type Output = ();
504
505 fn poll(self: Pin<&mut Self>, context: &mut Context<'_>) -> Poll<Self::Output> {
506 if self.cancellation.is_cancelled() {
507 return Poll::Ready(());
508 }
509 self.cancellation.waker.register(context.waker());
510 if self.cancellation.is_cancelled() {
511 Poll::Ready(())
512 } else {
513 Poll::Pending
514 }
515 }
516}
517
518#[cfg(test)]
519mod tests {
520 use std::sync::Arc;
521
522 use super::TransferredCancellation;
523
524 #[tokio::test(flavor = "current_thread")]
525 async fn transferred_cancellation_observes_an_initial_signal() {
526 let cancellation = TransferredCancellation::new(true);
527
528 cancellation.cancelled().await;
529
530 assert!(cancellation.is_cancelled());
531 }
532
533 #[tokio::test(flavor = "current_thread")]
534 async fn transferred_cancellation_wakes_the_provider_waiter() {
535 let cancellation = Arc::new(TransferredCancellation::new(false));
536 let canceller = Arc::clone(&cancellation);
537
538 tokio::join!(cancellation.cancelled(), async move {
539 tokio::task::yield_now().await;
540 canceller.cancel();
541 });
542
543 assert!(cancellation.is_cancelled());
544 }
545
546 #[tokio::test(flavor = "current_thread")]
547 async fn transferred_acceptance_settles_without_cancellation() {
548 let cancellation = Arc::new(TransferredCancellation::new(false));
549 let accepter = Arc::clone(&cancellation);
550
551 let cancelled = tokio::join!(cancellation.settled(), async move {
552 tokio::task::yield_now().await;
553 accepter.accept();
554 })
555 .0;
556
557 assert!(!cancelled);
558 assert!(!cancellation.is_cancelled());
559 }
560}