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 {
234 descriptor: ServiceDescriptor,
235 start_action: Arc<StartAction>,
236 stop_action: Arc<StopAction>,
237 health_probe: Arc<HealthProbe>,
238 state: Mutex<CallbackState>,
239 lifecycle: Mutex<()>,
240}
241
242struct CallbackState {
243 health: ServiceHealth,
244 runtime_state: ServiceRuntimeState,
245}
246
247impl CallbackManagedService {
248 pub fn new<S, T, H>(
250 descriptor: ServiceDescriptor,
251 start_action: S,
252 stop_action: T,
253 health_probe: H,
254 ) -> Self
255 where
256 S: Fn() -> Result<(), String> + Send + Sync + 'static,
257 T: Fn(Duration) -> Result<(), String> + Send + Sync + 'static,
258 H: Fn() -> ServiceHealth + Send + Sync + 'static,
259 {
260 Self {
261 descriptor,
262 start_action: Arc::new(start_action),
263 stop_action: Arc::new(stop_action),
264 health_probe: Arc::new(health_probe),
265 lifecycle: Mutex::new(()),
266 state: Mutex::new(CallbackState {
267 health: ServiceHealth::Unknown,
268 runtime_state: ServiceRuntimeState::Stopped,
269 }),
270 }
271 }
272
273 fn set_state(
274 &self,
275 health: ServiceHealth,
276 runtime_state: ServiceRuntimeState,
277 ) -> SupervisorResult<()> {
278 let mut state = self
279 .state
280 .lock()
281 .map_err(|_| SupervisorError::StatePoisoned)?;
282 if state.runtime_state == ServiceRuntimeState::Orphaned {
283 return Err(SupervisorError::ServiceOrphaned(
284 self.descriptor.name().to_string(),
285 ));
286 }
287 state.health = health;
288 state.runtime_state = runtime_state;
289 Ok(())
290 }
291
292 fn acquire_lifecycle(&self) -> SupervisorResult<std::sync::MutexGuard<'_, ()>> {
293 self.lifecycle
295 .try_lock()
296 .map_err(|_| SupervisorError::ServiceFailure {
297 service: self.descriptor.name().to_string(),
298 reason: "lifecycle operation unavailable".to_string(),
299 })
300 }
301}
302
303impl ManagedService for CallbackManagedService {
304 fn descriptor(&self) -> &ServiceDescriptor {
305 &self.descriptor
306 }
307
308 fn start(&self) -> SupervisorResult<()> {
309 let _operation = self.acquire_lifecycle()?;
310 if self.descriptor.activation() != ServiceActivationState::Enabled {
311 return self.set_state(ServiceHealth::Unknown, ServiceRuntimeState::Stopped);
312 }
313 self.set_state(ServiceHealth::Starting, ServiceRuntimeState::Starting)?;
314 catch_unwind(AssertUnwindSafe(|| (self.start_action)()))
315 .map_err(|_| "managed start callback panicked".to_string())
316 .and_then(|result| result)
317 .map_err(|reason| {
318 let _ = self.set_state(ServiceHealth::Failed, ServiceRuntimeState::Failed);
319 SupervisorError::ServiceFailure {
320 service: self.descriptor.name().to_string(),
321 reason,
322 }
323 })?;
324 self.set_state(ServiceHealth::Ready, ServiceRuntimeState::Running)
325 }
326
327 fn stop(&self, timeout: Duration) -> SupervisorResult<()> {
328 let _operation = self.acquire_lifecycle()?;
329 self.set_state(ServiceHealth::Stopping, ServiceRuntimeState::Stopping)?;
330 catch_unwind(AssertUnwindSafe(|| (self.stop_action)(timeout)))
331 .map_err(|_| "managed stop callback panicked".to_string())
332 .and_then(|result| result)
333 .map_err(|reason| {
334 let _ = self.set_state(ServiceHealth::Failed, ServiceRuntimeState::Orphaned);
335 SupervisorError::ServiceFailure {
336 service: self.descriptor.name().to_string(),
337 reason,
338 }
339 })?;
340 self.set_state(ServiceHealth::Unknown, ServiceRuntimeState::Stopped)
341 }
342
343 fn health(&self) -> ServiceHealth {
344 let state = self
345 .state
346 .lock()
347 .map(|state| state.health)
348 .unwrap_or(ServiceHealth::Failed);
349 if matches!(
350 state,
351 ServiceHealth::Ready | ServiceHealth::Healthy | ServiceHealth::Degraded
352 ) {
353 return catch_unwind(AssertUnwindSafe(|| (self.health_probe)()))
354 .unwrap_or(ServiceHealth::Failed);
355 }
356 state
357 }
358
359 fn runtime_state(&self) -> ServiceRuntimeState {
360 self.state
361 .lock()
362 .map(|state| state.runtime_state)
363 .unwrap_or(ServiceRuntimeState::Failed)
364 }
365}
366
367pub struct PassiveManagedService {
369 descriptor: ServiceDescriptor,
370 state: Mutex<CallbackState>,
371}
372
373impl PassiveManagedService {
374 pub fn new(descriptor: ServiceDescriptor) -> Self {
376 Self {
377 descriptor,
378 state: Mutex::new(CallbackState {
379 health: ServiceHealth::Unknown,
380 runtime_state: ServiceRuntimeState::Stopped,
381 }),
382 }
383 }
384}
385
386impl ManagedService for PassiveManagedService {
387 fn descriptor(&self) -> &ServiceDescriptor {
388 &self.descriptor
389 }
390
391 fn start(&self) -> SupervisorResult<()> {
392 let mut state = self
393 .state
394 .lock()
395 .map_err(|_| SupervisorError::StatePoisoned)?;
396 if self.descriptor.activation() == ServiceActivationState::Enabled {
397 state.health = ServiceHealth::Healthy;
398 state.runtime_state = ServiceRuntimeState::Running;
399 } else {
400 state.health = ServiceHealth::Unknown;
401 state.runtime_state = ServiceRuntimeState::Stopped;
402 }
403 Ok(())
404 }
405
406 fn stop(&self, _timeout: Duration) -> SupervisorResult<()> {
407 let mut state = self
408 .state
409 .lock()
410 .map_err(|_| SupervisorError::StatePoisoned)?;
411 state.health = ServiceHealth::Unknown;
412 state.runtime_state = ServiceRuntimeState::Stopped;
413 Ok(())
414 }
415
416 fn health(&self) -> ServiceHealth {
417 self.state
418 .lock()
419 .map(|state| state.health)
420 .unwrap_or(ServiceHealth::Failed)
421 }
422
423 fn runtime_state(&self) -> ServiceRuntimeState {
424 self.state
425 .lock()
426 .map(|state| state.runtime_state)
427 .unwrap_or(ServiceRuntimeState::Failed)
428 }
429}