Skip to main content

grite_daemon/
state.rs

1//! Supervisor and Worker state machine definitions
2
3use std::sync::atomic::{AtomicU8, Ordering};
4
5/// Lifecycle states for the Supervisor.
6#[derive(Debug, Clone, Copy, PartialEq, Eq)]
7#[repr(u8)]
8pub enum SupervisorState {
9    /// Initial state before the accept loop begins
10    Starting = 0,
11    /// Accepting connections and routing requests
12    Running = 1,
13    /// Shutdown signal received, draining connections
14    ShuttingDown = 2,
15    /// All workers stopped, cleanup complete
16    Stopped = 3,
17}
18
19impl SupervisorState {
20    /// Valid state transitions.
21    pub fn transition(self, next: SupervisorState) -> Result<SupervisorState, crate::DaemonError> {
22        let valid = matches!(
23            (self, next),
24            (SupervisorState::Starting, SupervisorState::Running)
25                | (SupervisorState::Starting, SupervisorState::ShuttingDown)
26                | (SupervisorState::Running, SupervisorState::ShuttingDown)
27                | (SupervisorState::ShuttingDown, SupervisorState::Stopped)
28        );
29        if !valid {
30            return Err(crate::DaemonError::InvalidStateTransition {
31                from: format!("{:?}", self),
32                to: format!("{:?}", next),
33            });
34        }
35        Ok(next)
36    }
37}
38
39/// Atomic storage for SupervisorState using AtomicU8.
40pub struct AtomicSupervisorState {
41    inner: AtomicU8,
42}
43
44impl AtomicSupervisorState {
45    pub fn new(state: SupervisorState) -> Self {
46        Self {
47            inner: AtomicU8::new(state as u8),
48        }
49    }
50
51    pub fn load(&self, ordering: Ordering) -> SupervisorState {
52        match self.inner.load(ordering) {
53            0 => SupervisorState::Starting,
54            1 => SupervisorState::Running,
55            2 => SupervisorState::ShuttingDown,
56            _ => SupervisorState::Stopped,
57        }
58    }
59
60    pub fn store(&self, state: SupervisorState, ordering: Ordering) {
61        self.inner.store(state as u8, ordering);
62    }
63
64    /// Attempt a transition, returning the new state or an error.
65    pub fn transition(
66        &self,
67        next: SupervisorState,
68        ordering: Ordering,
69    ) -> Result<SupervisorState, crate::DaemonError> {
70        let current = self.load(ordering);
71        let result = current.transition(next)?;
72        self.store(result, ordering);
73        Ok(result)
74    }
75}
76
77/// Lifecycle states for a Worker.
78#[derive(Debug, Clone, Copy, PartialEq, Eq)]
79#[repr(u8)]
80pub enum WorkerState {
81    /// Opening sled store and WAL
82    Initializing = 0,
83    /// Waiting for commands
84    Idle = 1,
85    /// Processing one or more commands
86    Busy = 2,
87    /// Shutdown message received, finishing in-flight work
88    ShuttingDown = 3,
89    /// Event loop exited, resources released
90    Stopped = 4,
91}
92
93/// Atomic storage for WorkerState using AtomicU8.
94pub struct AtomicWorkerState {
95    inner: AtomicU8,
96}
97
98impl AtomicWorkerState {
99    pub fn new(state: WorkerState) -> Self {
100        Self {
101            inner: AtomicU8::new(state as u8),
102        }
103    }
104
105    pub fn load(&self, ordering: Ordering) -> WorkerState {
106        match self.inner.load(ordering) {
107            0 => WorkerState::Initializing,
108            1 => WorkerState::Idle,
109            2 => WorkerState::Busy,
110            3 => WorkerState::ShuttingDown,
111            _ => WorkerState::Stopped,
112        }
113    }
114
115    pub fn store(&self, state: WorkerState, ordering: Ordering) {
116        self.inner.store(state as u8, ordering);
117    }
118}