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, 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) next_waiter_id: Cell<usize>,
137 pub(super) waiters: RefCell<Vec<(usize, oneshot::Sender<()>)>>,
138}
139
140impl CancellationToken {
141 pub fn new() -> Self {
143 Self {
144 state: Rc::new(CancellationState {
145 cancelled: Cell::new(false),
146 next_waiter_id: Cell::new(0),
147 waiters: RefCell::new(Vec::new()),
148 }),
149 }
150 }
151
152 pub fn is_cancelled(&self) -> bool {
154 self.state.cancelled.get()
155 }
156
157 pub fn cancelled(&self) -> LocalBoxFuture<'static, ()> {
159 if self.is_cancelled() {
160 return Box::pin(futures::future::ready(()));
161 }
162 let (wakeup, waiter) = oneshot::channel();
163 let waiter_id = self.state.next_waiter_id.get();
164 self.state.next_waiter_id.set(waiter_id.saturating_add(1));
165 self.state.waiters.borrow_mut().push((waiter_id, wakeup));
166 Box::pin(CancellationWaiter {
167 state: self.state.clone(),
168 waiter_id,
169 receiver: waiter,
170 registered: true,
171 })
172 }
173
174 pub fn cancel(&self) {
176 if self.state.cancelled.replace(true) {
177 return;
178 }
179 for (_, waiter) in self.state.waiters.borrow_mut().drain(..) {
180 let _ = waiter.send(());
181 }
182 }
183}
184
185#[derive(Debug)]
186pub(super) struct CancellationWaiter {
187 pub(super) state: Rc<CancellationState>,
188 pub(super) waiter_id: usize,
189 pub(super) receiver: oneshot::Receiver<()>,
190 pub(super) registered: bool,
191}
192
193impl Future for CancellationWaiter {
194 type Output = ();
195
196 fn poll(mut self: Pin<&mut Self>, context: &mut Context<'_>) -> Poll<Self::Output> {
197 match Pin::new(&mut self.receiver).poll(context) {
198 Poll::Ready(_) => {
199 self.registered = false;
200 Poll::Ready(())
201 }
202 Poll::Pending => Poll::Pending,
203 }
204 }
205}
206
207impl Drop for CancellationWaiter {
208 fn drop(&mut self) {
209 if !self.registered {
210 return;
211 }
212 self.state
213 .waiters
214 .borrow_mut()
215 .retain(|(waiter_id, _)| *waiter_id != self.waiter_id);
216 }
217}
218
219impl Default for CancellationToken {
220 fn default() -> Self {
221 Self::new()
222 }
223}
224
225pub type ResourceFuture = LocalBoxFuture<'static, Result<(), RuntimeFailure>>;
227
228pub trait ManagedResource: std::fmt::Debug + 'static {
230 fn release(&self) -> ResourceFuture;
232}
233
234#[derive(Clone, Copy, Debug, Eq, PartialEq)]
236pub enum ResourceRegistrationError {
237 ScopeClosed,
239}
240
241pub(super) struct ManagedResourceEntry {
242 pub(super) resource: Rc<dyn ManagedResource>,
243 pub(super) release: RefCell<ManagedResourceRelease>,
244}
245
246pub(super) enum ManagedResourceRelease {
247 Pending,
248 Running(ResourceFuture),
249 Complete(Result<(), RuntimeFailure>),
250}
251
252impl std::fmt::Debug for ManagedResourceEntry {
253 fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
254 let state = match &*self.release.borrow() {
255 ManagedResourceRelease::Pending => "pending",
256 ManagedResourceRelease::Running(_) => "running",
257 ManagedResourceRelease::Complete(Ok(())) => "released",
258 ManagedResourceRelease::Complete(Err(_)) => "failed",
259 };
260 formatter
261 .debug_struct("ManagedResourceEntry")
262 .field("release", &state)
263 .finish_non_exhaustive()
264 }
265}
266
267#[derive(Clone, Debug)]
269pub struct ManagedResourceHandle {
270 pub(super) entry: Rc<ManagedResourceEntry>,
271}
272
273impl ManagedResourceHandle {
274 pub fn is_released(&self) -> bool {
276 matches!(
277 &*self.entry.release.borrow(),
278 ManagedResourceRelease::Complete(_)
279 )
280 }
281
282 pub async fn release(&self) -> Result<(), RuntimeFailure> {
284 ManagedResourceReleaseOperation {
285 entry: self.entry.clone(),
286 }
287 .await
288 }
289}
290
291pub(super) struct ManagedResourceReleaseOperation {
292 pub(super) entry: Rc<ManagedResourceEntry>,
293}
294
295impl Future for ManagedResourceReleaseOperation {
296 type Output = Result<(), RuntimeFailure>;
297
298 fn poll(self: Pin<&mut Self>, context: &mut Context<'_>) -> Poll<Self::Output> {
299 let mut release = self.entry.release.borrow_mut();
300 if matches!(*release, ManagedResourceRelease::Pending) {
301 *release = ManagedResourceRelease::Running(self.entry.resource.release());
302 }
303 match &mut *release {
304 ManagedResourceRelease::Running(future) => match future.as_mut().poll(context) {
305 Poll::Ready(result) => {
306 *release = ManagedResourceRelease::Complete(result.clone());
307 Poll::Ready(result)
308 }
309 Poll::Pending => Poll::Pending,
310 },
311 ManagedResourceRelease::Complete(result) => Poll::Ready(result.clone()),
312 ManagedResourceRelease::Pending => unreachable!("pending release was started"),
313 }
314 }
315}
316
317#[derive(Clone)]
319pub struct ManagedResourceScope {
320 pub(super) state: Rc<ManagedResourceScopeState>,
321}
322
323#[derive(Debug, Default)]
324pub(super) struct ManagedResourceScopeState {
325 pub(super) resources: RefCell<Vec<ManagedResourceHandle>>,
326 pub(super) closed: Cell<bool>,
327}
328
329impl std::fmt::Debug for ManagedResourceScope {
330 fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
331 formatter
332 .debug_struct("ManagedResourceScope")
333 .field("resource_count", &self.resource_count())
334 .finish()
335 }
336}
337
338impl ManagedResourceScope {
339 pub(super) fn new() -> Self {
340 Self {
341 state: Rc::new(ManagedResourceScopeState::default()),
342 }
343 }
344
345 pub fn register(
347 &self,
348 resource: impl ManagedResource,
349 ) -> Result<ManagedResourceHandle, ResourceRegistrationError> {
350 if self.state.closed.get() {
351 return Err(ResourceRegistrationError::ScopeClosed);
352 }
353 let handle = ManagedResourceHandle {
354 entry: Rc::new(ManagedResourceEntry {
355 resource: Rc::new(resource),
356 release: RefCell::new(ManagedResourceRelease::Pending),
357 }),
358 };
359 self.state.resources.borrow_mut().push(handle.clone());
360 Ok(handle)
361 }
362
363 pub fn resource_count(&self) -> usize {
365 self.state
366 .resources
367 .borrow()
368 .iter()
369 .filter(|resource| !resource.is_released())
370 .count()
371 }
372
373 pub(super) fn close(&self) {
374 self.state.closed.set(true);
375 }
376
377 pub(super) async fn release_all(&self) -> Option<RuntimeFailure> {
378 let resources = std::mem::take(&mut *self.state.resources.borrow_mut());
379 let mut first_error = None;
380 for resource in resources {
381 if let Err(error) = resource.release().await
382 && first_error.is_none()
383 {
384 first_error = Some(error);
385 }
386 }
387 first_error
388 }
389
390 pub(super) async fn release_all_until(
391 &self,
392 driver: &DriverControl,
393 deadline: Duration,
394 ) -> Result<Option<RuntimeFailure>, ()> {
395 let resources = std::mem::take(&mut *self.state.resources.borrow_mut());
396 let mut first_error = None;
397 for (index, resource) in resources.iter().enumerate() {
398 match wait_until(driver, deadline, resource.release()).await {
399 Some(Ok(())) => {}
400 Some(Err(error)) => {
401 if first_error.is_none() {
402 first_error = Some(error);
403 }
404 }
405 None => {
406 self.state
407 .resources
408 .borrow_mut()
409 .extend(resources.into_iter().skip(index));
410 return Err(());
411 }
412 }
413 }
414 Ok(first_error)
415 }
416}
417
418#[derive(Clone, Debug)]
420pub struct ManagedTask {
421 pub(super) task: Rc<RefCell<Option<DriverTask>>>,
422 pub(super) abort: AbortHandle,
423 pub(super) failed: Rc<Cell<bool>>,
424 pub(super) completed: Rc<Cell<bool>>,
425}
426
427impl ManagedTask {
428 pub(super) fn from_driver_task(task: DriverTask) -> Self {
429 Self {
430 abort: task.abort_handle(),
431 task: Rc::new(RefCell::new(Some(task))),
432 failed: Rc::new(Cell::new(false)),
433 completed: Rc::new(Cell::new(false)),
434 }
435 }
436
437 pub fn cancel(&self) {
439 self.abort.abort();
440 }
441
442 pub(super) async fn join(&self) -> TaskOutcome {
443 let outcome = std::future::poll_fn(|context| {
444 let mut slot = self.task.borrow_mut();
445 let Some(task) = slot.as_mut() else {
446 return Poll::Ready(TaskOutcome::Completed);
447 };
448 match Pin::new(task).poll(context) {
449 Poll::Ready(outcome) => {
450 slot.take();
451 Poll::Ready(outcome)
452 }
453 Poll::Pending => Poll::Pending,
454 }
455 })
456 .await;
457 if self.failed.get() {
458 TaskOutcome::Failed
459 } else {
460 outcome
461 }
462 }
463}
464
465#[derive(Debug)]
467pub enum ManagedTaskError {
468 ScopeClosed,
470 Driver(SpawnError),
472}
473
474impl From<SpawnError> for ManagedTaskError {
475 fn from(error: SpawnError) -> Self {
476 Self::Driver(error)
477 }
478}
479
480#[derive(Clone)]
482pub struct ManagedTaskScope {
483 pub(super) spawn: Rc<dyn Fn(LocalTask) -> Result<DriverTask, SpawnError>>,
484 pub(super) state: Rc<ManagedTaskScopeState>,
485}
486
487pub(super) struct ManagedTaskScopeState {
488 pub(super) tasks: RefCell<Vec<ManagedTask>>,
489 pub(super) closed: Cell<bool>,
490 pub(super) cancellation: CancellationToken,
491 pub(super) failure_handler: RefCell<Option<Rc<dyn Fn()>>>,
492 pub(super) unreported_failure: Cell<bool>,
493}
494
495impl Default for ManagedTaskScopeState {
496 fn default() -> Self {
497 Self {
498 tasks: RefCell::new(Vec::new()),
499 closed: Cell::new(false),
500 cancellation: CancellationToken::new(),
501 failure_handler: RefCell::new(None),
502 unreported_failure: Cell::new(false),
503 }
504 }
505}
506
507impl std::fmt::Debug for ManagedTaskScopeState {
508 fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
509 formatter
510 .debug_struct("ManagedTaskScopeState")
511 .field("task_count", &self.tasks.borrow().len())
512 .field("closed", &self.closed.get())
513 .field("unreported_failure", &self.unreported_failure.get())
514 .finish_non_exhaustive()
515 }
516}
517
518impl std::fmt::Debug for ManagedTaskScope {
519 fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
520 formatter
521 .debug_struct("ManagedTaskScope")
522 .field("task_count", &self.task_count())
523 .finish()
524 }
525}
526
527impl ManagedTaskScope {
528 pub(super) fn new<D: RuntimeDriver>(driver: &D) -> Self {
529 let spawner = driver.clone();
530 Self {
531 spawn: Rc::new(move |task| spawner.spawn_local(task)),
532 state: Rc::new(ManagedTaskScopeState::default()),
533 }
534 }
535
536 pub(super) fn new_from_driver_control(driver: &DriverControl) -> Self {
537 let spawn = driver.spawn_local.clone();
538 Self {
539 spawn,
540 state: Rc::new(ManagedTaskScopeState::default()),
541 }
542 }
543
544 pub fn spawn_local(&self, task: LocalTask) -> Result<ManagedTask, ManagedTaskError> {
546 if self.state.closed.get() {
547 return Err(ManagedTaskError::ScopeClosed);
548 }
549 let failed = Rc::new(Cell::new(false));
550 let task_failed = failed.clone();
551 let completed = Rc::new(Cell::new(false));
552 let task_completed = completed.clone();
553 let state = self.state.clone();
554 let monitored = Box::pin(async move {
555 let outcome = AssertUnwindSafe(task).catch_unwind().await;
556 task_completed.set(true);
557 if outcome.is_err() {
558 task_failed.set(true);
559 state.report_failure();
560 }
561 });
562 let driver_task = (self.spawn)(monitored)?;
563 let handle = ManagedTask {
564 failed,
565 completed,
566 ..ManagedTask::from_driver_task(driver_task)
567 };
568 self.state
569 .tasks
570 .borrow_mut()
571 .retain(|task| !task.completed.get());
572 self.state.tasks.borrow_mut().push(handle.clone());
573 Ok(handle)
574 }
575
576 pub fn task_count(&self) -> usize {
578 self.state
579 .tasks
580 .borrow()
581 .iter()
582 .filter(|task| !task.completed.get())
583 .count()
584 }
585
586 pub fn cancellation(&self) -> CancellationToken {
588 self.state.cancellation.clone()
589 }
590
591 pub(super) fn close(&self) {
592 self.state.closed.set(true);
593 self.state.cancellation.cancel();
594 }
595
596 pub(super) fn set_failure_handler(&self, handler: &Rc<dyn Fn()>) {
597 self.state.failure_handler.replace(Some(handler.clone()));
598 if self.state.unreported_failure.replace(false) {
599 handler();
600 }
601 }
602
603 pub(super) fn cancel(&self) {
604 self.state.cancellation.cancel();
605 }
606
607 pub(super) fn abort_all(&self) {
608 for task in self.state.tasks.borrow().iter() {
609 task.cancel();
610 }
611 }
612
613 pub(super) async fn cancel_all(&self) {
614 self.close();
615 let tasks = std::mem::take(&mut *self.state.tasks.borrow_mut());
616 for task in tasks {
617 task.cancel();
618 let _ = task.join().await;
619 }
620 }
621
622 pub(super) async fn drain_until(&self, driver: &DriverControl, deadline: Duration) -> bool {
623 self.cancel();
624 let tasks = std::mem::take(&mut *self.state.tasks.borrow_mut());
625 for (index, task) in tasks.iter().enumerate() {
626 if wait_until(driver, deadline, task.join()).await.is_none() {
627 for pending in tasks.iter().skip(index) {
628 pending.cancel();
629 }
630 self.state
631 .tasks
632 .borrow_mut()
633 .extend(tasks.into_iter().skip(index));
634 return false;
635 }
636 }
637 true
638 }
639}
640
641impl ManagedTaskScopeState {
642 pub(super) fn report_failure(&self) {
643 let handler = self.failure_handler.borrow().clone();
644 if let Some(handler) = handler {
645 handler();
646 } else {
647 self.unreported_failure.set(true);
648 }
649 }
650}
651
652#[derive(Clone, Debug)]
654pub struct PrepareContext {
655 pub(super) instance_key: String,
656 pub(super) entrypoint: String,
657 pub(super) configuration: String,
658 pub(super) dependencies: PluginDependencies,
659 pub(super) resources: ManagedResourceScope,
660 pub(super) cancellation: CancellationToken,
661 pub(super) admission: AppAdmission,
662}
663
664impl PrepareContext {
665 pub fn instance_key(&self) -> &str {
667 &self.instance_key
668 }
669
670 pub fn entrypoint(&self) -> &str {
672 &self.entrypoint
673 }
674
675 pub fn configuration(&self) -> &str {
677 &self.configuration
678 }
679
680 pub const fn phase(&self) -> PluginLifecyclePhase {
682 PluginLifecyclePhase::Prepare
683 }
684
685 pub fn dependencies(&self) -> &PluginDependencies {
687 &self.dependencies
688 }
689
690 pub fn resources(&self) -> &ManagedResourceScope {
692 &self.resources
693 }
694
695 pub fn cancellation(&self) -> CancellationToken {
697 self.cancellation.clone()
698 }
699
700 pub fn admission(&self) -> AppAdmission {
702 self.admission.clone()
703 }
704}
705
706#[derive(Clone, Debug)]
708pub struct ActivateContext {
709 pub(super) instance_key: String,
710 pub(super) dependencies: PluginDependencies,
711 pub(super) ready_gate: AppReadyGate,
712 pub(super) tasks: ManagedTaskScope,
713 pub(super) resources: ManagedResourceScope,
714 pub(super) cancellation: CancellationToken,
715 pub(super) admission: AppAdmission,
716}
717
718impl ActivateContext {
719 pub fn instance_key(&self) -> &str {
721 &self.instance_key
722 }
723
724 pub const fn phase(&self) -> PluginLifecyclePhase {
726 PluginLifecyclePhase::Activate
727 }
728
729 pub fn dependencies(&self) -> &PluginDependencies {
731 &self.dependencies
732 }
733
734 pub fn ready_gate(&self) -> AppReadyGate {
736 self.ready_gate.clone()
737 }
738
739 pub fn readiness(&self) -> ReadinessContext {
741 ReadinessContext {
742 instance_key: self.instance_key.clone(),
743 dependencies: self.dependencies.clone(),
744 ready_gate: self.ready_gate.clone(),
745 tasks: self.tasks.clone(),
746 resources: self.resources.clone(),
747 cancellation: self.cancellation.clone(),
748 admission: self.admission.clone(),
749 }
750 }
751
752 pub fn tasks(&self) -> &ManagedTaskScope {
754 &self.tasks
755 }
756
757 pub fn resources(&self) -> &ManagedResourceScope {
759 &self.resources
760 }
761
762 pub fn cancellation(&self) -> CancellationToken {
764 self.cancellation.clone()
765 }
766
767 pub fn admission(&self) -> AppAdmission {
769 self.admission.clone()
770 }
771}
772
773#[derive(Clone, Debug)]
775pub struct ReadinessContext {
776 pub(super) instance_key: String,
777 pub(super) dependencies: PluginDependencies,
778 pub(super) ready_gate: AppReadyGate,
779 pub(super) tasks: ManagedTaskScope,
780 pub(super) resources: ManagedResourceScope,
781 pub(super) cancellation: CancellationToken,
782 pub(super) admission: AppAdmission,
783}
784
785impl ReadinessContext {
786 pub fn instance_key(&self) -> &str {
788 &self.instance_key
789 }
790
791 pub const fn phase(&self) -> PluginLifecyclePhase {
793 PluginLifecyclePhase::Ready
794 }
795
796 pub fn dependencies(&self) -> &PluginDependencies {
798 &self.dependencies
799 }
800
801 pub fn ready_gate(&self) -> AppReadyGate {
803 self.ready_gate.clone()
804 }
805
806 pub fn wait(&self) -> LocalBoxFuture<'static, ()> {
808 self.ready_gate.wait()
809 }
810
811 pub fn is_open(&self) -> bool {
813 self.ready_gate.is_open()
814 }
815
816 pub fn tasks(&self) -> &ManagedTaskScope {
818 &self.tasks
819 }
820
821 pub fn resources(&self) -> &ManagedResourceScope {
823 &self.resources
824 }
825
826 pub fn cancellation(&self) -> CancellationToken {
828 self.cancellation.clone()
829 }
830
831 pub fn is_accepting(&self) -> bool {
833 self.admission.is_open()
834 }
835
836 pub fn admission(&self) -> AppAdmission {
838 self.admission.clone()
839 }
840}
841
842#[derive(Clone, Copy, Debug, Eq, PartialEq)]
844pub enum DeactivationReason {
845 StartupRollback,
847 Shutdown,
849 SupervisionRestart,
851}
852
853#[derive(Clone, Debug)]
855pub struct DeactivateContext {
856 pub(super) instance_key: String,
857 pub(super) dependencies: PluginDependencies,
858 pub(super) reason: DeactivationReason,
859 pub(super) tasks: ManagedTaskScope,
860 pub(super) resources: ManagedResourceScope,
861 pub(super) cancellation: CancellationToken,
862 pub(super) admission: AppAdmission,
863 pub(super) cleanup: Option<super::cleanup::CleanupBudget>,
864}
865
866impl DeactivateContext {
867 pub fn instance_key(&self) -> &str {
869 &self.instance_key
870 }
871
872 pub const fn phase(&self) -> PluginLifecyclePhase {
874 PluginLifecyclePhase::Deactivate
875 }
876
877 pub fn dependencies(&self) -> &PluginDependencies {
879 &self.dependencies
880 }
881
882 pub const fn reason(&self) -> DeactivationReason {
884 self.reason
885 }
886
887 pub fn tasks(&self) -> &ManagedTaskScope {
889 &self.tasks
890 }
891
892 pub fn resources(&self) -> &ManagedResourceScope {
894 &self.resources
895 }
896
897 pub fn cancellation(&self) -> CancellationToken {
899 self.cleanup.as_ref().map_or_else(
900 || self.cancellation.clone(),
901 super::cleanup::CleanupBudget::cancellation,
902 )
903 }
904
905 pub fn dependency_invocation_context(&self) -> Result<InvocationContext, RuntimeFailure> {
910 self.dependencies.shutdown_invocation_context(
911 self.cleanup
912 .as_ref()
913 .map(super::cleanup::CleanupBudget::deadline),
914 self.cancellation(),
915 )
916 }
917
918 pub fn remaining_budget(&self) -> Option<Duration> {
923 self.cleanup
924 .as_ref()
925 .map(super::cleanup::CleanupBudget::remaining)
926 }
927
928 pub fn admission(&self) -> AppAdmission {
930 self.admission.clone()
931 }
932}
933
934pub type PluginFuture = LocalBoxFuture<'static, Result<(), RuntimeFailure>>;
936
937pub trait PluginLifecycle: std::fmt::Debug + 'static {
939 fn prepare(&self, _context: PrepareContext) -> PluginFuture {
941 Box::pin(futures::future::ready(Ok(())))
942 }
943
944 #[doc(hidden)]
947 fn construct(&self, _context: ActivateContext) -> PluginFuture {
948 Box::pin(futures::future::ready(Ok(())))
949 }
950
951 fn activate(&self, _context: ActivateContext) -> PluginFuture {
953 Box::pin(futures::future::ready(Ok(())))
954 }
955
956 fn deactivate(&self, _context: DeactivateContext) -> PluginFuture {
958 Box::pin(futures::future::ready(Ok(())))
959 }
960}
961
962#[derive(Debug, Default)]
964pub struct NoopPluginLifecycle;
965
966impl PluginLifecycle for NoopPluginLifecycle {}
967
968#[cfg(test)]
969mod tests {
970 use super::*;
971
972 #[test]
973 fn admission_close_waiter_ignores_the_initial_startup_gate() {
974 let admission = AppAdmission::new();
975 let mut waiting = admission.wait_closed();
976 let mut context = Context::from_waker(futures::task::noop_waker_ref());
977
978 assert!(matches!(waiting.as_mut().poll(&mut context), Poll::Pending));
979 admission.open();
980 assert!(matches!(waiting.as_mut().poll(&mut context), Poll::Pending));
981 admission.close();
982 assert!(matches!(
983 waiting.as_mut().poll(&mut context),
984 Poll::Ready(())
985 ));
986
987 let mut late_waiter = admission.wait_closed();
988 assert!(matches!(
989 late_waiter.as_mut().poll(&mut context),
990 Poll::Ready(())
991 ));
992 }
993}