1 use std::cell::Cell;
2 use std::num::Wrapping;
3 use std::pin::Pin;
4 use std::sync::atomic::{AtomicUsize, Ordering};
5 use std::sync::Arc;
6 use std::task::{Context, Poll};
7 
8 use async_std::prelude::*;
9 use async_std::sync::RwLock;
10 use async_std::task;
11 use futures::channel::mpsc;
12 
13 #[cfg(not(target_os = "unknown"))]
14 use async_std::task::spawn;
15 #[cfg(target_os = "unknown")]
16 use async_std::task::spawn_local as spawn;
17 
18 #[cfg(target_arch = "wasm32")]
19 wasm_bindgen_test::wasm_bindgen_test_configure!(run_in_browser);
20 
21 /// Generates a random number in `0..n`.
random(n: u32) -> u3222 pub fn random(n: u32) -> u32 {
23     thread_local! {
24         static RNG: Cell<Wrapping<u32>> = Cell::new(Wrapping(1_406_868_647));
25     }
26 
27     RNG.with(|rng| {
28         // This is the 32-bit variant of Xorshift.
29         //
30         // Source: https://en.wikipedia.org/wiki/Xorshift
31         let mut x = rng.get();
32         x ^= x << 13;
33         x ^= x >> 17;
34         x ^= x << 5;
35         rng.set(x);
36 
37         // This is a fast alternative to `x % n`.
38         //
39         // Author: Daniel Lemire
40         // Source: https://lemire.me/blog/2016/06/27/a-fast-alternative-to-the-modulo-reduction/
41         ((x.0 as u64).wrapping_mul(n as u64) >> 32) as u32
42     })
43 }
44 
45 #[test]
46 #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test::wasm_bindgen_test)]
smoke()47 fn smoke() {
48     task::block_on(async {
49         let lock = RwLock::new(());
50         drop(lock.read().await);
51         drop(lock.write().await);
52         drop((lock.read().await, lock.read().await));
53         drop(lock.write().await);
54     });
55 }
56 
57 #[test]
58 #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test::wasm_bindgen_test)]
try_write()59 fn try_write() {
60     task::block_on(async {
61         let lock = RwLock::new(0isize);
62         let read_guard = lock.read().await;
63         assert!(lock.try_write().is_none());
64         drop(read_guard);
65     });
66 }
67 
68 #[test]
69 #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test::wasm_bindgen_test)]
into_inner()70 fn into_inner() {
71     let lock = RwLock::new(10);
72     assert_eq!(lock.into_inner(), 10);
73 }
74 
75 #[test]
76 #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test::wasm_bindgen_test)]
into_inner_and_drop()77 fn into_inner_and_drop() {
78     struct Counter(Arc<AtomicUsize>);
79 
80     impl Drop for Counter {
81         fn drop(&mut self) {
82             self.0.fetch_add(1, Ordering::SeqCst);
83         }
84     }
85 
86     let cnt = Arc::new(AtomicUsize::new(0));
87     let lock = RwLock::new(Counter(cnt.clone()));
88     assert_eq!(cnt.load(Ordering::SeqCst), 0);
89 
90     {
91         let _inner = lock.into_inner();
92         assert_eq!(cnt.load(Ordering::SeqCst), 0);
93     }
94 
95     assert_eq!(cnt.load(Ordering::SeqCst), 1);
96 }
97 
98 #[test]
99 #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test::wasm_bindgen_test)]
get_mut()100 fn get_mut() {
101     let mut lock = RwLock::new(10);
102     *lock.get_mut() = 20;
103     assert_eq!(lock.into_inner(), 20);
104 }
105 
106 #[test]
107 #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test::wasm_bindgen_test)]
contention()108 fn contention() {
109     const N: u32 = 10;
110     const M: usize = 1000;
111 
112     let (tx, mut rx) = mpsc::unbounded();
113     let tx = Arc::new(tx);
114     let rw = Arc::new(RwLock::new(()));
115 
116     // Spawn N tasks that randomly acquire the lock M times.
117     for _ in 0..N {
118         let tx = tx.clone();
119         let rw = rw.clone();
120 
121         spawn(async move {
122             for _ in 0..M {
123                 if random(N) == 0 {
124                     drop(rw.write().await);
125                 } else {
126                     drop(rw.read().await);
127                 }
128             }
129             tx.unbounded_send(()).unwrap();
130         });
131     }
132 
133     task::block_on(async move {
134         for _ in 0..N {
135             rx.next().await.unwrap();
136         }
137     });
138 }
139 
140 #[test]
141 #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test::wasm_bindgen_test)]
writer_and_readers()142 fn writer_and_readers() {
143     #[derive(Default)]
144     struct Yield(Cell<bool>);
145 
146     impl Future for Yield {
147         type Output = ();
148 
149         fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
150             if self.0.get() {
151                 Poll::Ready(())
152             } else {
153                 self.0.set(true);
154                 cx.waker().wake_by_ref();
155                 Poll::Pending
156             }
157         }
158     }
159 
160     let lock = Arc::new(RwLock::new(0i32));
161     let (tx, mut rx) = mpsc::unbounded();
162 
163     // Spawn a writer task.
164     spawn({
165         let lock = lock.clone();
166         async move {
167             let mut lock = lock.write().await;
168             for _ in 0..10 {
169                 let tmp = *lock;
170                 *lock = -1;
171                 Yield::default().await;
172                 *lock = tmp + 1;
173             }
174             tx.unbounded_send(()).unwrap();
175         }
176     });
177 
178     // Readers try to catch the writer in the act.
179     let mut readers = Vec::new();
180     for _ in 0..5 {
181         let lock = lock.clone();
182         readers.push(spawn(async move {
183             let lock = lock.read().await;
184             assert!(*lock >= 0);
185         }));
186     }
187 
188     task::block_on(async move {
189         // Wait for readers to pass their asserts.
190         for r in readers {
191             r.await;
192         }
193 
194         // Wait for writer to finish.
195         rx.next().await.unwrap();
196         let lock = lock.read().await;
197         assert_eq!(*lock, 10);
198     });
199 }
200