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