rust_memory_safety_examples/
data_race_prevention.rs

1//! Data race prevention through Send/Sync traits
2
3use std::sync::{Arc, Mutex, RwLock};
4use std::thread;
5
6/// VULNERABLE C CODE (for comparison):
7/// ```c
8/// #include <pthread.h>
9/// #include <stdio.h>
10///
11/// int counter = 0;  // Shared mutable state
12///
13/// void* increment(void* arg) {
14///     for (int i = 0; i < 100000; i++) {
15///         counter++;  // DATA RACE!
16///     }
17///     return NULL;
18/// }
19///
20/// int main() {
21///     pthread_t t1, t2;
22///     pthread_create(&t1, NULL, increment, NULL);
23///     pthread_create(&t2, NULL, increment, NULL);
24///     pthread_join(t1, NULL);
25///     pthread_join(t2, NULL);
26///     printf("Counter: %d\n", counter);  // Undefined result!
27///     return 0;
28/// }
29/// ```
30///
31/// **Vulnerability:** Concurrent access to shared mutable data causes:
32/// - Data corruption
33/// - Undefined behavior
34/// - Non-deterministic bugs
35/// - Security vulnerabilities
36///
37
38/// SAFE RUST EQUIVALENT:
39/// Rust prevents data races at compile time through the type system
40
41/// Example 1: Mutex protects shared mutable state
42pub fn safe_concurrent_counter() {
43    let counter = Arc::new(Mutex::new(0));
44    let mut handles = vec![];
45
46    for _ in 0..10 {
47        let counter_clone = Arc::clone(&counter);
48        let handle = thread::spawn(move || {
49            for _ in 0..1000 {
50                let mut num = counter_clone.lock().unwrap();
51                *num += 1;
52            }
53        });
54        handles.push(handle);
55    }
56
57    for handle in handles {
58        handle.join().unwrap();
59    }
60
61    println!("Final counter value: {}", *counter.lock().unwrap());
62    // Always produces correct result: 10,000
63}
64
65/// Example 2: RwLock for read-heavy workloads
66pub fn safe_read_write_access() {
67    let data = Arc::new(RwLock::new(vec![1, 2, 3]));
68    let mut handles = vec![];
69
70    // Multiple readers (allowed concurrently)
71    for i in 0..5 {
72        let data_clone = Arc::clone(&data);
73        let handle = thread::spawn(move || {
74            let read_guard = data_clone.read().unwrap();
75            println!("Reader {}: {:?}", i, *read_guard);
76        });
77        handles.push(handle);
78    }
79
80    // Single writer (exclusive access)
81    let data_clone = Arc::clone(&data);
82    let writer_handle = thread::spawn(move || {
83        let mut write_guard = data_clone.write().unwrap();
84        write_guard.push(4);
85        println!("Writer added element");
86    });
87    handles.push(writer_handle);
88
89    for handle in handles {
90        handle.join().unwrap();
91    }
92}
93
94/// Example 3: Demonstrating Send and Sync traits
95/// Send: Safe to transfer ownership between threads
96/// Sync: Safe to share references between threads
97
98/// This type is Send (can be moved to another thread)
99struct SendableData {
100    value: i32,
101}
102
103/// This type is NOT Send due to raw pointer
104struct NotSendable {
105    ptr: *mut i32,  // Raw pointers are not Send
106}
107
108/// Compiler enforces thread safety
109pub fn thread_safety_enforced() {
110    let sendable = SendableData { value: 42 };
111
112    // This works: SendableData is Send
113    thread::spawn(move || {
114        println!("Value in thread: {}", sendable.value);
115    });
116
117    // This would NOT compile:
118    // let not_sendable = NotSendable { ptr: std::ptr::null_mut() };
119    // thread::spawn(move || {
120    //     // Compile error: NotSendable is not Send!
121    // });
122}
123
124/// Example 4: Message passing (alternative to shared state)
125use std::sync::mpsc;
126
127pub fn safe_message_passing() {
128    let (tx, rx) = mpsc::channel();
129
130    thread::spawn(move || {
131        for i in 0..5 {
132            tx.send(i).unwrap();
133        }
134    });
135
136    for received in rx {
137        println!("Received: {}", received);
138    }
139}
140
141/// Example 5: Atomic types for lock-free programming
142use std::sync::atomic::{AtomicUsize, Ordering};
143
144pub fn safe_atomic_operations() {
145    let counter = Arc::new(AtomicUsize::new(0));
146    let mut handles = vec![];
147
148    for _ in 0..10 {
149        let counter_clone = Arc::clone(&counter);
150        let handle = thread::spawn(move || {
151            for _ in 0..1000 {
152                counter_clone.fetch_add(1, Ordering::SeqCst);
153            }
154        });
155        handles.push(handle);
156    }
157
158    for handle in handles {
159        handle.join().unwrap();
160    }
161
162    println!("Atomic counter: {}", counter.load(Ordering::SeqCst));
163}
164
165/// Example 6: Scoped threads for guaranteed lifetime
166pub fn scoped_threads_safe() {
167    let mut data = vec![1, 2, 3];
168
169    thread::scope(|s| {
170        s.spawn(|| {
171            // Can borrow data because scope guarantees lifetime
172            println!("Data length: {}", data.len());
173        });
174
175        s.spawn(|| {
176            println!("Data: {:?}", data);
177        });
178    });
179
180    // All spawned threads are joined before scope ends
181    data.push(4);  // Safe to modify again
182}
183
184#[cfg(test)]
185mod tests {
186    use super::*;
187    use std::sync::atomic::Ordering;
188
189    #[test]
190    fn test_mutex_correctness() {
191        let counter = Arc::new(Mutex::new(0));
192        let mut handles = vec![];
193
194        for _ in 0..10 {
195            let counter_clone = Arc::clone(&counter);
196            let handle = thread::spawn(move || {
197                for _ in 0..100 {
198                    *counter_clone.lock().unwrap() += 1;
199                }
200            });
201            handles.push(handle);
202        }
203
204        for handle in handles {
205            handle.join().unwrap();
206        }
207
208        assert_eq!(*counter.lock().unwrap(), 1000);
209    }
210
211    #[test]
212    fn test_atomic_correctness() {
213        let counter = Arc::new(AtomicUsize::new(0));
214        let mut handles = vec![];
215
216        for _ in 0..10 {
217            let counter_clone = Arc::clone(&counter);
218            let handle = thread::spawn(move || {
219                for _ in 0..100 {
220                    counter_clone.fetch_add(1, Ordering::SeqCst);
221                }
222            });
223            handles.push(handle);
224        }
225
226        for handle in handles {
227            handle.join().unwrap();
228        }
229
230        assert_eq!(counter.load(Ordering::SeqCst), 1000);
231    }
232
233    #[test]
234    fn test_message_passing() {
235        let (tx, rx) = mpsc::channel();
236
237        thread::spawn(move || {
238            tx.send(42).unwrap();
239        });
240
241        let received = rx.recv().unwrap();
242        assert_eq!(received, 42);
243    }
244
245    #[test]
246    fn test_rwlock() {
247        let data = Arc::new(RwLock::new(vec![1, 2, 3]));
248        let data_clone = Arc::clone(&data);
249
250        let handle = thread::spawn(move || {
251            let read = data_clone.read().unwrap();
252            read.len()
253        });
254
255        let len = handle.join().unwrap();
256        assert_eq!(len, 3);
257    }
258}