1use super::{
2 AbortHandle, CancellationToken, Cell, Context, Duration, Either, Future, FutureExt,
3 InvocationContext, LocalBoxFuture, NativeAppRuntime, Pin, Poll, Rc, RefCell,
4 RequestAdmissionPlan, RuntimeFailure, SpawnError, VecDeque, begin_module_supervision, oneshot,
5 pending, schedule_module_supervision, select,
6};
7
8pub type LocalTask = Pin<Box<dyn Future<Output = ()> + 'static>>;
10
11#[derive(Clone, Copy, Debug, Eq, PartialEq)]
13pub enum TaskOutcome {
14 Completed,
16 Cancelled,
18 Failed,
20}
21
22#[derive(Debug)]
24pub struct DriverTask {
25 pub(super) abort: AbortHandle,
26 pub(super) completion: oneshot::Receiver<TaskOutcome>,
27}
28
29impl DriverTask {
30 pub fn new(abort: AbortHandle, completion: oneshot::Receiver<TaskOutcome>) -> Self {
32 Self { abort, completion }
33 }
34
35 pub fn cancel(&self) {
37 self.abort.abort();
38 }
39
40 pub(super) fn abort_handle(&self) -> AbortHandle {
41 self.abort.clone()
42 }
43}
44
45impl Future for DriverTask {
46 type Output = TaskOutcome;
47
48 fn poll(mut self: Pin<&mut Self>, context: &mut Context<'_>) -> Poll<Self::Output> {
49 Pin::new(&mut self.completion)
50 .poll(context)
51 .map(|outcome| outcome.unwrap_or(TaskOutcome::Failed))
52 }
53}
54
55pub trait RuntimeDriver: Clone + 'static {
57 fn now(&self) -> Duration;
59
60 fn sleep_until(&self, deadline: Duration) -> LocalBoxFuture<'static, ()>;
62
63 fn yield_now(&self) -> LocalBoxFuture<'static, ()>;
65
66 fn jitter(&self, _maximum: Duration) -> Duration {
68 Duration::ZERO
69 }
70
71 fn spawn_local(&self, task: LocalTask) -> Result<DriverTask, SpawnError>;
73
74 fn shutdown_requested(&self) -> bool;
76}
77
78#[derive(Clone)]
79pub(super) struct DriverControl {
80 pub(super) now: Rc<dyn Fn() -> Duration>,
81 pub(super) sleep_until: Rc<dyn Fn(Duration) -> LocalBoxFuture<'static, ()>>,
82 pub(super) yield_now: Rc<dyn Fn() -> LocalBoxFuture<'static, ()>>,
83 pub(super) jitter: Rc<dyn Fn(Duration) -> Duration>,
84 pub(super) spawn_local: Rc<dyn Fn(LocalTask) -> Result<DriverTask, SpawnError>>,
85}
86
87impl std::fmt::Debug for DriverControl {
88 fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
89 formatter
90 .debug_struct("DriverControl")
91 .finish_non_exhaustive()
92 }
93}
94
95impl DriverControl {
96 pub(super) fn new<D: RuntimeDriver>(driver: &D) -> Self {
97 let now_driver = driver.clone();
98 let sleep_driver = driver.clone();
99 let yield_driver = driver.clone();
100 let jitter_driver = driver.clone();
101 let spawn_driver = driver.clone();
102 Self {
103 now: Rc::new(move || now_driver.now()),
104 sleep_until: Rc::new(move |deadline| sleep_driver.sleep_until(deadline)),
105 yield_now: Rc::new(move || yield_driver.yield_now()),
106 jitter: Rc::new(move |maximum| jitter_driver.jitter(maximum)),
107 spawn_local: Rc::new(move |task| spawn_driver.spawn_local(task)),
108 }
109 }
110}
111
112pub(super) async fn wait_until<F: Future>(
113 driver: &DriverControl,
114 deadline: Duration,
115 future: F,
116) -> Option<F::Output> {
117 let work = future.fuse();
118 let timer = (driver.sleep_until)(deadline).fuse();
119 futures::pin_mut!(work, timer);
120 match select(work, timer).await {
121 Either::Left((output, _)) => Some(output),
122 Either::Right(((), _)) => None,
123 }
124}
125
126#[derive(Clone, Debug)]
128pub(super) struct RequestAdmission {
129 pub(super) limits: RequestAdmissionPlan,
130 pub(super) state: Rc<RequestAdmissionState>,
131}
132
133#[derive(Debug, Default)]
134pub(super) struct RequestAdmissionState {
135 pub(super) active: Cell<usize>,
136 pub(super) queued: Cell<usize>,
137 pub(super) waiters: RefCell<VecDeque<Rc<QueueWaiter>>>,
138}
139
140#[derive(Clone, Copy, Debug, Eq, PartialEq)]
141pub(super) enum QueueWaiterStatus {
142 Waiting,
143 Woken,
144 Acquired,
145 Cancelled,
146}
147
148#[derive(Debug)]
149pub(super) struct QueueWaiter {
150 pub(super) status: Cell<QueueWaiterStatus>,
151 pub(super) wakeup: RefCell<Option<oneshot::Sender<()>>>,
152}
153
154impl RequestAdmission {
155 pub(super) fn new(limits: RequestAdmissionPlan) -> Self {
156 Self {
157 limits,
158 state: Rc::new(RequestAdmissionState::default()),
159 }
160 }
161
162 pub(super) fn queue_depth(&self) -> usize {
163 self.state.queued.get()
164 }
165
166 pub(crate) fn try_acquire(
167 &self,
168 capability: &'static str,
169 operation: &str,
170 context: &InvocationContext,
171 driver: &DriverControl,
172 ) -> Result<RequestPermit, RuntimeFailure> {
173 ensure_context_active(driver, context)?;
174 if self.state.active.get() < self.limits.max_concurrency() {
175 self.state.active.set(self.state.active.get() + 1);
176 return Ok(RequestPermit {
177 state: self.state.clone(),
178 });
179 }
180 Err(RuntimeFailure::ResourceExhausted {
181 capability,
182 operation: operation.to_owned(),
183 })
184 }
185
186 pub(super) fn acquire(
187 &self,
188 capability: &'static str,
189 operation: &str,
190 context: InvocationContext,
191 driver: DriverControl,
192 ) -> LocalBoxFuture<'static, Result<RequestPermit, RuntimeFailure>> {
193 if let Ok(permit) = self.try_acquire(capability, operation, &context, &driver) {
194 return Box::pin(futures::future::ready(Ok(permit)));
195 }
196 if let Err(error) = ensure_context_active(&driver, &context) {
197 return Box::pin(futures::future::ready(Err(error)));
198 }
199
200 if self.state.queued.get() >= self.limits.queue_capacity() {
201 return Box::pin(futures::future::ready(Err(
202 RuntimeFailure::ResourceExhausted {
203 capability,
204 operation: operation.to_owned(),
205 },
206 )));
207 }
208
209 let (wakeup, waiter) = oneshot::channel();
210 let waiter_state = Rc::new(QueueWaiter {
211 status: Cell::new(QueueWaiterStatus::Waiting),
212 wakeup: RefCell::new(Some(wakeup)),
213 });
214 self.state.queued.set(self.state.queued.get() + 1);
215 self.state
216 .waiters
217 .borrow_mut()
218 .push_back(waiter_state.clone());
219 let queued = QueuedAdmission {
220 state: self.state.clone(),
221 waiter_state,
222 waiter,
223 };
224 Box::pin(async move { queued.wait(&driver, &context).await })
225 }
226}
227
228#[derive(Debug)]
229pub(super) struct QueuedAdmission {
230 pub(super) state: Rc<RequestAdmissionState>,
231 pub(super) waiter_state: Rc<QueueWaiter>,
232 pub(super) waiter: oneshot::Receiver<()>,
233}
234
235impl QueuedAdmission {
236 pub(super) async fn wait(
237 mut self,
238 driver: &DriverControl,
239 context: &InvocationContext,
240 ) -> Result<RequestPermit, RuntimeFailure> {
241 let result = await_with_context(driver, context, &mut self.waiter).await;
242 match result {
243 Ok(Ok(())) => {
244 if self.waiter_state.status.get() == QueueWaiterStatus::Woken {
245 self.waiter_state.status.set(QueueWaiterStatus::Acquired);
246 self.state.queued.set(self.state.queued.get() - 1);
247 Ok(RequestPermit {
248 state: self.state.clone(),
249 })
250 } else {
251 Err(RuntimeFailure::Cancelled {
252 request_id: context.request_id(),
253 })
254 }
255 }
256 Ok(Err(_)) => Err(RuntimeFailure::Cancelled {
257 request_id: context.request_id(),
258 }),
259 Err(error) => Err(error),
260 }
261 }
262}
263
264impl Drop for QueuedAdmission {
265 fn drop(&mut self) {
266 let previous = self
267 .waiter_state
268 .status
269 .replace(QueueWaiterStatus::Cancelled);
270 match previous {
271 QueueWaiterStatus::Waiting => {
272 self.state.queued.set(self.state.queued.get() - 1);
273 }
274 QueueWaiterStatus::Woken => {
275 self.state.queued.set(self.state.queued.get() - 1);
276 self.state.active.set(self.state.active.get() - 1);
277 wake_next(&self.state);
278 }
279 QueueWaiterStatus::Acquired | QueueWaiterStatus::Cancelled => {}
280 }
281 self.state
282 .waiters
283 .borrow_mut()
284 .retain(|waiter| !Rc::ptr_eq(waiter, &self.waiter_state));
285 }
286}
287
288#[derive(Debug)]
289pub(super) struct RequestPermit {
290 pub(super) state: Rc<RequestAdmissionState>,
291}
292
293impl Drop for RequestPermit {
294 fn drop(&mut self) {
295 self.state.active.set(self.state.active.get() - 1);
296 wake_next(&self.state);
297 }
298}
299
300pub(super) fn wake_next(state: &Rc<RequestAdmissionState>) {
301 loop {
302 let Some(waiter) = state.waiters.borrow_mut().pop_front() else {
303 return;
304 };
305 if waiter.status.replace(QueueWaiterStatus::Woken) != QueueWaiterStatus::Waiting {
306 continue;
307 }
308 state.active.set(state.active.get() + 1);
309 let sent = waiter
310 .wakeup
311 .borrow_mut()
312 .take()
313 .is_some_and(|wakeup| wakeup.send(()).is_ok());
314 if sent {
315 return;
316 }
317 waiter.status.set(QueueWaiterStatus::Cancelled);
318 state.active.set(state.active.get() - 1);
319 state.queued.set(state.queued.get() - 1);
320 }
321}
322
323pub(super) async fn await_with_context<F: Future>(
324 driver: &DriverControl,
325 context: &InvocationContext,
326 future: F,
327) -> Result<F::Output, RuntimeFailure> {
328 ensure_context_active(driver, context)?;
329
330 let work = future.fuse();
331 let cancellation = context.cancellation.cancelled().fuse();
332 let deadline: LocalBoxFuture<'static, ()> = context.deadline().map_or_else(
333 || Box::pin(pending::<()>()) as LocalBoxFuture<'static, ()>,
334 |deadline| (driver.sleep_until)(deadline),
335 );
336 let deadline = deadline.fuse();
337 futures::pin_mut!(work, cancellation, deadline);
338
339 match select(select(work, cancellation), deadline).await {
340 Either::Left((Either::Left((output, _)), _)) => Ok(output),
341 Either::Left((Either::Right(((), _)), _)) => Err(RuntimeFailure::Cancelled {
342 request_id: context.request_id(),
343 }),
344 Either::Right(((), _)) => Err(RuntimeFailure::DeadlineExceeded {
345 request_id: context.request_id(),
346 }),
347 }
348}
349
350pub(super) async fn await_with_generation_context<F: Future>(
351 driver: &DriverControl,
352 context: &InvocationContext,
353 generation_cancellation: CancellationToken,
354 capability: &'static str,
355 future: F,
356) -> Result<F::Output, RuntimeFailure> {
357 ensure_context_active(driver, context)?;
358 if generation_cancellation.is_cancelled() {
359 return Err(RuntimeFailure::Unavailable { capability });
360 }
361
362 let work = future.fuse();
363 let cancellation = context.cancellation.cancelled().fuse();
364 let generation_cancellation = generation_cancellation.cancelled().fuse();
365 let deadline: LocalBoxFuture<'static, ()> = context.deadline().map_or_else(
366 || Box::pin(pending::<()>()) as LocalBoxFuture<'static, ()>,
367 |deadline| (driver.sleep_until)(deadline),
368 );
369 let deadline = deadline.fuse();
370 futures::pin_mut!(work, cancellation, generation_cancellation, deadline);
371
372 match select(
373 select(select(work, cancellation), generation_cancellation),
374 deadline,
375 )
376 .await
377 {
378 Either::Left((Either::Left((Either::Left((output, _)), _)), _)) => Ok(output),
379 Either::Left((Either::Left((Either::Right(((), _)), _)), _)) => {
380 Err(RuntimeFailure::Cancelled {
381 request_id: context.request_id(),
382 })
383 }
384 Either::Left((Either::Right(((), _)), _)) => {
385 Err(RuntimeFailure::Unavailable { capability })
386 }
387 Either::Right(((), _)) => Err(RuntimeFailure::DeadlineExceeded {
388 request_id: context.request_id(),
389 }),
390 }
391}
392
393pub(super) fn is_module_failure(error: &RuntimeFailure) -> bool {
394 matches!(error, RuntimeFailure::ModuleFailure { .. })
395}
396
397pub(super) fn schedule_module_supervision_after_failure(
398 runtime: &Rc<NativeAppRuntime>,
399 instance_key: &str,
400 error: RuntimeFailure,
401) -> RuntimeFailure {
402 if is_module_failure(&error)
403 && begin_module_supervision(runtime, instance_key).unwrap_or(false)
404 && let Err(schedule_error) = schedule_module_supervision(runtime, instance_key)
405 {
406 return handle_supervision_schedule_failure(runtime, instance_key, schedule_error);
407 }
408 error
409}
410
411pub(super) fn handle_supervision_schedule_failure(
412 runtime: &Rc<NativeAppRuntime>,
413 instance_key: &str,
414 error: RuntimeFailure,
415) -> RuntimeFailure {
416 let must_fail = runtime
417 .supervision
418 .borrow()
419 .get(instance_key)
420 .is_some_and(|state| state.criticality.is_critical() || state.required_path);
421 if must_fail {
422 runtime.terminal_failure.replace(Some(error.clone()));
423 runtime.begin_shutdown();
424 }
425 error
426}
427
428pub(super) fn ensure_context_active(
429 driver: &DriverControl,
430 context: &InvocationContext,
431) -> Result<(), RuntimeFailure> {
432 if context.is_cancelled() {
433 return Err(RuntimeFailure::Cancelled {
434 request_id: context.request_id(),
435 });
436 }
437 if context.is_expired((driver.now)()) {
438 return Err(RuntimeFailure::DeadlineExceeded {
439 request_id: context.request_id(),
440 });
441 }
442 Ok(())
443}
444
445#[derive(Clone, Debug, Eq, PartialEq)]
447pub enum ShutdownOutcome {
448 Clean,
450 RuntimeFailure { error: RuntimeFailure },
452 Timeout,
454}
455
456#[derive(Clone, Debug, Eq, PartialEq)]
458pub enum TerminalOutcome {
459 CleanShutdown,
461 StartupFailure { error: RuntimeFailure },
463 RuntimeFailure { error: RuntimeFailure },
465 RuntimeFailureDuringShutdown {
467 error: RuntimeFailure,
468 cleanup_error: RuntimeFailure,
469 },
470 RuntimeFailureWithShutdownTimeout { error: RuntimeFailure },
472 ShutdownTimeout,
474}