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