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