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