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}
74
75impl AppAdmission {
76 pub(super) fn new() -> Self {
77 Self {
78 state: Rc::new(AppAdmissionState {
79 open: Cell::new(false),
80 }),
81 }
82 }
83
84 pub fn is_open(&self) -> bool {
86 self.state.open.get()
87 }
88
89 pub fn is_closed(&self) -> bool {
91 !self.is_open()
92 }
93
94 pub(super) fn open(&self) {
95 self.state.open.set(true);
96 }
97
98 pub(super) fn close(&self) {
99 self.state.open.set(false);
100 }
101}
102
103#[derive(Clone, Debug)]
105pub struct CancellationToken {
106 pub(super) state: Rc<CancellationState>,
107}
108
109#[derive(Debug)]
110pub(super) struct CancellationState {
111 pub(super) cancelled: Cell<bool>,
112 pub(super) next_waiter_id: Cell<usize>,
113 pub(super) waiters: RefCell<Vec<(usize, oneshot::Sender<()>)>>,
114}
115
116impl CancellationToken {
117 pub fn new() -> Self {
119 Self {
120 state: Rc::new(CancellationState {
121 cancelled: Cell::new(false),
122 next_waiter_id: Cell::new(0),
123 waiters: RefCell::new(Vec::new()),
124 }),
125 }
126 }
127
128 pub fn is_cancelled(&self) -> bool {
130 self.state.cancelled.get()
131 }
132
133 pub fn cancelled(&self) -> LocalBoxFuture<'static, ()> {
135 if self.is_cancelled() {
136 return Box::pin(futures::future::ready(()));
137 }
138 let (wakeup, waiter) = oneshot::channel();
139 let waiter_id = self.state.next_waiter_id.get();
140 self.state.next_waiter_id.set(waiter_id.saturating_add(1));
141 self.state.waiters.borrow_mut().push((waiter_id, wakeup));
142 Box::pin(CancellationWaiter {
143 state: self.state.clone(),
144 waiter_id,
145 receiver: waiter,
146 registered: true,
147 })
148 }
149
150 pub fn cancel(&self) {
152 if self.state.cancelled.replace(true) {
153 return;
154 }
155 for (_, waiter) in self.state.waiters.borrow_mut().drain(..) {
156 let _ = waiter.send(());
157 }
158 }
159}
160
161#[derive(Debug)]
162pub(super) struct CancellationWaiter {
163 pub(super) state: Rc<CancellationState>,
164 pub(super) waiter_id: usize,
165 pub(super) receiver: oneshot::Receiver<()>,
166 pub(super) registered: bool,
167}
168
169impl Future for CancellationWaiter {
170 type Output = ();
171
172 fn poll(mut self: Pin<&mut Self>, context: &mut Context<'_>) -> Poll<Self::Output> {
173 match Pin::new(&mut self.receiver).poll(context) {
174 Poll::Ready(_) => {
175 self.registered = false;
176 Poll::Ready(())
177 }
178 Poll::Pending => Poll::Pending,
179 }
180 }
181}
182
183impl Drop for CancellationWaiter {
184 fn drop(&mut self) {
185 if !self.registered {
186 return;
187 }
188 self.state
189 .waiters
190 .borrow_mut()
191 .retain(|(waiter_id, _)| *waiter_id != self.waiter_id);
192 }
193}
194
195impl Default for CancellationToken {
196 fn default() -> Self {
197 Self::new()
198 }
199}
200
201pub type ResourceFuture = LocalBoxFuture<'static, Result<(), RuntimeFailure>>;
203
204pub trait ManagedResource: std::fmt::Debug + 'static {
206 fn release(&self) -> ResourceFuture;
208}
209
210#[derive(Clone, Copy, Debug, Eq, PartialEq)]
212pub enum ResourceRegistrationError {
213 ScopeClosed,
215}
216
217pub(super) struct ManagedResourceEntry {
218 pub(super) resource: Rc<dyn ManagedResource>,
219 pub(super) release: RefCell<ManagedResourceRelease>,
220}
221
222pub(super) enum ManagedResourceRelease {
223 Pending,
224 Running(ResourceFuture),
225 Complete(Result<(), RuntimeFailure>),
226}
227
228impl std::fmt::Debug for ManagedResourceEntry {
229 fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
230 let state = match &*self.release.borrow() {
231 ManagedResourceRelease::Pending => "pending",
232 ManagedResourceRelease::Running(_) => "running",
233 ManagedResourceRelease::Complete(Ok(())) => "released",
234 ManagedResourceRelease::Complete(Err(_)) => "failed",
235 };
236 formatter
237 .debug_struct("ManagedResourceEntry")
238 .field("release", &state)
239 .finish_non_exhaustive()
240 }
241}
242
243#[derive(Clone, Debug)]
245pub struct ManagedResourceHandle {
246 pub(super) entry: Rc<ManagedResourceEntry>,
247}
248
249impl ManagedResourceHandle {
250 pub fn is_released(&self) -> bool {
252 matches!(
253 &*self.entry.release.borrow(),
254 ManagedResourceRelease::Complete(_)
255 )
256 }
257
258 pub async fn release(&self) -> Result<(), RuntimeFailure> {
260 ManagedResourceReleaseOperation {
261 entry: self.entry.clone(),
262 }
263 .await
264 }
265}
266
267pub(super) struct ManagedResourceReleaseOperation {
268 pub(super) entry: Rc<ManagedResourceEntry>,
269}
270
271impl Future for ManagedResourceReleaseOperation {
272 type Output = Result<(), RuntimeFailure>;
273
274 fn poll(self: Pin<&mut Self>, context: &mut Context<'_>) -> Poll<Self::Output> {
275 let mut release = self.entry.release.borrow_mut();
276 if matches!(*release, ManagedResourceRelease::Pending) {
277 *release = ManagedResourceRelease::Running(self.entry.resource.release());
278 }
279 match &mut *release {
280 ManagedResourceRelease::Running(future) => match future.as_mut().poll(context) {
281 Poll::Ready(result) => {
282 *release = ManagedResourceRelease::Complete(result.clone());
283 Poll::Ready(result)
284 }
285 Poll::Pending => Poll::Pending,
286 },
287 ManagedResourceRelease::Complete(result) => Poll::Ready(result.clone()),
288 ManagedResourceRelease::Pending => unreachable!("pending release was started"),
289 }
290 }
291}
292
293#[derive(Clone)]
295pub struct ManagedResourceScope {
296 pub(super) state: Rc<ManagedResourceScopeState>,
297}
298
299#[derive(Debug, Default)]
300pub(super) struct ManagedResourceScopeState {
301 pub(super) resources: RefCell<Vec<ManagedResourceHandle>>,
302 pub(super) closed: Cell<bool>,
303}
304
305impl std::fmt::Debug for ManagedResourceScope {
306 fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
307 formatter
308 .debug_struct("ManagedResourceScope")
309 .field("resource_count", &self.resource_count())
310 .finish()
311 }
312}
313
314impl ManagedResourceScope {
315 pub(super) fn new() -> Self {
316 Self {
317 state: Rc::new(ManagedResourceScopeState::default()),
318 }
319 }
320
321 pub fn register(
323 &self,
324 resource: impl ManagedResource,
325 ) -> Result<ManagedResourceHandle, ResourceRegistrationError> {
326 if self.state.closed.get() {
327 return Err(ResourceRegistrationError::ScopeClosed);
328 }
329 let handle = ManagedResourceHandle {
330 entry: Rc::new(ManagedResourceEntry {
331 resource: Rc::new(resource),
332 release: RefCell::new(ManagedResourceRelease::Pending),
333 }),
334 };
335 self.state.resources.borrow_mut().push(handle.clone());
336 Ok(handle)
337 }
338
339 pub fn resource_count(&self) -> usize {
341 self.state
342 .resources
343 .borrow()
344 .iter()
345 .filter(|resource| !resource.is_released())
346 .count()
347 }
348
349 pub(super) fn close(&self) {
350 self.state.closed.set(true);
351 }
352
353 pub(super) async fn release_all(&self) -> Option<RuntimeFailure> {
354 let resources = std::mem::take(&mut *self.state.resources.borrow_mut());
355 let mut first_error = None;
356 for resource in resources {
357 if let Err(error) = resource.release().await
358 && first_error.is_none()
359 {
360 first_error = Some(error);
361 }
362 }
363 first_error
364 }
365
366 pub(super) async fn release_all_until(
367 &self,
368 driver: &DriverControl,
369 deadline: Duration,
370 ) -> Result<Option<RuntimeFailure>, ()> {
371 let resources = std::mem::take(&mut *self.state.resources.borrow_mut());
372 let mut first_error = None;
373 for (index, resource) in resources.iter().enumerate() {
374 match wait_until(driver, deadline, resource.release()).await {
375 Some(Ok(())) => {}
376 Some(Err(error)) => {
377 if first_error.is_none() {
378 first_error = Some(error);
379 }
380 }
381 None => {
382 self.state
383 .resources
384 .borrow_mut()
385 .extend(resources.into_iter().skip(index));
386 return Err(());
387 }
388 }
389 }
390 Ok(first_error)
391 }
392}
393
394#[derive(Clone, Debug)]
396pub struct ManagedTask {
397 pub(super) task: Rc<RefCell<Option<DriverTask>>>,
398 pub(super) abort: AbortHandle,
399 pub(super) failed: Rc<Cell<bool>>,
400 pub(super) completed: Rc<Cell<bool>>,
401}
402
403impl ManagedTask {
404 pub(super) fn from_driver_task(task: DriverTask) -> Self {
405 Self {
406 abort: task.abort_handle(),
407 task: Rc::new(RefCell::new(Some(task))),
408 failed: Rc::new(Cell::new(false)),
409 completed: Rc::new(Cell::new(false)),
410 }
411 }
412
413 pub fn cancel(&self) {
415 self.abort.abort();
416 }
417
418 pub(super) async fn join(&self) -> TaskOutcome {
419 let outcome = std::future::poll_fn(|context| {
420 let mut slot = self.task.borrow_mut();
421 let Some(task) = slot.as_mut() else {
422 return Poll::Ready(TaskOutcome::Completed);
423 };
424 match Pin::new(task).poll(context) {
425 Poll::Ready(outcome) => {
426 slot.take();
427 Poll::Ready(outcome)
428 }
429 Poll::Pending => Poll::Pending,
430 }
431 })
432 .await;
433 if self.failed.get() {
434 TaskOutcome::Failed
435 } else {
436 outcome
437 }
438 }
439}
440
441#[derive(Debug)]
443pub enum ManagedTaskError {
444 ScopeClosed,
446 Driver(SpawnError),
448}
449
450impl From<SpawnError> for ManagedTaskError {
451 fn from(error: SpawnError) -> Self {
452 Self::Driver(error)
453 }
454}
455
456#[derive(Clone)]
458pub struct ManagedTaskScope {
459 pub(super) spawn: Rc<dyn Fn(LocalTask) -> Result<DriverTask, SpawnError>>,
460 pub(super) state: Rc<ManagedTaskScopeState>,
461}
462
463pub(super) struct ManagedTaskScopeState {
464 pub(super) tasks: RefCell<Vec<ManagedTask>>,
465 pub(super) closed: Cell<bool>,
466 pub(super) cancellation: CancellationToken,
467 pub(super) failure_handler: RefCell<Option<Rc<dyn Fn()>>>,
468 pub(super) unreported_failure: Cell<bool>,
469}
470
471impl Default for ManagedTaskScopeState {
472 fn default() -> Self {
473 Self {
474 tasks: RefCell::new(Vec::new()),
475 closed: Cell::new(false),
476 cancellation: CancellationToken::new(),
477 failure_handler: RefCell::new(None),
478 unreported_failure: Cell::new(false),
479 }
480 }
481}
482
483impl std::fmt::Debug for ManagedTaskScopeState {
484 fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
485 formatter
486 .debug_struct("ManagedTaskScopeState")
487 .field("task_count", &self.tasks.borrow().len())
488 .field("closed", &self.closed.get())
489 .field("unreported_failure", &self.unreported_failure.get())
490 .finish_non_exhaustive()
491 }
492}
493
494impl std::fmt::Debug for ManagedTaskScope {
495 fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
496 formatter
497 .debug_struct("ManagedTaskScope")
498 .field("task_count", &self.task_count())
499 .finish()
500 }
501}
502
503impl ManagedTaskScope {
504 pub(super) fn new<D: RuntimeDriver>(driver: &D) -> Self {
505 let spawner = driver.clone();
506 Self {
507 spawn: Rc::new(move |task| spawner.spawn_local(task)),
508 state: Rc::new(ManagedTaskScopeState::default()),
509 }
510 }
511
512 pub(super) fn new_from_driver_control(driver: &DriverControl) -> Self {
513 let spawn = driver.spawn_local.clone();
514 Self {
515 spawn,
516 state: Rc::new(ManagedTaskScopeState::default()),
517 }
518 }
519
520 pub fn spawn_local(&self, task: LocalTask) -> Result<ManagedTask, ManagedTaskError> {
522 if self.state.closed.get() {
523 return Err(ManagedTaskError::ScopeClosed);
524 }
525 let failed = Rc::new(Cell::new(false));
526 let task_failed = failed.clone();
527 let completed = Rc::new(Cell::new(false));
528 let task_completed = completed.clone();
529 let state = self.state.clone();
530 let monitored = Box::pin(async move {
531 let outcome = AssertUnwindSafe(task).catch_unwind().await;
532 task_completed.set(true);
533 if outcome.is_err() {
534 task_failed.set(true);
535 state.report_failure();
536 }
537 });
538 let driver_task = (self.spawn)(monitored)?;
539 let handle = ManagedTask {
540 failed,
541 completed,
542 ..ManagedTask::from_driver_task(driver_task)
543 };
544 self.state
545 .tasks
546 .borrow_mut()
547 .retain(|task| !task.completed.get());
548 self.state.tasks.borrow_mut().push(handle.clone());
549 Ok(handle)
550 }
551
552 pub fn task_count(&self) -> usize {
554 self.state
555 .tasks
556 .borrow()
557 .iter()
558 .filter(|task| !task.completed.get())
559 .count()
560 }
561
562 pub fn cancellation(&self) -> CancellationToken {
564 self.state.cancellation.clone()
565 }
566
567 pub(super) fn close(&self) {
568 self.state.closed.set(true);
569 self.state.cancellation.cancel();
570 }
571
572 pub(super) fn set_failure_handler(&self, handler: &Rc<dyn Fn()>) {
573 self.state.failure_handler.replace(Some(handler.clone()));
574 if self.state.unreported_failure.replace(false) {
575 handler();
576 }
577 }
578
579 pub(super) fn cancel(&self) {
580 self.state.cancellation.cancel();
581 }
582
583 pub(super) fn abort_all(&self) {
584 for task in self.state.tasks.borrow().iter() {
585 task.cancel();
586 }
587 }
588
589 pub(super) async fn cancel_all(&self) {
590 self.close();
591 let tasks = std::mem::take(&mut *self.state.tasks.borrow_mut());
592 for task in tasks {
593 task.cancel();
594 let _ = task.join().await;
595 }
596 }
597
598 pub(super) async fn drain_until(&self, driver: &DriverControl, deadline: Duration) -> bool {
599 self.cancel();
600 let tasks = std::mem::take(&mut *self.state.tasks.borrow_mut());
601 for (index, task) in tasks.iter().enumerate() {
602 if wait_until(driver, deadline, task.join()).await.is_none() {
603 for pending in tasks.iter().skip(index) {
604 pending.cancel();
605 }
606 self.state
607 .tasks
608 .borrow_mut()
609 .extend(tasks.into_iter().skip(index));
610 return false;
611 }
612 }
613 true
614 }
615}
616
617impl ManagedTaskScopeState {
618 pub(super) fn report_failure(&self) {
619 let handler = self.failure_handler.borrow().clone();
620 if let Some(handler) = handler {
621 handler();
622 } else {
623 self.unreported_failure.set(true);
624 }
625 }
626}
627
628#[derive(Clone, Debug)]
630pub struct PrepareContext {
631 pub(super) instance_key: String,
632 pub(super) entrypoint: String,
633 pub(super) configuration: String,
634 pub(super) dependencies: PluginDependencies,
635 pub(super) resources: ManagedResourceScope,
636 pub(super) cancellation: CancellationToken,
637 pub(super) admission: AppAdmission,
638}
639
640impl PrepareContext {
641 pub fn instance_key(&self) -> &str {
643 &self.instance_key
644 }
645
646 pub fn entrypoint(&self) -> &str {
648 &self.entrypoint
649 }
650
651 pub fn configuration(&self) -> &str {
653 &self.configuration
654 }
655
656 pub const fn phase(&self) -> PluginLifecyclePhase {
658 PluginLifecyclePhase::Prepare
659 }
660
661 pub fn dependencies(&self) -> &PluginDependencies {
663 &self.dependencies
664 }
665
666 pub fn resources(&self) -> &ManagedResourceScope {
668 &self.resources
669 }
670
671 pub fn cancellation(&self) -> CancellationToken {
673 self.cancellation.clone()
674 }
675
676 pub fn admission(&self) -> AppAdmission {
678 self.admission.clone()
679 }
680}
681
682#[derive(Clone, Debug)]
684pub struct ActivateContext {
685 pub(super) instance_key: String,
686 pub(super) dependencies: PluginDependencies,
687 pub(super) ready_gate: AppReadyGate,
688 pub(super) tasks: ManagedTaskScope,
689 pub(super) resources: ManagedResourceScope,
690 pub(super) cancellation: CancellationToken,
691 pub(super) admission: AppAdmission,
692}
693
694impl ActivateContext {
695 pub fn instance_key(&self) -> &str {
697 &self.instance_key
698 }
699
700 pub const fn phase(&self) -> PluginLifecyclePhase {
702 PluginLifecyclePhase::Activate
703 }
704
705 pub fn dependencies(&self) -> &PluginDependencies {
707 &self.dependencies
708 }
709
710 pub fn ready_gate(&self) -> AppReadyGate {
712 self.ready_gate.clone()
713 }
714
715 pub fn readiness(&self) -> ReadinessContext {
717 ReadinessContext {
718 instance_key: self.instance_key.clone(),
719 dependencies: self.dependencies.clone(),
720 ready_gate: self.ready_gate.clone(),
721 tasks: self.tasks.clone(),
722 resources: self.resources.clone(),
723 cancellation: self.cancellation.clone(),
724 admission: self.admission.clone(),
725 }
726 }
727
728 pub fn tasks(&self) -> &ManagedTaskScope {
730 &self.tasks
731 }
732
733 pub fn resources(&self) -> &ManagedResourceScope {
735 &self.resources
736 }
737
738 pub fn cancellation(&self) -> CancellationToken {
740 self.cancellation.clone()
741 }
742
743 pub fn admission(&self) -> AppAdmission {
745 self.admission.clone()
746 }
747}
748
749#[derive(Clone, Debug)]
751pub struct ReadinessContext {
752 pub(super) instance_key: String,
753 pub(super) dependencies: PluginDependencies,
754 pub(super) ready_gate: AppReadyGate,
755 pub(super) tasks: ManagedTaskScope,
756 pub(super) resources: ManagedResourceScope,
757 pub(super) cancellation: CancellationToken,
758 pub(super) admission: AppAdmission,
759}
760
761impl ReadinessContext {
762 pub fn instance_key(&self) -> &str {
764 &self.instance_key
765 }
766
767 pub const fn phase(&self) -> PluginLifecyclePhase {
769 PluginLifecyclePhase::Ready
770 }
771
772 pub fn dependencies(&self) -> &PluginDependencies {
774 &self.dependencies
775 }
776
777 pub fn ready_gate(&self) -> AppReadyGate {
779 self.ready_gate.clone()
780 }
781
782 pub fn wait(&self) -> LocalBoxFuture<'static, ()> {
784 self.ready_gate.wait()
785 }
786
787 pub fn is_open(&self) -> bool {
789 self.ready_gate.is_open()
790 }
791
792 pub fn tasks(&self) -> &ManagedTaskScope {
794 &self.tasks
795 }
796
797 pub fn resources(&self) -> &ManagedResourceScope {
799 &self.resources
800 }
801
802 pub fn cancellation(&self) -> CancellationToken {
804 self.cancellation.clone()
805 }
806
807 pub fn is_accepting(&self) -> bool {
809 self.admission.is_open()
810 }
811
812 pub fn admission(&self) -> AppAdmission {
814 self.admission.clone()
815 }
816}
817
818#[derive(Clone, Copy, Debug, Eq, PartialEq)]
820pub enum DeactivationReason {
821 StartupRollback,
823 Shutdown,
825 SupervisionRestart,
827}
828
829#[derive(Clone, Debug)]
831pub struct DeactivateContext {
832 pub(super) instance_key: String,
833 pub(super) dependencies: PluginDependencies,
834 pub(super) reason: DeactivationReason,
835 pub(super) tasks: ManagedTaskScope,
836 pub(super) resources: ManagedResourceScope,
837 pub(super) cancellation: CancellationToken,
838 pub(super) admission: AppAdmission,
839 pub(super) cleanup: Option<super::cleanup::CleanupBudget>,
840}
841
842impl DeactivateContext {
843 pub fn instance_key(&self) -> &str {
845 &self.instance_key
846 }
847
848 pub const fn phase(&self) -> PluginLifecyclePhase {
850 PluginLifecyclePhase::Deactivate
851 }
852
853 pub fn dependencies(&self) -> &PluginDependencies {
855 &self.dependencies
856 }
857
858 pub const fn reason(&self) -> DeactivationReason {
860 self.reason
861 }
862
863 pub fn tasks(&self) -> &ManagedTaskScope {
865 &self.tasks
866 }
867
868 pub fn resources(&self) -> &ManagedResourceScope {
870 &self.resources
871 }
872
873 pub fn cancellation(&self) -> CancellationToken {
875 self.cleanup.as_ref().map_or_else(
876 || self.cancellation.clone(),
877 super::cleanup::CleanupBudget::cancellation,
878 )
879 }
880
881 pub fn remaining_budget(&self) -> Option<Duration> {
886 self.cleanup
887 .as_ref()
888 .map(super::cleanup::CleanupBudget::remaining)
889 }
890
891 pub fn admission(&self) -> AppAdmission {
893 self.admission.clone()
894 }
895}
896
897pub type PluginFuture = LocalBoxFuture<'static, Result<(), RuntimeFailure>>;
899
900pub trait PluginLifecycle: std::fmt::Debug + 'static {
902 fn prepare(&self, _context: PrepareContext) -> PluginFuture {
904 Box::pin(futures::future::ready(Ok(())))
905 }
906
907 #[doc(hidden)]
910 fn construct(&self, _context: ActivateContext) -> PluginFuture {
911 Box::pin(futures::future::ready(Ok(())))
912 }
913
914 fn activate(&self, _context: ActivateContext) -> PluginFuture {
916 Box::pin(futures::future::ready(Ok(())))
917 }
918
919 fn deactivate(&self, _context: DeactivateContext) -> PluginFuture {
921 Box::pin(futures::future::ready(Ok(())))
922 }
923}
924
925#[derive(Debug, Default)]
927pub struct NoopPluginLifecycle;
928
929impl PluginLifecycle for NoopPluginLifecycle {}