synd_api/session/
idle_shutdown.rs1use std::time::Duration;
2
3use tokio_util::sync::CancellationToken;
4use tracing::info;
5
6use crate::shutdown::{Shutdown, ShutdownReason};
7
8#[derive(Clone)]
10pub struct SessionIdleShutdown {
11 grace: Duration,
12 shutdown: Shutdown,
13}
14
15impl SessionIdleShutdown {
16 pub fn new(grace: Duration, shutdown: Shutdown) -> Self {
17 Self { grace, shutdown }
18 }
19
20 pub fn grace(&self) -> Duration {
21 self.grace
22 }
23
24 fn schedule(&self, timer: IdleShutdownTimer) {
25 let grace = self.grace;
26 let shutdown = self.shutdown.clone();
27
28 tokio::spawn(async move {
29 tokio::select! {
30 () = timer.cancelled() => {}
31 () = tokio::time::sleep(grace) => {
32 info!(
33 idle_shutdown_grace_ms = grace.as_millis(),
34 "Daemon session idle grace elapsed"
35 );
36 shutdown.shutdown_with_reason(ShutdownReason::Idle);
37 }
38 }
39 });
40 }
41}
42
43impl std::fmt::Debug for SessionIdleShutdown {
44 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
45 f.debug_struct("SessionIdleShutdown")
46 .field("grace", &self.grace)
47 .finish_non_exhaustive()
48 }
49}
50
51#[derive(Clone)]
53pub(super) struct IdleShutdownTimer {
54 cancellation: CancellationToken,
55}
56
57impl IdleShutdownTimer {
58 pub(super) fn new() -> Self {
59 Self {
60 cancellation: CancellationToken::new(),
61 }
62 }
63
64 fn cancel(self) {
65 self.cancellation.cancel();
66 }
67
68 async fn cancelled(&self) {
69 self.cancellation.cancelled().await;
70 }
71}
72
73impl std::fmt::Debug for IdleShutdownTimer {
74 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
75 f.debug_struct("IdleShutdownTimer").finish_non_exhaustive()
76 }
77}
78
79#[derive(Debug, Clone)]
81pub(super) enum DaemonSessionsEffect {
82 None,
83 CancelIdleShutdown { timer: IdleShutdownTimer },
84 ScheduleIdleShutdown { timer: IdleShutdownTimer },
85}
86
87impl PartialEq for DaemonSessionsEffect {
88 fn eq(&self, other: &Self) -> bool {
89 std::mem::discriminant(self) == std::mem::discriminant(other)
90 }
91}
92
93impl Eq for DaemonSessionsEffect {}
94
95impl DaemonSessionsEffect {
96 pub(super) fn apply(self, idle_shutdown: Option<&SessionIdleShutdown>) {
97 match self {
98 Self::None => {}
99 Self::CancelIdleShutdown { timer } => timer.cancel(),
100 Self::ScheduleIdleShutdown { timer } => {
101 if let Some(idle_shutdown) = idle_shutdown {
102 idle_shutdown.schedule(timer);
103 }
104 }
105 }
106 }
107}