1use super::{
2 AbortHandle, AssertUnwindSafe, Cell, Context, DriverControl, DriverTask, Duration, Future,
3 FutureExt, InvocationContext, LocalBoxFuture, LocalTask, Pin, PluginDependencies,
4 PluginLifecyclePhase, Poll, Rc, RefCell, RuntimeDriver, RuntimeFailure, SpawnError,
5 TaskOutcome, oneshot, select, wait_until,
6};
7
8#[derive(Clone, Debug)]
10pub struct AppReadyGate {
11 pub(super) state: Rc<AppReadyState>,
12}
13
14#[derive(Debug)]
15pub(super) struct AppReadyState {
16 pub(super) open: Cell<bool>,
17 pub(super) waiters: RefCell<Vec<oneshot::Sender<()>>>,
18}
19
20impl AppReadyGate {
21 pub fn new() -> Self {
23 Self {
24 state: Rc::new(AppReadyState {
25 open: Cell::new(false),
26 waiters: RefCell::new(Vec::new()),
27 }),
28 }
29 }
30
31 pub fn is_open(&self) -> bool {
33 self.state.open.get()
34 }
35
36 pub fn wait(&self) -> LocalBoxFuture<'static, ()> {
38 if self.is_open() {
39 return Box::pin(futures::future::ready(()));
40 }
41
42 let (wakeup, waiter) = oneshot::channel();
43 self.state.waiters.borrow_mut().push(wakeup);
44 Box::pin(async move {
45 let _ = waiter.await;
46 })
47 }
48
49 pub(super) fn open(&self) {
50 if self.state.open.replace(true) {
51 return;
52 }
53 for waiter in self.state.waiters.borrow_mut().drain(..) {
54 let _ = waiter.send(());
55 }
56 }
57}
58
59impl Default for AppReadyGate {
60 fn default() -> Self {
61 Self::new()
62 }
63}
64
65#[derive(Clone, Debug)]
67pub struct AppAdmission {
68 pub(super) state: Rc<AppAdmissionState>,
69}
70
71#[derive(Debug)]
72pub(super) struct AppAdmissionState {
73 pub(super) open: Cell<bool>,
74 pub(super) close_signalled: Cell<bool>,
75 pub(super) close_waiters: RefCell<Vec<oneshot::Sender<()>>>,
76}
77
78impl AppAdmission {
79 pub(super) fn new() -> Self {
80 Self {
81 state: Rc::new(AppAdmissionState {
82 open: Cell::new(false),
83 close_signalled: Cell::new(false),
84 close_waiters: RefCell::new(Vec::new()),
85 }),
86 }
87 }
88
89 pub fn is_open(&self) -> bool {
91 self.state.open.get()
92 }
93
94 pub fn is_closed(&self) -> bool {
96 !self.is_open()
97 }
98
99 pub fn wait_closed(&self) -> LocalBoxFuture<'static, ()> {
104 if self.state.close_signalled.get() {
105 return Box::pin(futures::future::ready(()));
106 }
107 let (wakeup, waiter) = oneshot::channel();
108 self.state.close_waiters.borrow_mut().push(wakeup);
109 Box::pin(async move {
110 let _ = waiter.await;
111 })
112 }
113
114 pub(super) fn open(&self) {
115 self.state.open.set(true);
116 }
117
118 pub(super) fn close(&self) {
119 self.state.open.set(false);
120 self.state.close_signalled.set(true);
121 for waiter in self.state.close_waiters.borrow_mut().drain(..) {
122 let _ = waiter.send(());
123 }
124 }
125}
126
127#[derive(Clone, Debug)]
129pub struct CancellationToken {
130 pub(super) state: Rc<CancellationState>,
131}
132
133#[derive(Debug)]
134pub(super) struct CancellationState {
135 pub(super) cancelled: Cell<bool>,
136 pub(super) parent: Option<CancellationToken>,
137 pub(super) next_waiter_id: Cell<usize>,
138 pub(super) waiters: RefCell<Vec<(usize, oneshot::Sender<()>)>>,
139}
140
141impl CancellationToken {
142 pub fn new() -> Self {
144 Self {
145 state: Rc::new(CancellationState {
146 cancelled: Cell::new(false),
147 parent: None,
148 next_waiter_id: Cell::new(0),
149 waiters: RefCell::new(Vec::new()),
150 }),
151 }
152 }
153
154 pub fn is_cancelled(&self) -> bool {
156 self.state.cancelled.get()
157 || self
158 .state
159 .parent
160 .as_ref()
161 .is_some_and(CancellationToken::is_cancelled)
162 }
163
164 #[must_use]
167 pub fn child(&self) -> Self {
168 Self {
169 state: Rc::new(CancellationState {
170 cancelled: Cell::new(false),
171 parent: Some(self.clone()),
172 next_waiter_id: Cell::new(0),
173 waiters: RefCell::new(Vec::new()),
174 }),
175 }
176 }
177
178 pub fn cancelled(&self) -> LocalBoxFuture<'static, ()> {
180 if self.is_cancelled() {
181 return Box::pin(futures::future::ready(()));
182 }
183 let (wakeup, waiter) = oneshot::channel();
184 let waiter_id = self.state.next_waiter_id.get();
185 self.state.next_waiter_id.set(waiter_id.saturating_add(1));
186 self.state.waiters.borrow_mut().push((waiter_id, wakeup));
187 let own = Box::pin(CancellationWaiter {
188 state: self.state.clone(),
189 waiter_id,
190 receiver: waiter,
191 registered: true,
192 });
193 let Some(parent) = self.state.parent.clone() else {
194 return own;
195 };
196 Box::pin(async move {
197 let _ = select(own, parent.cancelled()).await;
198 })
199 }
200
201 pub fn cancel(&self) {
203 if self.state.cancelled.replace(true) {
204 return;
205 }
206 for (_, waiter) in self.state.waiters.borrow_mut().drain(..) {
207 let _ = waiter.send(());
208 }
209 }
210}
211
212#[derive(Debug)]
213pub(super) struct CancellationWaiter {
214 pub(super) state: Rc<CancellationState>,
215 pub(super) waiter_id: usize,
216 pub(super) receiver: oneshot::Receiver<()>,
217 pub(super) registered: bool,
218}
219
220impl Future for CancellationWaiter {
221 type Output = ();
222
223 fn poll(mut self: Pin<&mut Self>, context: &mut Context<'_>) -> Poll<Self::Output> {
224 match Pin::new(&mut self.receiver).poll(context) {
225 Poll::Ready(_) => {
226 self.registered = false;
227 Poll::Ready(())
228 }
229 Poll::Pending => Poll::Pending,
230 }
231 }
232}
233
234impl Drop for CancellationWaiter {
235 fn drop(&mut self) {
236 if !self.registered {
237 return;
238 }
239 self.state
240 .waiters
241 .borrow_mut()
242 .retain(|(waiter_id, _)| *waiter_id != self.waiter_id);
243 }
244}
245
246impl Default for CancellationToken {
247 fn default() -> Self {
248 Self::new()
249 }
250}
251
252pub type ResourceFuture = LocalBoxFuture<'static, Result<(), RuntimeFailure>>;
254
255pub trait ManagedResource: std::fmt::Debug + 'static {
257 fn release(&self) -> ResourceFuture;
259}
260
261#[derive(Clone, Copy, Debug, Eq, PartialEq)]
263pub enum ResourceRegistrationError {
264 ScopeClosed,
266}
267
268pub(super) struct ManagedResourceEntry {
269 pub(super) resource: Rc<dyn ManagedResource>,
270 pub(super) release: RefCell<ManagedResourceRelease>,
271}
272
273pub(super) enum ManagedResourceRelease {
274 Pending,
275 Running(ResourceFuture),
276 Complete(Result<(), RuntimeFailure>),
277}
278
279impl std::fmt::Debug for ManagedResourceEntry {
280 fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
281 let state = match &*self.release.borrow() {
282 ManagedResourceRelease::Pending => "pending",
283 ManagedResourceRelease::Running(_) => "running",
284 ManagedResourceRelease::Complete(Ok(())) => "released",
285 ManagedResourceRelease::Complete(Err(_)) => "failed",
286 };
287 formatter
288 .debug_struct("ManagedResourceEntry")
289 .field("release", &state)
290 .finish_non_exhaustive()
291 }
292}
293
294#[derive(Clone, Debug)]
296pub struct ManagedResourceHandle {
297 pub(super) entry: Rc<ManagedResourceEntry>,
298}
299
300impl ManagedResourceHandle {
301 pub fn is_released(&self) -> bool {
303 matches!(
304 &*self.entry.release.borrow(),
305 ManagedResourceRelease::Complete(_)
306 )
307 }
308
309 pub async fn release(&self) -> Result<(), RuntimeFailure> {
311 ManagedResourceReleaseOperation {
312 entry: self.entry.clone(),
313 }
314 .await
315 }
316}
317
318pub(super) struct ManagedResourceReleaseOperation {
319 pub(super) entry: Rc<ManagedResourceEntry>,
320}
321
322impl Future for ManagedResourceReleaseOperation {
323 type Output = Result<(), RuntimeFailure>;
324
325 fn poll(self: Pin<&mut Self>, context: &mut Context<'_>) -> Poll<Self::Output> {
326 let mut release = self.entry.release.borrow_mut();
327 if matches!(*release, ManagedResourceRelease::Pending) {
328 *release = ManagedResourceRelease::Running(self.entry.resource.release());
329 }
330 match &mut *release {
331 ManagedResourceRelease::Running(future) => match future.as_mut().poll(context) {
332 Poll::Ready(result) => {
333 *release = ManagedResourceRelease::Complete(result.clone());
334 Poll::Ready(result)
335 }
336 Poll::Pending => Poll::Pending,
337 },
338 ManagedResourceRelease::Complete(result) => Poll::Ready(result.clone()),
339 ManagedResourceRelease::Pending => unreachable!("pending release was started"),
340 }
341 }
342}
343
344#[derive(Clone)]
346pub struct ManagedResourceScope {
347 pub(super) state: Rc<ManagedResourceScopeState>,
348}
349
350#[derive(Debug, Default)]
351pub(super) struct ManagedResourceScopeState {
352 pub(super) resources: RefCell<Vec<ManagedResourceHandle>>,
353 pub(super) closed: Cell<bool>,
354}
355
356impl std::fmt::Debug for ManagedResourceScope {
357 fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
358 formatter
359 .debug_struct("ManagedResourceScope")
360 .field("resource_count", &self.resource_count())
361 .finish()
362 }
363}
364
365impl ManagedResourceScope {
366 pub(super) fn new() -> Self {
367 Self {
368 state: Rc::new(ManagedResourceScopeState::default()),
369 }
370 }
371
372 pub fn register(
374 &self,
375 resource: impl ManagedResource,
376 ) -> Result<ManagedResourceHandle, ResourceRegistrationError> {
377 if self.state.closed.get() {
378 return Err(ResourceRegistrationError::ScopeClosed);
379 }
380 let handle = ManagedResourceHandle {
381 entry: Rc::new(ManagedResourceEntry {
382 resource: Rc::new(resource),
383 release: RefCell::new(ManagedResourceRelease::Pending),
384 }),
385 };
386 self.state.resources.borrow_mut().push(handle.clone());
387 Ok(handle)
388 }
389
390 pub fn resource_count(&self) -> usize {
392 self.state
393 .resources
394 .borrow()
395 .iter()
396 .filter(|resource| !resource.is_released())
397 .count()
398 }
399
400 pub(super) fn close(&self) {
401 self.state.closed.set(true);
402 }
403
404 pub(super) async fn release_all(&self) -> Option<RuntimeFailure> {
405 let resources = std::mem::take(&mut *self.state.resources.borrow_mut());
406 let mut first_error = None;
407 for resource in resources {
408 if let Err(error) = resource.release().await
409 && first_error.is_none()
410 {
411 first_error = Some(error);
412 }
413 }
414 first_error
415 }
416
417 pub(super) async fn release_all_until(
418 &self,
419 driver: &DriverControl,
420 deadline: Duration,
421 ) -> Result<Option<RuntimeFailure>, ()> {
422 let resources = std::mem::take(&mut *self.state.resources.borrow_mut());
423 let mut first_error = None;
424 for (index, resource) in resources.iter().enumerate() {
425 match wait_until(driver, deadline, resource.release()).await {
426 Some(Ok(())) => {}
427 Some(Err(error)) => {
428 if first_error.is_none() {
429 first_error = Some(error);
430 }
431 }
432 None => {
433 self.state
434 .resources
435 .borrow_mut()
436 .extend(resources.into_iter().skip(index));
437 return Err(());
438 }
439 }
440 }
441 Ok(first_error)
442 }
443}
444
445#[derive(Clone, Debug)]
447pub struct ManagedTask {
448 pub(super) task: Rc<RefCell<Option<DriverTask>>>,
449 pub(super) abort: AbortHandle,
450 pub(super) failed: Rc<Cell<bool>>,
451 pub(super) completed: Rc<Cell<bool>>,
452}
453
454impl ManagedTask {
455 pub(super) fn from_driver_task(task: DriverTask) -> Self {
456 Self {
457 abort: task.abort_handle(),
458 task: Rc::new(RefCell::new(Some(task))),
459 failed: Rc::new(Cell::new(false)),
460 completed: Rc::new(Cell::new(false)),
461 }
462 }
463
464 pub fn cancel(&self) {
466 self.abort.abort();
467 }
468
469 pub(super) async fn join(&self) -> TaskOutcome {
470 let outcome = std::future::poll_fn(|context| {
471 let mut slot = self.task.borrow_mut();
472 let Some(task) = slot.as_mut() else {
473 return Poll::Ready(TaskOutcome::Completed);
474 };
475 match Pin::new(task).poll(context) {
476 Poll::Ready(outcome) => {
477 slot.take();
478 Poll::Ready(outcome)
479 }
480 Poll::Pending => Poll::Pending,
481 }
482 })
483 .await;
484 if self.failed.get() {
485 TaskOutcome::Failed
486 } else {
487 outcome
488 }
489 }
490}
491
492#[derive(Debug)]
494pub enum ManagedTaskError {
495 ScopeClosed,
497 Driver(SpawnError),
499}
500
501impl From<SpawnError> for ManagedTaskError {
502 fn from(error: SpawnError) -> Self {
503 Self::Driver(error)
504 }
505}
506
507#[derive(Clone)]
509pub struct ManagedTaskScope {
510 pub(super) spawn: Rc<dyn Fn(LocalTask) -> Result<DriverTask, SpawnError>>,
511 pub(super) state: Rc<ManagedTaskScopeState>,
512}
513
514pub(super) struct ManagedTaskScopeState {
515 pub(super) tasks: RefCell<Vec<ManagedTask>>,
516 pub(super) closed: Cell<bool>,
517 pub(super) cancellation: CancellationToken,
518 pub(super) failure_handler: RefCell<Option<Rc<dyn Fn()>>>,
519 pub(super) unreported_failure: Cell<bool>,
520}
521
522impl Default for ManagedTaskScopeState {
523 fn default() -> Self {
524 Self {
525 tasks: RefCell::new(Vec::new()),
526 closed: Cell::new(false),
527 cancellation: CancellationToken::new(),
528 failure_handler: RefCell::new(None),
529 unreported_failure: Cell::new(false),
530 }
531 }
532}
533
534impl std::fmt::Debug for ManagedTaskScopeState {
535 fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
536 formatter
537 .debug_struct("ManagedTaskScopeState")
538 .field("task_count", &self.tasks.borrow().len())
539 .field("closed", &self.closed.get())
540 .field("unreported_failure", &self.unreported_failure.get())
541 .finish_non_exhaustive()
542 }
543}
544
545impl std::fmt::Debug for ManagedTaskScope {
546 fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
547 formatter
548 .debug_struct("ManagedTaskScope")
549 .field("task_count", &self.task_count())
550 .finish()
551 }
552}
553
554impl ManagedTaskScope {
555 pub(super) fn new_with_cancellation<D: RuntimeDriver>(
556 driver: &D,
557 cancellation: CancellationToken,
558 ) -> Self {
559 let spawner = driver.clone();
560 Self {
561 spawn: Rc::new(move |task| spawner.spawn_local(task)),
562 state: Rc::new(ManagedTaskScopeState {
563 cancellation,
564 ..ManagedTaskScopeState::default()
565 }),
566 }
567 }
568
569 pub(super) fn new_from_driver_control(driver: &DriverControl) -> Self {
570 let spawn = driver.spawn_local.clone();
571 Self {
572 spawn,
573 state: Rc::new(ManagedTaskScopeState::default()),
574 }
575 }
576
577 pub fn spawn_local(&self, task: LocalTask) -> Result<ManagedTask, ManagedTaskError> {
579 if self.state.closed.get() {
580 return Err(ManagedTaskError::ScopeClosed);
581 }
582 let failed = Rc::new(Cell::new(false));
583 let task_failed = failed.clone();
584 let completed = Rc::new(Cell::new(false));
585 let task_completed = completed.clone();
586 let state = self.state.clone();
587 let monitored = Box::pin(async move {
588 let outcome = AssertUnwindSafe(task).catch_unwind().await;
589 task_completed.set(true);
590 if outcome.is_err() {
591 task_failed.set(true);
592 state.report_failure();
593 }
594 });
595 let driver_task = (self.spawn)(monitored)?;
596 let handle = ManagedTask {
597 failed,
598 completed,
599 ..ManagedTask::from_driver_task(driver_task)
600 };
601 self.state
602 .tasks
603 .borrow_mut()
604 .retain(|task| !task.completed.get());
605 self.state.tasks.borrow_mut().push(handle.clone());
606 Ok(handle)
607 }
608
609 pub fn task_count(&self) -> usize {
611 self.state
612 .tasks
613 .borrow()
614 .iter()
615 .filter(|task| !task.completed.get())
616 .count()
617 }
618
619 pub fn cancellation(&self) -> CancellationToken {
621 self.state.cancellation.clone()
622 }
623
624 pub(super) fn close(&self) {
625 self.state.closed.set(true);
626 self.state.cancellation.cancel();
627 }
628
629 pub(super) fn set_failure_handler(&self, handler: &Rc<dyn Fn()>) {
630 self.state.failure_handler.replace(Some(handler.clone()));
631 if self.state.unreported_failure.replace(false) {
632 handler();
633 }
634 }
635
636 pub(super) fn cancel(&self) {
637 self.state.cancellation.cancel();
638 }
639
640 pub(super) fn abort_all(&self) {
641 for task in self.state.tasks.borrow().iter() {
642 task.cancel();
643 }
644 }
645
646 pub(super) async fn cancel_all(&self) {
647 self.close();
648 let tasks = std::mem::take(&mut *self.state.tasks.borrow_mut());
649 for task in tasks {
650 task.cancel();
651 let _ = task.join().await;
652 }
653 }
654
655 pub(super) async fn drain_until(&self, driver: &DriverControl, deadline: Duration) -> bool {
656 self.cancel();
657 let tasks = std::mem::take(&mut *self.state.tasks.borrow_mut());
658 for (index, task) in tasks.iter().enumerate() {
659 if wait_until(driver, deadline, task.join()).await.is_none() {
660 for pending in tasks.iter().skip(index) {
661 pending.cancel();
662 }
663 self.state
664 .tasks
665 .borrow_mut()
666 .extend(tasks.into_iter().skip(index));
667 return false;
668 }
669 }
670 true
671 }
672}
673
674impl ManagedTaskScopeState {
675 pub(super) fn report_failure(&self) {
676 let handler = self.failure_handler.borrow().clone();
677 if let Some(handler) = handler {
678 handler();
679 } else {
680 self.unreported_failure.set(true);
681 }
682 }
683}
684
685#[derive(Clone, Debug)]
687pub struct PrepareContext {
688 pub(super) instance_key: String,
689 pub(super) entrypoint: String,
690 pub(super) configuration: String,
691 pub(super) dependencies: PluginDependencies,
692 pub(super) resources: ManagedResourceScope,
693 pub(super) cancellation: CancellationToken,
694 pub(super) admission: AppAdmission,
695}
696
697impl PrepareContext {
698 pub fn instance_key(&self) -> &str {
700 &self.instance_key
701 }
702
703 pub fn entrypoint(&self) -> &str {
705 &self.entrypoint
706 }
707
708 pub fn configuration(&self) -> &str {
710 &self.configuration
711 }
712
713 pub const fn phase(&self) -> PluginLifecyclePhase {
715 PluginLifecyclePhase::Prepare
716 }
717
718 pub fn dependencies(&self) -> &PluginDependencies {
720 &self.dependencies
721 }
722
723 pub fn resources(&self) -> &ManagedResourceScope {
725 &self.resources
726 }
727
728 pub fn cancellation(&self) -> CancellationToken {
730 self.cancellation.clone()
731 }
732
733 pub fn admission(&self) -> AppAdmission {
735 self.admission.clone()
736 }
737}
738
739#[derive(Clone, Debug)]
741pub struct ActivateContext {
742 pub(super) instance_key: String,
743 pub(super) dependencies: PluginDependencies,
744 pub(super) ready_gate: AppReadyGate,
745 pub(super) tasks: ManagedTaskScope,
746 pub(super) resources: ManagedResourceScope,
747 pub(super) cancellation: CancellationToken,
748 pub(super) admission: AppAdmission,
749}
750
751impl ActivateContext {
752 pub fn instance_key(&self) -> &str {
754 &self.instance_key
755 }
756
757 pub const fn phase(&self) -> PluginLifecyclePhase {
759 PluginLifecyclePhase::Activate
760 }
761
762 pub fn dependencies(&self) -> &PluginDependencies {
764 &self.dependencies
765 }
766
767 pub fn ready_gate(&self) -> AppReadyGate {
769 self.ready_gate.clone()
770 }
771
772 pub fn readiness(&self) -> ReadinessContext {
774 ReadinessContext {
775 instance_key: self.instance_key.clone(),
776 dependencies: self.dependencies.clone(),
777 ready_gate: self.ready_gate.clone(),
778 tasks: self.tasks.clone(),
779 resources: self.resources.clone(),
780 cancellation: self.cancellation.clone(),
781 admission: self.admission.clone(),
782 }
783 }
784
785 pub fn tasks(&self) -> &ManagedTaskScope {
787 &self.tasks
788 }
789
790 pub fn resources(&self) -> &ManagedResourceScope {
792 &self.resources
793 }
794
795 pub fn cancellation(&self) -> CancellationToken {
797 self.cancellation.clone()
798 }
799
800 pub fn admission(&self) -> AppAdmission {
802 self.admission.clone()
803 }
804}
805
806#[derive(Clone, Debug)]
808pub struct ReadinessContext {
809 pub(super) instance_key: String,
810 pub(super) dependencies: PluginDependencies,
811 pub(super) ready_gate: AppReadyGate,
812 pub(super) tasks: ManagedTaskScope,
813 pub(super) resources: ManagedResourceScope,
814 pub(super) cancellation: CancellationToken,
815 pub(super) admission: AppAdmission,
816}
817
818impl ReadinessContext {
819 pub fn instance_key(&self) -> &str {
821 &self.instance_key
822 }
823
824 pub const fn phase(&self) -> PluginLifecyclePhase {
826 PluginLifecyclePhase::Ready
827 }
828
829 pub fn dependencies(&self) -> &PluginDependencies {
831 &self.dependencies
832 }
833
834 pub fn ready_gate(&self) -> AppReadyGate {
836 self.ready_gate.clone()
837 }
838
839 pub fn wait(&self) -> LocalBoxFuture<'static, ()> {
841 self.ready_gate.wait()
842 }
843
844 pub fn is_open(&self) -> bool {
846 self.ready_gate.is_open()
847 }
848
849 pub fn tasks(&self) -> &ManagedTaskScope {
851 &self.tasks
852 }
853
854 pub fn resources(&self) -> &ManagedResourceScope {
856 &self.resources
857 }
858
859 pub fn cancellation(&self) -> CancellationToken {
861 self.cancellation.clone()
862 }
863
864 pub fn is_accepting(&self) -> bool {
866 self.admission.is_open()
867 }
868
869 pub fn admission(&self) -> AppAdmission {
871 self.admission.clone()
872 }
873}
874
875#[derive(Clone, Copy, Debug, Eq, PartialEq)]
877pub enum DeactivationReason {
878 StartupRollback,
880 Shutdown,
882 SupervisionRestart,
884}
885
886#[derive(Clone, Debug)]
888pub struct DeactivateContext {
889 pub(super) instance_key: String,
890 pub(super) dependencies: PluginDependencies,
891 pub(super) reason: DeactivationReason,
892 pub(super) tasks: ManagedTaskScope,
893 pub(super) resources: ManagedResourceScope,
894 pub(super) cancellation: CancellationToken,
895 pub(super) admission: AppAdmission,
896 pub(super) cleanup: Option<super::cleanup::CleanupBudget>,
897}
898
899impl DeactivateContext {
900 pub fn instance_key(&self) -> &str {
902 &self.instance_key
903 }
904
905 pub const fn phase(&self) -> PluginLifecyclePhase {
907 PluginLifecyclePhase::Deactivate
908 }
909
910 pub fn dependencies(&self) -> &PluginDependencies {
912 &self.dependencies
913 }
914
915 pub const fn reason(&self) -> DeactivationReason {
917 self.reason
918 }
919
920 pub fn tasks(&self) -> &ManagedTaskScope {
922 &self.tasks
923 }
924
925 pub fn resources(&self) -> &ManagedResourceScope {
927 &self.resources
928 }
929
930 pub fn cancellation(&self) -> CancellationToken {
932 self.cleanup.as_ref().map_or_else(
933 || self.cancellation.clone(),
934 super::cleanup::CleanupBudget::cancellation,
935 )
936 }
937
938 pub fn dependency_invocation_context(&self) -> Result<InvocationContext, RuntimeFailure> {
943 self.dependencies.shutdown_invocation_context(
944 self.cleanup
945 .as_ref()
946 .map(super::cleanup::CleanupBudget::deadline),
947 self.cancellation(),
948 )
949 }
950
951 pub fn remaining_budget(&self) -> Option<Duration> {
956 self.cleanup
957 .as_ref()
958 .map(super::cleanup::CleanupBudget::remaining)
959 }
960
961 pub fn admission(&self) -> AppAdmission {
963 self.admission.clone()
964 }
965}
966
967pub type PluginFuture = LocalBoxFuture<'static, Result<(), RuntimeFailure>>;
969
970pub trait PluginLifecycle: std::fmt::Debug + 'static {
972 fn prepare(&self, _context: PrepareContext) -> PluginFuture {
974 Box::pin(futures::future::ready(Ok(())))
975 }
976
977 #[doc(hidden)]
980 fn construct(&self, _context: ActivateContext) -> PluginFuture {
981 Box::pin(futures::future::ready(Ok(())))
982 }
983
984 fn activate(&self, _context: ActivateContext) -> PluginFuture {
986 Box::pin(futures::future::ready(Ok(())))
987 }
988
989 fn deactivate(&self, _context: DeactivateContext) -> PluginFuture {
991 Box::pin(futures::future::ready(Ok(())))
992 }
993}
994
995#[derive(Debug, Default)]
997pub struct NoopPluginLifecycle;
998
999impl PluginLifecycle for NoopPluginLifecycle {}
1000
1001#[cfg(test)]
1002mod tests {
1003 use super::*;
1004
1005 #[test]
1006 fn admission_close_waiter_ignores_the_initial_startup_gate() {
1007 let admission = AppAdmission::new();
1008 let mut waiting = admission.wait_closed();
1009 let mut context = Context::from_waker(futures::task::noop_waker_ref());
1010
1011 assert!(matches!(waiting.as_mut().poll(&mut context), Poll::Pending));
1012 admission.open();
1013 assert!(matches!(waiting.as_mut().poll(&mut context), Poll::Pending));
1014 admission.close();
1015 assert!(matches!(
1016 waiting.as_mut().poll(&mut context),
1017 Poll::Ready(())
1018 ));
1019
1020 let mut late_waiter = admission.wait_closed();
1021 assert!(matches!(
1022 late_waiter.as_mut().poll(&mut context),
1023 Poll::Ready(())
1024 ));
1025 }
1026
1027 #[test]
1028 fn child_cancellation_is_one_way() {
1029 let parent = CancellationToken::new();
1030 let child = parent.child();
1031 child.cancel();
1032 assert!(child.is_cancelled());
1033 assert!(!parent.is_cancelled());
1034
1035 let second_child = parent.child();
1036 let mut waiting = second_child.cancelled();
1037 let mut context = Context::from_waker(futures::task::noop_waker_ref());
1038 assert!(matches!(waiting.as_mut().poll(&mut context), Poll::Pending));
1039 parent.cancel();
1040 assert!(second_child.is_cancelled());
1041 assert!(matches!(
1042 waiting.as_mut().poll(&mut context),
1043 Poll::Ready(())
1044 ));
1045 }
1046}