]> git.lizzy.rs Git - rust.git/blob - tests/pass/concurrency/sync.rs
16bdc03f1092d6550c9cbcd02efdeb12654aec23
[rust.git] / tests / pass / concurrency / sync.rs
1 //@ignore-target-windows: Concurrency on Windows is not supported yet.
2 //@compile-flags: -Zmiri-disable-isolation -Zmiri-strict-provenance
3
4 use std::sync::{Arc, Barrier, Condvar, Mutex, Once, RwLock};
5 use std::thread;
6 use std::time::{Duration, Instant};
7
8 // Check if Rust barriers are working.
9
10 /// This test is taken from the Rust documentation.
11 fn check_barriers() {
12     let mut handles = Vec::with_capacity(10);
13     let barrier = Arc::new(Barrier::new(10));
14     for _ in 0..10 {
15         let c = barrier.clone();
16         // The same messages will be printed together.
17         // You will NOT see any interleaving.
18         handles.push(thread::spawn(move || {
19             println!("before wait");
20             c.wait();
21             println!("after wait");
22         }));
23     }
24     // Wait for other threads to finish.
25     for handle in handles {
26         handle.join().unwrap();
27     }
28 }
29
30 // Check if Rust conditional variables are working.
31
32 /// The test taken from the Rust documentation.
33 fn check_conditional_variables_notify_one() {
34     let pair = Arc::new((Mutex::new(false), Condvar::new()));
35     let pair2 = pair.clone();
36
37     // Spawn a new thread.
38     let t = thread::spawn(move || {
39         thread::yield_now();
40         let (lock, cvar) = &*pair2;
41         let mut started = lock.lock().unwrap();
42         *started = true;
43         // We notify the condvar that the value has changed.
44         cvar.notify_one();
45     });
46
47     // Wait for the thread to fully start up.
48     let (lock, cvar) = &*pair;
49     let mut started = lock.lock().unwrap();
50     while !*started {
51         started = cvar.wait(started).unwrap();
52     }
53
54     t.join().unwrap();
55 }
56
57 /// Test that waiting on a conditional variable with a timeout does not
58 /// deadlock.
59 fn check_conditional_variables_timed_wait_timeout() {
60     let lock = Mutex::new(());
61     let cvar = Condvar::new();
62     let guard = lock.lock().unwrap();
63     let now = Instant::now();
64     let (_guard, timeout) = cvar.wait_timeout(guard, Duration::from_millis(100)).unwrap();
65     assert!(timeout.timed_out());
66     let elapsed_time = now.elapsed().as_millis();
67     assert!(100 <= elapsed_time && elapsed_time <= 1000);
68 }
69
70 /// Test that signaling a conditional variable when waiting with a timeout works
71 /// as expected.
72 fn check_conditional_variables_timed_wait_notimeout() {
73     let pair = Arc::new((Mutex::new(()), Condvar::new()));
74     let pair2 = pair.clone();
75
76     let (lock, cvar) = &*pair;
77     let guard = lock.lock().unwrap();
78
79     let handle = thread::spawn(move || {
80         let (_lock, cvar) = &*pair2;
81         cvar.notify_one();
82     });
83
84     let (_guard, timeout) = cvar.wait_timeout(guard, Duration::from_millis(1000)).unwrap();
85     assert!(!timeout.timed_out());
86     handle.join().unwrap();
87 }
88
89 // Check if locks are working.
90
91 fn check_mutex() {
92     let data = Arc::new(Mutex::new(0));
93     let mut threads = Vec::new();
94
95     for _ in 0..3 {
96         let data = Arc::clone(&data);
97         let thread = thread::spawn(move || {
98             let mut data = data.lock().unwrap();
99             thread::yield_now();
100             *data += 1;
101         });
102         threads.push(thread);
103     }
104
105     for thread in threads {
106         thread.join().unwrap();
107     }
108
109     assert!(data.try_lock().is_ok());
110
111     let data = Arc::try_unwrap(data).unwrap().into_inner().unwrap();
112     assert_eq!(data, 3);
113 }
114
115 fn check_rwlock_write() {
116     let data = Arc::new(RwLock::new(0));
117     let mut threads = Vec::new();
118
119     for _ in 0..3 {
120         let data = Arc::clone(&data);
121         let thread = thread::spawn(move || {
122             let mut data = data.write().unwrap();
123             thread::yield_now();
124             *data += 1;
125         });
126         threads.push(thread);
127     }
128
129     for thread in threads {
130         thread.join().unwrap();
131     }
132
133     assert!(data.try_write().is_ok());
134
135     let data = Arc::try_unwrap(data).unwrap().into_inner().unwrap();
136     assert_eq!(data, 3);
137 }
138
139 fn check_rwlock_read_no_deadlock() {
140     let l1 = Arc::new(RwLock::new(0));
141     let l2 = Arc::new(RwLock::new(0));
142
143     let l1_copy = Arc::clone(&l1);
144     let l2_copy = Arc::clone(&l2);
145     let _guard1 = l1.read().unwrap();
146     let handle = thread::spawn(move || {
147         let _guard2 = l2_copy.read().unwrap();
148         thread::yield_now();
149         let _guard1 = l1_copy.read().unwrap();
150     });
151     thread::yield_now();
152     let _guard2 = l2.read().unwrap();
153     handle.join().unwrap();
154 }
155
156 // Check if Rust once statics are working.
157
158 static mut VAL: usize = 0;
159 static INIT: Once = Once::new();
160
161 fn get_cached_val() -> usize {
162     unsafe {
163         INIT.call_once(|| {
164             VAL = expensive_computation();
165         });
166         VAL
167     }
168 }
169
170 fn expensive_computation() -> usize {
171     let mut i = 1;
172     let mut c = 1;
173     while i < 1000 {
174         i *= c;
175         c += 1;
176     }
177     i
178 }
179
180 /// The test taken from the Rust documentation.
181 fn check_once() {
182     let handles: Vec<_> = (0..10)
183         .map(|_| {
184             thread::spawn(|| {
185                 thread::yield_now();
186                 let val = get_cached_val();
187                 assert_eq!(val, 5040);
188             })
189         })
190         .collect();
191     for handle in handles {
192         handle.join().unwrap();
193     }
194 }
195
196 fn park_timeout() {
197     let start = Instant::now();
198
199     thread::park_timeout(Duration::from_millis(200));
200     // Normally, waiting in park/park_timeout may spuriously wake up early, but we
201     // know Miri's timed synchronization primitives do not do that.
202
203     assert!((200..1000).contains(&start.elapsed().as_millis()));
204 }
205
206 fn park_unpark() {
207     let t1 = thread::current();
208     let t2 = thread::spawn(move || {
209         thread::park();
210         thread::sleep(Duration::from_millis(200));
211         t1.unpark();
212     });
213
214     let start = Instant::now();
215
216     t2.thread().unpark();
217     thread::park();
218     // Normally, waiting in park/park_timeout may spuriously wake up early, but we
219     // know Miri's timed synchronization primitives do not do that.
220
221     assert!((200..1000).contains(&start.elapsed().as_millis()));
222
223     t2.join().unwrap();
224 }
225
226 fn main() {
227     check_barriers();
228     check_conditional_variables_notify_one();
229     check_conditional_variables_timed_wait_timeout();
230     check_conditional_variables_timed_wait_notimeout();
231     check_mutex();
232     check_rwlock_write();
233     check_rwlock_read_no_deadlock();
234     check_once();
235     park_timeout();
236     park_unpark();
237 }