autumn_plugin_pingora/
lifecycle.rs1use std::fmt;
18use std::sync::atomic::{AtomicU8, Ordering};
19
20#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
22#[non_exhaustive]
23pub enum Lifecycle {
24 Idle,
26 Serving,
28 Draining,
30 Stopped,
32 Failed,
34}
35
36#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
38#[non_exhaustive]
39pub enum LifecycleEvent {
40 Bound,
42 StartFailed,
44 ShutdownRequested,
46 Drained,
48 ServerExited,
50}
51
52impl Lifecycle {
53 pub const ALL: [Self; 5] = [
55 Self::Idle,
56 Self::Serving,
57 Self::Draining,
58 Self::Stopped,
59 Self::Failed,
60 ];
61
62 #[must_use]
64 pub const fn next(self, event: LifecycleEvent) -> Option<Self> {
65 use LifecycleEvent::{Bound, Drained, ServerExited, ShutdownRequested, StartFailed};
66 match (self, event) {
67 (Self::Idle, Bound) => Some(Self::Serving),
68 (Self::Serving, ShutdownRequested) => Some(Self::Draining),
69 (Self::Idle, StartFailed) | (Self::Serving, ServerExited) => Some(Self::Failed),
70 (Self::Idle, ShutdownRequested) | (Self::Draining, Drained | ServerExited) => {
71 Some(Self::Stopped)
72 }
73 _ => None,
74 }
75 }
76
77 #[must_use]
79 pub const fn is_ready(self) -> bool {
80 matches!(self, Self::Serving)
81 }
82
83 #[must_use]
85 pub const fn is_terminal(self) -> bool {
86 matches!(self, Self::Stopped | Self::Failed)
87 }
88
89 #[must_use]
91 pub const fn as_str(self) -> &'static str {
92 match self {
93 Self::Idle => "idle",
94 Self::Serving => "serving",
95 Self::Draining => "draining",
96 Self::Stopped => "stopped",
97 Self::Failed => "failed",
98 }
99 }
100
101 const fn to_u8(self) -> u8 {
102 match self {
103 Self::Idle => 0,
104 Self::Serving => 1,
105 Self::Draining => 2,
106 Self::Stopped => 3,
107 Self::Failed => 4,
108 }
109 }
110
111 const fn from_u8(value: u8) -> Self {
112 match value {
113 0 => Self::Idle,
114 1 => Self::Serving,
115 2 => Self::Draining,
116 3 => Self::Stopped,
117 _ => Self::Failed,
118 }
119 }
120}
121
122impl LifecycleEvent {
123 pub const ALL: [Self; 5] = [
125 Self::Bound,
126 Self::StartFailed,
127 Self::ShutdownRequested,
128 Self::Drained,
129 Self::ServerExited,
130 ];
131}
132
133impl fmt::Display for Lifecycle {
134 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
135 f.write_str(self.as_str())
136 }
137}
138
139#[derive(Debug)]
141pub struct LifecycleCell(AtomicU8);
142
143impl Default for LifecycleCell {
144 fn default() -> Self {
145 Self::new()
146 }
147}
148
149impl LifecycleCell {
150 #[must_use]
152 pub const fn new() -> Self {
153 Self(AtomicU8::new(Lifecycle::Idle.to_u8()))
154 }
155
156 #[must_use]
158 pub fn get(&self) -> Lifecycle {
159 Lifecycle::from_u8(self.0.load(Ordering::Acquire))
160 }
161
162 pub fn apply(&self, event: LifecycleEvent) -> Result<Lifecycle, Lifecycle> {
168 let mut current = self.get();
169 loop {
170 let Some(next) = current.next(event) else {
171 return Err(current);
172 };
173 match self.0.compare_exchange(
174 current.to_u8(),
175 next.to_u8(),
176 Ordering::AcqRel,
177 Ordering::Acquire,
178 ) {
179 Ok(_) => return Ok(next),
180 Err(actual) => current = Lifecycle::from_u8(actual),
181 }
182 }
183 }
184}