memscope_rs/core/
safe_operations.rs

1//! Safe Operations - Provides safe lock operations
2//!
3//! This module provides safe lock operations that replace
4//! dangerous .lock().expect("Failed to acquire lock") calls throughout the codebase.
5
6use crate::core::types::TrackingResult;
7use std::sync::{Mutex, RwLock};
8
9/// Safe lock operations - replaces .lock().expect("Failed to acquire lock")
10pub trait SafeLock<T> {
11    /// Safely acquire lock with timeout and error handling
12    fn safe_lock(&self) -> TrackingResult<std::sync::MutexGuard<'_, T>>;
13
14    /// Try to acquire lock without blocking
15    fn try_safe_lock(&self) -> TrackingResult<Option<std::sync::MutexGuard<'_, T>>>;
16}
17
18impl<T> SafeLock<T> for Mutex<T> {
19    fn safe_lock(&self) -> TrackingResult<std::sync::MutexGuard<'_, T>> {
20        self.lock().map_err(|e| {
21            crate::core::types::TrackingError::LockError(format!(
22                "Failed to acquire mutex lock: {e}",
23            ))
24        })
25    }
26
27    fn try_safe_lock(&self) -> TrackingResult<Option<std::sync::MutexGuard<'_, T>>> {
28        match self.try_lock() {
29            Ok(guard) => Ok(Some(guard)),
30            Err(std::sync::TryLockError::WouldBlock) => Ok(None),
31            Err(std::sync::TryLockError::Poisoned(e)) => Err(
32                crate::core::types::TrackingError::LockError(format!("Mutex poisoned: {e}")),
33            ),
34        }
35    }
36}
37
38/// Safe RwLock operations
39pub trait SafeRwLock<T> {
40    /// Safely acquire read lock
41    fn safe_read(&self) -> TrackingResult<std::sync::RwLockReadGuard<'_, T>>;
42
43    /// Safely acquire write lock
44    fn safe_write(&self) -> TrackingResult<std::sync::RwLockWriteGuard<'_, T>>;
45}
46
47impl<T> SafeRwLock<T> for RwLock<T> {
48    fn safe_read(&self) -> TrackingResult<std::sync::RwLockReadGuard<'_, T>> {
49        self.read().map_err(|e| {
50            crate::core::types::TrackingError::LockError(format!(
51                "Failed to acquire read lock: {e}",
52            ))
53        })
54    }
55
56    fn safe_write(&self) -> TrackingResult<std::sync::RwLockWriteGuard<'_, T>> {
57        self.write().map_err(|e| {
58            crate::core::types::TrackingError::LockError(format!(
59                "Failed to acquire write lock: {e}",
60            ))
61        })
62    }
63}
64
65/// Macro for safe lock acquisition
66#[macro_export]
67macro_rules! safe_lock {
68    ($mutex:expr) => {
69        $mutex.safe_lock()?
70    };
71}
72
73#[cfg(test)]
74mod tests {
75    use super::*;
76    use std::sync::{Arc, Mutex, RwLock};
77    use std::thread;
78    use std::time::Duration;
79
80    #[test]
81    fn test_safe_mutex_lock() {
82        let mutex = Mutex::new(42);
83
84        let guard = mutex.safe_lock().unwrap();
85        assert_eq!(*guard, 42);
86    }
87
88    #[test]
89    fn test_safe_mutex_try_lock() {
90        let mutex = Mutex::new(42);
91
92        let guard = mutex.try_safe_lock().unwrap();
93        assert!(guard.is_some());
94        assert_eq!(*guard.unwrap(), 42);
95    }
96
97    #[test]
98    fn test_safe_mutex_try_lock_would_block() {
99        let mutex = Arc::new(Mutex::new(42));
100        let mutex_clone = Arc::clone(&mutex);
101
102        let _guard = mutex.safe_lock().unwrap();
103
104        // Try to lock from another context - should return None (would block)
105        let handle = thread::spawn(move || {
106            let result = mutex_clone.try_safe_lock().unwrap();
107            result.is_none()
108        });
109
110        assert!(handle.join().unwrap());
111    }
112
113    #[test]
114    fn test_safe_rwlock_read() {
115        let rwlock = RwLock::new(42);
116
117        let guard = rwlock.safe_read().unwrap();
118        assert_eq!(*guard, 42);
119    }
120
121    #[test]
122    fn test_safe_rwlock_write() {
123        let rwlock = RwLock::new(42);
124
125        let mut guard = rwlock.safe_write().unwrap();
126        *guard = 100;
127        drop(guard);
128
129        let guard = rwlock.safe_read().unwrap();
130        assert_eq!(*guard, 100);
131    }
132
133    #[test]
134    fn test_safe_rwlock_multiple_readers() {
135        let rwlock = Arc::new(RwLock::new(42));
136        let mut handles = vec![];
137
138        // Multiple readers should be able to acquire locks simultaneously
139        for _ in 0..5 {
140            let rwlock_clone = Arc::clone(&rwlock);
141            let handle = thread::spawn(move || {
142                let guard = rwlock_clone.safe_read().unwrap();
143                assert_eq!(*guard, 42);
144                thread::sleep(Duration::from_millis(10));
145            });
146            handles.push(handle);
147        }
148
149        for handle in handles {
150            handle.join().unwrap();
151        }
152    }
153
154    #[test]
155    fn test_safe_rwlock_writer_exclusivity() {
156        let rwlock = Arc::new(RwLock::new(0));
157        let rwlock_clone = Arc::clone(&rwlock);
158
159        let handle = thread::spawn(move || {
160            let mut guard = rwlock_clone.safe_write().unwrap();
161            *guard = 42;
162            thread::sleep(Duration::from_millis(50));
163            *guard = 100;
164        });
165
166        // Give the writer thread time to acquire the lock
167        thread::sleep(Duration::from_millis(10));
168
169        // This read should wait for the writer to finish
170        let guard = rwlock.safe_read().unwrap();
171        assert_eq!(*guard, 100);
172
173        handle.join().unwrap();
174    }
175
176    #[test]
177    fn test_concurrent_safe_operations() {
178        let mutex = Arc::new(Mutex::new(0));
179        let mut handles = vec![];
180
181        // Multiple threads incrementing safely
182        for _ in 0..10 {
183            let mutex_clone = Arc::clone(&mutex);
184            let handle = thread::spawn(move || {
185                let mut guard = mutex_clone.safe_lock().unwrap();
186                *guard += 1;
187            });
188            handles.push(handle);
189        }
190
191        for handle in handles {
192            handle.join().unwrap();
193        }
194
195        let guard = mutex.safe_lock().unwrap();
196        assert_eq!(*guard, 10);
197    }
198
199    #[test]
200    fn test_safe_lock_macro() {
201        use crate::safe_lock;
202
203        let mutex = Mutex::new(42);
204
205        // Test the macro - this should compile and work
206        let result: Result<(), crate::core::types::TrackingError> = (|| {
207            let guard = safe_lock!(mutex);
208            assert_eq!(*guard, 42);
209            Ok(())
210        })();
211
212        assert!(result.is_ok());
213    }
214
215    #[test]
216    fn test_error_handling() {
217        let mutex = Mutex::new(42);
218
219        // Test that errors are properly wrapped
220        let result = mutex.safe_lock();
221        assert!(result.is_ok());
222
223        let try_result = mutex.try_safe_lock();
224        assert!(try_result.is_ok());
225    }
226}