rustfs_common/
readiness.rs1use std::sync::atomic::{AtomicU8, Ordering};
16
17#[repr(u8)]
19pub enum SystemStage {
20 Booting = 0,
21 StorageReady = 1, IamReady = 2, FullReady = 3, }
25
26pub struct GlobalReadiness {
30 status: AtomicU8,
31}
32
33impl Default for GlobalReadiness {
34 fn default() -> Self {
35 Self::new()
36 }
37}
38
39impl GlobalReadiness {
40 pub fn new() -> Self {
44 Self {
45 status: AtomicU8::new(SystemStage::Booting as u8),
46 }
47 }
48
49 pub fn mark_stage(&self, step: SystemStage) {
54 self.status.fetch_max(step as u8, Ordering::SeqCst);
55 }
56
57 pub fn is_ready(&self) -> bool {
61 self.status.load(Ordering::SeqCst) == SystemStage::FullReady as u8
62 }
63
64 pub fn current_stage(&self) -> SystemStage {
68 match self.status.load(Ordering::SeqCst) {
69 0 => SystemStage::Booting,
70 1 => SystemStage::StorageReady,
71 2 => SystemStage::IamReady,
72 3 => SystemStage::FullReady,
73 invalid => {
74 debug_assert!(false, "GlobalReadiness::current_stage: invalid status value {}", invalid);
75 SystemStage::Booting
77 }
78 }
79 }
80}
81
82#[cfg(test)]
83mod tests {
84 use super::*;
85 use std::sync::Arc;
86 use std::thread;
87
88 #[test]
89 fn test_initial_state() {
90 let readiness = GlobalReadiness::new();
91 assert!(!readiness.is_ready());
92 assert_eq!(readiness.status.load(Ordering::SeqCst), SystemStage::Booting as u8);
93 }
94
95 #[test]
96 fn test_mark_stage_progression() {
97 let readiness = GlobalReadiness::new();
98 readiness.mark_stage(SystemStage::StorageReady);
99 assert!(!readiness.is_ready());
100 assert_eq!(readiness.status.load(Ordering::SeqCst), SystemStage::StorageReady as u8);
101
102 readiness.mark_stage(SystemStage::IamReady);
103 assert!(!readiness.is_ready());
104 assert_eq!(readiness.status.load(Ordering::SeqCst), SystemStage::IamReady as u8);
105
106 readiness.mark_stage(SystemStage::FullReady);
107 assert!(readiness.is_ready());
108 }
109
110 #[test]
111 fn test_no_regression() {
112 let readiness = GlobalReadiness::new();
113 readiness.mark_stage(SystemStage::FullReady);
114 readiness.mark_stage(SystemStage::IamReady); assert!(readiness.is_ready());
116 }
117
118 #[test]
119 fn test_concurrent_marking() {
120 let readiness = Arc::new(GlobalReadiness::new());
121 let mut handles = vec![];
122
123 for _ in 0..10 {
124 let r = Arc::clone(&readiness);
125 handles.push(thread::spawn(move || {
126 r.mark_stage(SystemStage::StorageReady);
127 r.mark_stage(SystemStage::IamReady);
128 r.mark_stage(SystemStage::FullReady);
129 }));
130 }
131
132 for h in handles {
133 h.join().unwrap();
134 }
135
136 assert!(readiness.is_ready());
137 }
138
139 #[test]
140 fn test_is_ready_only_at_full_ready() {
141 let readiness = GlobalReadiness::new();
142 assert!(!readiness.is_ready());
143
144 readiness.mark_stage(SystemStage::StorageReady);
145 assert!(!readiness.is_ready());
146
147 readiness.mark_stage(SystemStage::IamReady);
148 assert!(!readiness.is_ready());
149
150 readiness.mark_stage(SystemStage::FullReady);
151 assert!(readiness.is_ready());
152 }
153}