1use crate::{
14 ManagedService, ServiceActivationState, ServiceDescriptor, ServiceHealth, ServiceRuntimeState,
15 SupervisorError, SupervisorResult,
16};
17use std::panic::{catch_unwind, AssertUnwindSafe};
18use std::sync::atomic::{AtomicBool, Ordering};
19use std::sync::{Arc, Mutex};
20use std::thread::JoinHandle;
21use std::time::{Duration, Instant};
22
23type ThreadFactory =
24 dyn Fn(Arc<AtomicBool>) -> Result<JoinHandle<Result<(), String>>, String> + Send + Sync;
25type StartAction = dyn Fn() -> Result<(), String> + Send + Sync;
26type StopAction = dyn Fn(Duration) -> Result<(), String> + Send + Sync;
27type HealthProbe = dyn Fn() -> ServiceHealth + Send + Sync;
28
29struct ThreadRuntime {
30 shutdown: Arc<AtomicBool>,
31 handle: JoinHandle<Result<(), String>>,
32}
33
34struct ThreadState {
35 health: ServiceHealth,
36 runtime_state: ServiceRuntimeState,
37 runtime: Option<ThreadRuntime>,
38}
39
40pub struct ManagedThreadService {
42 descriptor: ServiceDescriptor,
43 factory: Arc<ThreadFactory>,
44 health_probe: Option<Arc<HealthProbe>>,
45 state: Mutex<ThreadState>,
46}
47
48impl ManagedThreadService {
49 pub fn new<F>(descriptor: ServiceDescriptor, factory: F) -> Self
51 where
52 F: Fn(Arc<AtomicBool>) -> Result<JoinHandle<Result<(), String>>, String>
53 + Send
54 + Sync
55 + 'static,
56 {
57 Self {
58 descriptor,
59 factory: Arc::new(factory),
60 health_probe: None,
61 state: Mutex::new(ThreadState {
62 health: ServiceHealth::Unknown,
63 runtime_state: ServiceRuntimeState::Stopped,
64 runtime: None,
65 }),
66 }
67 }
68
69 pub fn with_health_probe<H>(mut self, health_probe: H) -> Self
71 where
72 H: Fn() -> ServiceHealth + Send + Sync + 'static,
73 {
74 self.health_probe = Some(Arc::new(health_probe));
75 self
76 }
77
78 fn refresh(state: &mut ThreadState) {
79 let finished = state
80 .runtime
81 .as_ref()
82 .is_some_and(|runtime| runtime.handle.is_finished());
83 if !finished {
84 return;
85 }
86 let Some(runtime) = state.runtime.take() else {
87 return;
88 };
89 (state.health, state.runtime_state) = if runtime.shutdown.load(Ordering::Acquire) {
90 (ServiceHealth::Unknown, ServiceRuntimeState::Stopped)
91 } else {
92 (ServiceHealth::Failed, ServiceRuntimeState::Failed)
93 };
94 let _ = runtime.handle.join();
95 }
96}
97
98impl ManagedService for ManagedThreadService {
99 fn descriptor(&self) -> &ServiceDescriptor {
100 &self.descriptor
101 }
102
103 fn start(&self) -> SupervisorResult<()> {
104 let mut state = self
105 .state
106 .lock()
107 .map_err(|_| SupervisorError::StatePoisoned)?;
108 Self::refresh(&mut state);
109 if state.runtime_state == ServiceRuntimeState::Orphaned {
110 return Err(SupervisorError::ServiceOrphaned(
111 self.descriptor.name().to_string(),
112 ));
113 }
114 if state.runtime.is_some() {
115 return Ok(());
116 }
117 if self.descriptor.activation() != ServiceActivationState::Enabled {
118 state.health = ServiceHealth::Unknown;
119 state.runtime_state = ServiceRuntimeState::Stopped;
120 return Ok(());
121 }
122 state.health = ServiceHealth::Starting;
123 state.runtime_state = ServiceRuntimeState::Starting;
124 let shutdown = Arc::new(AtomicBool::new(false));
125 let handle = catch_unwind(AssertUnwindSafe(|| (self.factory)(Arc::clone(&shutdown))))
126 .map_err(|_| "managed thread factory panicked".to_string())
127 .and_then(|result| result)
128 .map_err(|reason| {
129 state.health = ServiceHealth::Failed;
130 state.runtime_state = ServiceRuntimeState::Failed;
131 SupervisorError::ServiceFailure {
132 service: self.descriptor.name().to_string(),
133 reason,
134 }
135 })?;
136 state.runtime = Some(ThreadRuntime { shutdown, handle });
137 state.health = ServiceHealth::Ready;
138 state.runtime_state = ServiceRuntimeState::Running;
139 Ok(())
140 }
141
142 fn stop(&self, timeout: Duration) -> SupervisorResult<()> {
143 let runtime = {
144 let mut state = self
145 .state
146 .lock()
147 .map_err(|_| SupervisorError::StatePoisoned)?;
148 Self::refresh(&mut state);
149 if state.runtime_state == ServiceRuntimeState::Orphaned {
150 return Err(SupervisorError::ServiceOrphaned(
151 self.descriptor.name().to_string(),
152 ));
153 }
154 state.health = ServiceHealth::Stopping;
155 state.runtime_state = ServiceRuntimeState::StopRequested;
156 let Some(runtime) = state.runtime.take() else {
157 state.health = ServiceHealth::Unknown;
158 state.runtime_state = ServiceRuntimeState::Stopped;
159 return Ok(());
160 };
161 runtime.shutdown.store(true, Ordering::Release);
162 state.runtime_state = ServiceRuntimeState::Stopping;
163 runtime
164 };
165 let deadline = Instant::now().checked_add(timeout);
166 while !runtime.handle.is_finished()
167 && deadline.is_none_or(|deadline| Instant::now() < deadline)
168 {
169 std::thread::sleep(Duration::from_millis(10));
170 }
171 if !runtime.handle.is_finished() {
172 let mut state = self
173 .state
174 .lock()
175 .map_err(|_| SupervisorError::StatePoisoned)?;
176 state.runtime = Some(runtime);
177 state.health = ServiceHealth::Failed;
178 state.runtime_state = ServiceRuntimeState::Orphaned;
179 return Err(SupervisorError::ShutdownTimeout(
180 self.descriptor.name().to_string(),
181 ));
182 }
183 let result = runtime
184 .handle
185 .join()
186 .map_err(|_| SupervisorError::ServiceFailure {
187 service: self.descriptor.name().to_string(),
188 reason: "managed thread panicked".to_string(),
189 })?;
190 let mut state = self
191 .state
192 .lock()
193 .map_err(|_| SupervisorError::StatePoisoned)?;
194 state.health = ServiceHealth::Unknown;
195 state.runtime_state = ServiceRuntimeState::Stopped;
196 result.map_err(|reason| SupervisorError::ServiceFailure {
197 service: self.descriptor.name().to_string(),
198 reason,
199 })
200 }
201
202 fn health(&self) -> ServiceHealth {
203 let Ok(mut state) = self.state.lock() else {
204 return ServiceHealth::Failed;
205 };
206 Self::refresh(&mut state);
207 if matches!(state.health, ServiceHealth::Ready | ServiceHealth::Healthy) {
208 return self
209 .health_probe
210 .as_ref()
211 .map(|probe| {
212 catch_unwind(AssertUnwindSafe(|| probe())).unwrap_or(ServiceHealth::Failed)
213 })
214 .unwrap_or(state.health);
215 }
216 state.health
217 }
218
219 fn runtime_state(&self) -> ServiceRuntimeState {
220 let Ok(mut state) = self.state.lock() else {
221 return ServiceRuntimeState::Failed;
222 };
223 Self::refresh(&mut state);
224 state.runtime_state
225 }
226}
227
228pub struct CallbackManagedService {
230 descriptor: ServiceDescriptor,
231 start_action: Arc<StartAction>,
232 stop_action: Arc<StopAction>,
233 health_probe: Arc<HealthProbe>,
234 state: Mutex<CallbackState>,
235}
236
237struct CallbackState {
238 health: ServiceHealth,
239 runtime_state: ServiceRuntimeState,
240}
241
242impl CallbackManagedService {
243 pub fn new<S, T, H>(
245 descriptor: ServiceDescriptor,
246 start_action: S,
247 stop_action: T,
248 health_probe: H,
249 ) -> Self
250 where
251 S: Fn() -> Result<(), String> + Send + Sync + 'static,
252 T: Fn(Duration) -> Result<(), String> + Send + Sync + 'static,
253 H: Fn() -> ServiceHealth + Send + Sync + 'static,
254 {
255 Self {
256 descriptor,
257 start_action: Arc::new(start_action),
258 stop_action: Arc::new(stop_action),
259 health_probe: Arc::new(health_probe),
260 state: Mutex::new(CallbackState {
261 health: ServiceHealth::Unknown,
262 runtime_state: ServiceRuntimeState::Stopped,
263 }),
264 }
265 }
266
267 fn set_state(
268 &self,
269 health: ServiceHealth,
270 runtime_state: ServiceRuntimeState,
271 ) -> SupervisorResult<()> {
272 let mut state = self
273 .state
274 .lock()
275 .map_err(|_| SupervisorError::StatePoisoned)?;
276 state.health = health;
277 state.runtime_state = runtime_state;
278 Ok(())
279 }
280}
281
282impl ManagedService for CallbackManagedService {
283 fn descriptor(&self) -> &ServiceDescriptor {
284 &self.descriptor
285 }
286
287 fn start(&self) -> SupervisorResult<()> {
288 if self.descriptor.activation() != ServiceActivationState::Enabled {
289 return self.set_state(ServiceHealth::Unknown, ServiceRuntimeState::Stopped);
290 }
291 self.set_state(ServiceHealth::Starting, ServiceRuntimeState::Starting)?;
292 catch_unwind(AssertUnwindSafe(|| (self.start_action)()))
293 .map_err(|_| "managed start callback panicked".to_string())
294 .and_then(|result| result)
295 .map_err(|reason| {
296 let _ = self.set_state(ServiceHealth::Failed, ServiceRuntimeState::Failed);
297 SupervisorError::ServiceFailure {
298 service: self.descriptor.name().to_string(),
299 reason,
300 }
301 })?;
302 self.set_state(ServiceHealth::Ready, ServiceRuntimeState::Running)
303 }
304
305 fn stop(&self, timeout: Duration) -> SupervisorResult<()> {
306 self.set_state(ServiceHealth::Stopping, ServiceRuntimeState::Stopping)?;
307 catch_unwind(AssertUnwindSafe(|| (self.stop_action)(timeout)))
308 .map_err(|_| "managed stop callback panicked".to_string())
309 .and_then(|result| result)
310 .map_err(|reason| {
311 let _ = self.set_state(ServiceHealth::Failed, ServiceRuntimeState::Failed);
312 SupervisorError::ServiceFailure {
313 service: self.descriptor.name().to_string(),
314 reason,
315 }
316 })?;
317 self.set_state(ServiceHealth::Unknown, ServiceRuntimeState::Stopped)
318 }
319
320 fn health(&self) -> ServiceHealth {
321 let state = self
322 .state
323 .lock()
324 .map(|state| state.health)
325 .unwrap_or(ServiceHealth::Failed);
326 if matches!(
327 state,
328 ServiceHealth::Ready | ServiceHealth::Healthy | ServiceHealth::Degraded
329 ) {
330 return catch_unwind(AssertUnwindSafe(|| (self.health_probe)()))
331 .unwrap_or(ServiceHealth::Failed);
332 }
333 state
334 }
335
336 fn runtime_state(&self) -> ServiceRuntimeState {
337 self.state
338 .lock()
339 .map(|state| state.runtime_state)
340 .unwrap_or(ServiceRuntimeState::Failed)
341 }
342}
343
344pub struct PassiveManagedService {
346 descriptor: ServiceDescriptor,
347 state: Mutex<CallbackState>,
348}
349
350impl PassiveManagedService {
351 pub fn new(descriptor: ServiceDescriptor) -> Self {
353 Self {
354 descriptor,
355 state: Mutex::new(CallbackState {
356 health: ServiceHealth::Unknown,
357 runtime_state: ServiceRuntimeState::Stopped,
358 }),
359 }
360 }
361}
362
363impl ManagedService for PassiveManagedService {
364 fn descriptor(&self) -> &ServiceDescriptor {
365 &self.descriptor
366 }
367
368 fn start(&self) -> SupervisorResult<()> {
369 let mut state = self
370 .state
371 .lock()
372 .map_err(|_| SupervisorError::StatePoisoned)?;
373 if self.descriptor.activation() == ServiceActivationState::Enabled {
374 state.health = ServiceHealth::Healthy;
375 state.runtime_state = ServiceRuntimeState::Running;
376 } else {
377 state.health = ServiceHealth::Unknown;
378 state.runtime_state = ServiceRuntimeState::Stopped;
379 }
380 Ok(())
381 }
382
383 fn stop(&self, _timeout: Duration) -> SupervisorResult<()> {
384 let mut state = self
385 .state
386 .lock()
387 .map_err(|_| SupervisorError::StatePoisoned)?;
388 state.health = ServiceHealth::Unknown;
389 state.runtime_state = ServiceRuntimeState::Stopped;
390 Ok(())
391 }
392
393 fn health(&self) -> ServiceHealth {
394 self.state
395 .lock()
396 .map(|state| state.health)
397 .unwrap_or(ServiceHealth::Failed)
398 }
399
400 fn runtime_state(&self) -> ServiceRuntimeState {
401 self.state
402 .lock()
403 .map(|state| state.runtime_state)
404 .unwrap_or(ServiceRuntimeState::Failed)
405 }
406}