1use std::{
2 any::Any, collections::BTreeMap, fmt, marker::PhantomData, rc::Rc, sync::Arc, time::Instant,
3};
4
5use futures::future::LocalBoxFuture;
6use lenso_app_plan::ResolvedAppPlan;
7use lenso_kernel::{
8 CancellationToken, InvocationContext, NativeRequestEndpoint, NativeRequestFuture,
9 RequestCapability, RuntimeFailure, TypedNativeRequestEndpoint,
10};
11use tokio::sync::watch;
12
13use super::{LaneRoute, LaneTask};
14
15trait RequestTransferFactory: fmt::Debug + Send + Sync {
16 fn endpoint(&self, provider_lane: LaneRoute, epoch: Instant) -> Rc<dyn NativeRequestEndpoint>;
17}
18
19struct TypedRequestTransferFactory<C: RequestCapability> {
20 operations: &'static [&'static str],
21 capability: PhantomData<fn() -> C>,
22}
23
24impl<C: RequestCapability> fmt::Debug for TypedRequestTransferFactory<C> {
25 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
26 formatter
27 .debug_struct("TypedRequestTransferFactory")
28 .field("capability", &C::ID)
29 .finish_non_exhaustive()
30 }
31}
32
33impl<C> RequestTransferFactory for TypedRequestTransferFactory<C>
34where
35 C: RequestCapability,
36 C::Request: Send,
37 C::Response: Send,
38 C::DomainError: Send,
39{
40 fn endpoint(&self, provider_lane: LaneRoute, epoch: Instant) -> Rc<dyn NativeRequestEndpoint> {
41 let typed_provider_lane = provider_lane.clone();
42 let operations = self.operations;
43 Rc::new(CrossLaneRequestEndpoint::<C> {
44 operations: self.operations,
45 typed: TypedNativeRequestEndpoint::new(move |operation, request, context| {
46 let Some(operation) = operations
47 .iter()
48 .copied()
49 .find(|candidate| *candidate == operation)
50 else {
51 return Box::pin(futures::future::ready(Err(
52 RuntimeFailure::UnknownOperation {
53 capability: C::ID,
54 operation: operation.to_owned(),
55 },
56 )));
57 };
58 invoke_cross_lane::<C>(
59 typed_provider_lane.clone(),
60 epoch,
61 operation,
62 request,
63 context,
64 )
65 }),
66 })
67 }
68}
69
70#[derive(Clone, Debug, Default)]
72pub struct CrossLaneRequestCatalog {
73 factories: BTreeMap<&'static str, Arc<dyn RequestTransferFactory>>,
74}
75
76impl CrossLaneRequestCatalog {
77 pub fn new() -> Self {
79 Self::default()
80 }
81
82 #[must_use]
84 pub fn with_request<C>(mut self, operations: &'static [&'static str]) -> Self
85 where
86 C: RequestCapability,
87 C::Request: Send,
88 C::Response: Send,
89 C::DomainError: Send,
90 {
91 self.factories.insert(
92 C::ID,
93 Arc::new(TypedRequestTransferFactory::<C> {
94 operations,
95 capability: PhantomData,
96 }),
97 );
98 self
99 }
100
101 pub(super) fn contains(&self, capability_id: &str) -> bool {
102 self.factories.contains_key(capability_id)
103 }
104
105 pub(super) fn validate_plan(
106 &self,
107 plan: &ResolvedAppPlan,
108 ) -> Result<(), super::ReplicatedRunnerError> {
109 for binding in plan.capability_bindings() {
110 let consumer = plan
111 .module_instance(binding.consumer_instance())
112 .expect("validated binding consumer should exist");
113 let provider = plan
114 .module_instance(binding.provider_instance())
115 .expect("validated binding provider should exist");
116 let endpoint = provider
117 .provided_capabilities()
118 .iter()
119 .find(|endpoint| endpoint.capability_id() == binding.capability_id())
120 .expect("validated provider endpoint should exist");
121 if consumer.execution_lane() != provider.execution_lane()
122 && !endpoint.request_operations().is_empty()
123 && !self.contains(binding.capability_id())
124 {
125 return Err(
126 super::ReplicatedRunnerError::MissingCrossLaneRequestTransfer {
127 capability: binding.capability_id().to_owned(),
128 },
129 );
130 }
131 }
132 Ok(())
133 }
134
135 pub(super) fn endpoint(
136 &self,
137 capability_id: &str,
138 provider_lane: LaneRoute,
139 epoch: Instant,
140 ) -> Option<Rc<dyn NativeRequestEndpoint>> {
141 self.factories
142 .get(capability_id)
143 .map(|factory| factory.endpoint(provider_lane, epoch))
144 }
145}
146
147struct CrossLaneRequestEndpoint<C: RequestCapability> {
148 operations: &'static [&'static str],
149 typed: TypedNativeRequestEndpoint<C>,
150}
151
152impl<C: RequestCapability> fmt::Debug for CrossLaneRequestEndpoint<C> {
153 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
154 formatter
155 .debug_struct("CrossLaneRequestEndpoint")
156 .field("capability", &C::ID)
157 .finish_non_exhaustive()
158 }
159}
160
161impl<C> NativeRequestEndpoint for CrossLaneRequestEndpoint<C>
162where
163 C: RequestCapability,
164 C::Request: Send,
165 C::Response: Send,
166 C::DomainError: Send,
167{
168 fn capability_id(&self) -> &'static str {
169 C::ID
170 }
171
172 fn descriptor_version(&self) -> &'static str {
173 C::DESCRIPTOR_VERSION
174 }
175
176 fn operations(&self) -> &'static [&'static str] {
177 self.operations
178 }
179
180 fn typed_endpoint(&self) -> Option<&dyn Any> {
181 Some(&self.typed)
182 }
183
184 fn invoke(
185 &self,
186 operation: &str,
187 request: Box<dyn Any>,
188 context: InvocationContext,
189 ) -> LocalBoxFuture<'static, Result<Result<Box<dyn Any>, Box<dyn Any>>, RuntimeFailure>> {
190 let Ok(request) = request.downcast::<C::Request>() else {
191 return Box::pin(futures::future::ready(Err(
192 RuntimeFailure::ProtocolViolation { capability: C::ID },
193 )));
194 };
195 let invocation = self.typed.invoke(operation, *request, context);
196 Box::pin(async move {
197 let result = invocation.await?;
198 Ok(result
199 .map(|response| Box::new(response) as Box<dyn Any>)
200 .map_err(|error| Box::new(error) as Box<dyn Any>))
201 })
202 }
203}
204
205fn invoke_cross_lane<C>(
206 provider_lane: LaneRoute,
207 epoch: Instant,
208 operation: &'static str,
209 request: C::Request,
210 context: InvocationContext,
211) -> NativeRequestFuture<C>
212where
213 C: RequestCapability,
214 C::Request: Send,
215 C::Response: Send,
216 C::DomainError: Send,
217{
218 Box::pin(async move {
219 let provider_lane = provider_lane.upgrade().ok_or_else(lane_unavailable::<C>)?;
220 let caller_instance = context
221 .caller_instance()
222 .ok_or_else(|| RuntimeFailure::InvalidResolvedPlan {
223 detail: format!("cross-lane invocation of `{}` has no planned caller", C::ID),
224 })?
225 .to_owned();
226 let cancellation = context.cancellation();
227 let deadline = context.deadline();
228 let request_id = context.request_id();
229 let (transferred, cancellation_signal) = TransferredInvocationContext::capture(&context);
230 let (completed, completion) = futures::channel::oneshot::channel();
231 let command: LaneTask = Box::new(move |lane| {
232 tokio::task::spawn_local(async move {
233 let local_cancellation = CancellationToken::new();
234 let (context, mut transferred_cancellation) =
235 transferred.restore(local_cancellation.clone());
236 if *transferred_cancellation.borrow() {
237 local_cancellation.cancel();
238 }
239 let handle = match lane.request_handle::<C>(&caller_instance) {
240 Ok(handle) => handle,
241 Err(error) => {
242 let _ = completed.send(Err(error));
243 return;
244 }
245 };
246 let invocation = handle.invoke_with_context(operation, context, request);
247 tokio::pin!(invocation);
248 let result = if local_cancellation.is_cancelled() {
249 invocation.await
250 } else {
251 tokio::select! {
252 result = &mut invocation => result,
253 () = wait_for_cancellation(&mut transferred_cancellation) => {
254 local_cancellation.cancel();
255 invocation.await
256 }
257 }
258 };
259 let _ = completed.send(result);
260 });
261 });
262
263 let send = provider_lane.send(command);
264 tokio::pin!(send);
265 let cancelled = cancellation.cancelled();
266 tokio::pin!(cancelled);
267 if let Some(deadline) = deadline {
268 let sleep = tokio::time::sleep_until((epoch + deadline).into());
269 tokio::pin!(sleep);
270 tokio::select! {
271 result = &mut send => result.map_err(|_| lane_unavailable::<C>())?,
272 () = &mut cancelled => {
273 cancellation_signal.send_replace(true);
274 return Err(RuntimeFailure::Cancelled { request_id });
275 }
276 () = &mut sleep => {
277 cancellation_signal.send_replace(true);
278 return Err(RuntimeFailure::DeadlineExceeded { request_id });
279 }
280 }
281 tokio::select! {
282 biased;
283 result = completion => result.map_err(|_| lane_unavailable::<C>())?,
284 () = &mut cancelled => {
285 cancellation_signal.send_replace(true);
286 Err(RuntimeFailure::Cancelled { request_id })
287 }
288 () = &mut sleep => {
289 cancellation_signal.send_replace(true);
290 Err(RuntimeFailure::DeadlineExceeded { request_id })
291 }
292 }
293 } else {
294 tokio::select! {
295 result = &mut send => result.map_err(|_| lane_unavailable::<C>())?,
296 () = &mut cancelled => {
297 cancellation_signal.send_replace(true);
298 return Err(RuntimeFailure::Cancelled { request_id });
299 }
300 }
301 tokio::select! {
302 biased;
303 result = completion => result.map_err(|_| lane_unavailable::<C>())?,
304 () = &mut cancelled => {
305 cancellation_signal.send_replace(true);
306 Err(RuntimeFailure::Cancelled { request_id })
307 }
308 }
309 }
310 })
311}
312
313fn lane_unavailable<C: RequestCapability>() -> RuntimeFailure {
314 RuntimeFailure::Internal {
315 detail: format!("provider lane for `{}` is unavailable", C::ID),
316 }
317}
318
319#[derive(Debug)]
320struct TransferredInvocationContext {
321 request_id: u64,
322 deadline: Option<std::time::Duration>,
323 cancellation: watch::Receiver<bool>,
324 extensions: Vec<lenso_kernel::InvocationExtension>,
325 sealed_extensions: Vec<lenso_kernel::SealedInvocationExtension>,
326}
327
328impl TransferredInvocationContext {
329 fn capture(context: &InvocationContext) -> (Self, watch::Sender<bool>) {
330 let (cancellation_signal, cancellation) = watch::channel(context.is_cancelled());
331 (
332 Self {
333 request_id: context.request_id(),
334 deadline: context.deadline(),
335 cancellation,
336 extensions: context.extensions().cloned().collect(),
337 sealed_extensions: context.sealed_extensions().cloned().collect(),
338 },
339 cancellation_signal,
340 )
341 }
342
343 fn restore(
344 self,
345 cancellation: CancellationToken,
346 ) -> (InvocationContext, watch::Receiver<bool>) {
347 let mut context = InvocationContext::new(self.request_id, self.deadline, cancellation);
348 for extension in self.extensions {
349 context = context
350 .with_extension(extension.key(), extension.value().to_vec())
351 .expect("captured ordinary Invocation Context extension remains valid");
352 }
353 for extension in self.sealed_extensions {
354 context = context
355 .with_sealed_extension(extension)
356 .expect("captured sealed Invocation Context extension remains valid");
357 }
358 (context, self.cancellation)
359 }
360}
361
362async fn wait_for_cancellation(cancellation: &mut watch::Receiver<bool>) {
363 loop {
364 if *cancellation.borrow_and_update() {
365 return;
366 }
367 if cancellation.changed().await.is_err() {
368 return;
369 }
370 }
371}