Skip to main content

driven/state/
atomic_sync.rs

1//! Atomic Synchronization
2//!
3//! Lock-free synchronization primitives for rule state.
4
5use std::sync::Arc;
6use std::sync::atomic::{AtomicBool, AtomicU64, Ordering};
7
8/// Sync state
9#[derive(Debug, Clone, Copy, PartialEq, Eq)]
10pub enum SyncState {
11    /// Idle, no sync in progress
12    Idle,
13    /// Syncing in progress
14    Syncing,
15    /// Sync completed successfully
16    Completed,
17    /// Sync failed
18    Failed,
19}
20
21/// Atomic sync controller
22#[derive(Debug)]
23pub struct AtomicSync {
24    /// Current state (encoded as u8)
25    state: AtomicU64,
26    /// Sync in progress flag
27    in_progress: AtomicBool,
28    /// Last sync timestamp
29    last_sync: AtomicU64,
30    /// Sync count
31    sync_count: AtomicU64,
32    /// Error count
33    error_count: AtomicU64,
34}
35
36impl AtomicSync {
37    /// Create a new sync controller
38    pub fn new() -> Self {
39        Self {
40            state: AtomicU64::new(SyncState::Idle as u64),
41            in_progress: AtomicBool::new(false),
42            last_sync: AtomicU64::new(0),
43            sync_count: AtomicU64::new(0),
44            error_count: AtomicU64::new(0),
45        }
46    }
47
48    /// Get current state
49    pub fn state(&self) -> SyncState {
50        match self.state.load(Ordering::SeqCst) {
51            0 => SyncState::Idle,
52            1 => SyncState::Syncing,
53            2 => SyncState::Completed,
54            _ => SyncState::Failed,
55        }
56    }
57
58    /// Try to start a sync (returns false if already syncing)
59    pub fn try_start(&self) -> bool {
60        if self
61            .in_progress
62            .compare_exchange(false, true, Ordering::SeqCst, Ordering::SeqCst)
63            .is_ok()
64        {
65            {
66                self.state
67                    .store(SyncState::Syncing as u64, Ordering::SeqCst);
68                true
69            }
70        } else {
71            false
72        }
73    }
74
75    /// Mark sync as completed
76    pub fn complete(&self) {
77        self.state
78            .store(SyncState::Completed as u64, Ordering::SeqCst);
79        self.in_progress.store(false, Ordering::SeqCst);
80        self.last_sync.store(
81            std::time::SystemTime::now()
82                .duration_since(std::time::UNIX_EPOCH)
83                .unwrap_or_default()
84                .as_secs(),
85            Ordering::SeqCst,
86        );
87        self.sync_count.fetch_add(1, Ordering::SeqCst);
88    }
89
90    /// Mark sync as failed
91    pub fn fail(&self) {
92        self.state.store(SyncState::Failed as u64, Ordering::SeqCst);
93        self.in_progress.store(false, Ordering::SeqCst);
94        self.error_count.fetch_add(1, Ordering::SeqCst);
95    }
96
97    /// Reset to idle
98    pub fn reset(&self) {
99        self.state.store(SyncState::Idle as u64, Ordering::SeqCst);
100        self.in_progress.store(false, Ordering::SeqCst);
101    }
102
103    /// Check if sync is in progress
104    pub fn is_syncing(&self) -> bool {
105        self.in_progress.load(Ordering::SeqCst)
106    }
107
108    /// Get last sync timestamp
109    pub fn last_sync_time(&self) -> u64 {
110        self.last_sync.load(Ordering::SeqCst)
111    }
112
113    /// Get sync count
114    pub fn sync_count(&self) -> u64 {
115        self.sync_count.load(Ordering::SeqCst)
116    }
117
118    /// Get error count
119    pub fn error_count(&self) -> u64 {
120        self.error_count.load(Ordering::SeqCst)
121    }
122
123    /// Get time since last sync
124    pub fn time_since_sync(&self) -> Option<u64> {
125        let last = self.last_sync.load(Ordering::SeqCst);
126        if last == 0 {
127            return None;
128        }
129
130        let now = std::time::SystemTime::now()
131            .duration_since(std::time::UNIX_EPOCH)
132            .unwrap_or_default()
133            .as_secs();
134
135        Some(now.saturating_sub(last))
136    }
137}
138
139impl Default for AtomicSync {
140    fn default() -> Self {
141        Self::new()
142    }
143}
144
145/// Sync guard that auto-completes or fails on drop
146pub struct SyncGuard<'a> {
147    sync: &'a AtomicSync,
148    completed: bool,
149}
150
151impl<'a> SyncGuard<'a> {
152    /// Create a new sync guard
153    pub fn new(sync: &'a AtomicSync) -> Option<Self> {
154        if sync.try_start() {
155            Some(Self {
156                sync,
157                completed: false,
158            })
159        } else {
160            None
161        }
162    }
163
164    /// Mark sync as completed
165    pub fn complete(mut self) {
166        self.completed = true;
167        self.sync.complete();
168    }
169
170    /// Mark sync as failed
171    pub fn fail(mut self) {
172        self.completed = true;
173        self.sync.fail();
174    }
175}
176
177impl<'a> Drop for SyncGuard<'a> {
178    fn drop(&mut self) {
179        if !self.completed {
180            self.sync.fail();
181        }
182    }
183}
184
185/// Thread-safe sync handle
186pub type AtomicSyncHandle = Arc<AtomicSync>;
187
188/// Create a new sync handle
189pub fn create_sync_handle() -> AtomicSyncHandle {
190    Arc::new(AtomicSync::new())
191}
192
193#[cfg(test)]
194mod tests {
195    use super::*;
196
197    #[test]
198    fn test_sync_lifecycle() {
199        let sync = AtomicSync::new();
200
201        assert_eq!(sync.state(), SyncState::Idle);
202        assert!(!sync.is_syncing());
203
204        assert!(sync.try_start());
205        assert_eq!(sync.state(), SyncState::Syncing);
206        assert!(sync.is_syncing());
207
208        // Can't start while syncing
209        assert!(!sync.try_start());
210
211        sync.complete();
212        assert_eq!(sync.state(), SyncState::Completed);
213        assert!(!sync.is_syncing());
214        assert_eq!(sync.sync_count(), 1);
215    }
216
217    #[test]
218    fn test_sync_guard() {
219        let sync = AtomicSync::new();
220
221        {
222            let guard = SyncGuard::new(&sync).unwrap();
223            assert!(sync.is_syncing());
224            guard.complete();
225        }
226
227        assert_eq!(sync.state(), SyncState::Completed);
228    }
229
230    #[test]
231    fn test_sync_guard_auto_fail() {
232        let sync = AtomicSync::new();
233
234        {
235            let _guard = SyncGuard::new(&sync).unwrap();
236            assert!(sync.is_syncing());
237            // Guard dropped without complete()
238        }
239
240        assert_eq!(sync.state(), SyncState::Failed);
241        assert_eq!(sync.error_count(), 1);
242    }
243
244    #[test]
245    fn test_thread_safety() {
246        let sync = Arc::new(AtomicSync::new());
247
248        let handles: Vec<_> = (0..10)
249            .map(|_| {
250                let sync = sync.clone();
251                std::thread::spawn(move || {
252                    if sync.try_start() {
253                        std::thread::sleep(std::time::Duration::from_millis(1));
254                        sync.complete();
255                        true
256                    } else {
257                        false
258                    }
259                })
260            })
261            .collect();
262
263        let results: Vec<_> = handles.into_iter().map(|h| h.join().unwrap()).collect();
264        let successes: usize = results.into_iter().filter(|&b| b).count();
265
266        // Only one thread should have succeeded at a time
267        assert!(successes >= 1);
268    }
269}