parking_lot/
rwlock.rs

1// Copyright 2016 Amanieu d'Antras
2//
3// Licensed under the Apache License, Version 2.0, <LICENSE-APACHE or
4// http://apache.org/licenses/LICENSE-2.0> or the MIT license <LICENSE-MIT or
5// http://opensource.org/licenses/MIT>, at your option. This file may not be
6// copied, modified, or distributed except according to those terms.
7
8use crate::raw_rwlock::RawRwLock;
9
10/// A reader-writer lock
11///
12/// This type of lock allows a number of readers or at most one writer at any
13/// point in time. The write portion of this lock typically allows modification
14/// of the underlying data (exclusive access) and the read portion of this lock
15/// typically allows for read-only access (shared access).
16///
17/// This lock uses a task-fair locking policy which avoids both reader and
18/// writer starvation. This means that readers trying to acquire the lock will
19/// block even if the lock is unlocked when there are writers waiting to acquire
20/// the lock. Because of this, attempts to recursively acquire a read lock
21/// within a single thread may result in a deadlock.
22///
23/// The type parameter `T` represents the data that this lock protects. It is
24/// required that `T` satisfies `Send` to be shared across threads and `Sync` to
25/// allow concurrent access through readers. The RAII guards returned from the
26/// locking methods implement `Deref` (and `DerefMut` for the `write` methods)
27/// to allow access to the contained of the lock.
28///
29/// # Fairness
30///
31/// A typical unfair lock can often end up in a situation where a single thread
32/// quickly acquires and releases the same lock in succession, which can starve
33/// other threads waiting to acquire the rwlock. While this improves throughput
34/// because it doesn't force a context switch when a thread tries to re-acquire
35/// a rwlock it has just released, this can starve other threads.
36///
37/// This rwlock uses [eventual fairness](https://trac.webkit.org/changeset/203350)
38/// to ensure that the lock will be fair on average without sacrificing
39/// throughput. This is done by forcing a fair unlock on average every 0.5ms,
40/// which will force the lock to go to the next thread waiting for the rwlock.
41///
42/// Additionally, any critical section longer than 1ms will always use a fair
43/// unlock, which has a negligible impact on throughput considering the length
44/// of the critical section.
45///
46/// You can also force a fair unlock by calling `RwLockReadGuard::unlock_fair`
47/// or `RwLockWriteGuard::unlock_fair` when unlocking a mutex instead of simply
48/// dropping the guard.
49///
50/// # Differences from the standard library `RwLock`
51///
52/// - Supports atomically downgrading a write lock into a read lock.
53/// - Task-fair locking policy instead of an unspecified platform default.
54/// - No poisoning, the lock is released normally on panic.
55/// - Only requires 1 word of space, whereas the standard library boxes the
56///   `RwLock` due to platform limitations.
57/// - Can be statically constructed.
58/// - Does not require any drop glue when dropped.
59/// - Inline fast path for the uncontended case.
60/// - Efficient handling of micro-contention using adaptive spinning.
61/// - Allows raw locking & unlocking without a guard.
62/// - Supports eventual fairness so that the rwlock is fair on average.
63/// - Optionally allows making the rwlock fair by calling
64///   `RwLockReadGuard::unlock_fair` and `RwLockWriteGuard::unlock_fair`.
65///
66/// # Examples
67///
68/// ```
69/// use parking_lot::RwLock;
70///
71/// let lock = RwLock::new(5);
72///
73/// // many reader locks can be held at once
74/// {
75///     let r1 = lock.read();
76///     let r2 = lock.read();
77///     assert_eq!(*r1, 5);
78///     assert_eq!(*r2, 5);
79/// } // read locks are dropped at this point
80///
81/// // only one write lock may be held, however
82/// {
83///     let mut w = lock.write();
84///     *w += 1;
85///     assert_eq!(*w, 6);
86/// } // write lock is dropped here
87/// ```
88pub type RwLock<T> = lock_api::RwLock<RawRwLock, T>;
89
90/// Creates a new instance of an `RwLock<T>` which is unlocked.
91///
92/// This allows creating a `RwLock<T>` in a constant context on stable Rust.
93pub const fn const_rwlock<T>(val: T) -> RwLock<T> {
94    RwLock::const_new(<RawRwLock as lock_api::RawRwLock>::INIT, val)
95}
96
97/// RAII structure used to release the shared read access of a lock when
98/// dropped.
99pub type RwLockReadGuard<'a, T> = lock_api::RwLockReadGuard<'a, RawRwLock, T>;
100
101/// RAII structure used to release the exclusive write access of a lock when
102/// dropped.
103pub type RwLockWriteGuard<'a, T> = lock_api::RwLockWriteGuard<'a, RawRwLock, T>;
104
105/// An RAII read lock guard returned by `RwLockReadGuard::map`, which can point to a
106/// subfield of the protected data.
107///
108/// The main difference between `MappedRwLockReadGuard` and `RwLockReadGuard` is that the
109/// former doesn't support temporarily unlocking and re-locking, since that
110/// could introduce soundness issues if the locked object is modified by another
111/// thread.
112pub type MappedRwLockReadGuard<'a, T> = lock_api::MappedRwLockReadGuard<'a, RawRwLock, T>;
113
114/// An RAII write lock guard returned by `RwLockWriteGuard::map`, which can point to a
115/// subfield of the protected data.
116///
117/// The main difference between `MappedRwLockWriteGuard` and `RwLockWriteGuard` is that the
118/// former doesn't support temporarily unlocking and re-locking, since that
119/// could introduce soundness issues if the locked object is modified by another
120/// thread.
121pub type MappedRwLockWriteGuard<'a, T> = lock_api::MappedRwLockWriteGuard<'a, RawRwLock, T>;
122
123/// RAII structure used to release the upgradable read access of a lock when
124/// dropped.
125pub type RwLockUpgradableReadGuard<'a, T> = lock_api::RwLockUpgradableReadGuard<'a, RawRwLock, T>;
126
127#[cfg(test)]
128mod tests {
129    use crate::{RwLock, RwLockUpgradableReadGuard, RwLockWriteGuard};
130    use rand::Rng;
131    use std::sync::atomic::{AtomicUsize, Ordering};
132    use std::sync::mpsc::channel;
133    use std::sync::Arc;
134    use std::thread;
135    use std::time::Duration;
136
137    #[cfg(feature = "serde")]
138    use bincode::{deserialize, serialize};
139
140    #[derive(Eq, PartialEq, Debug)]
141    struct NonCopy(i32);
142
143    #[test]
144    fn smoke() {
145        let l = RwLock::new(());
146        drop(l.read());
147        drop(l.write());
148        drop(l.upgradable_read());
149        drop((l.read(), l.read()));
150        drop((l.read(), l.upgradable_read()));
151        drop(l.write());
152    }
153
154    #[test]
155    fn frob() {
156        const N: u32 = 10;
157        const M: u32 = 1000;
158
159        let r = Arc::new(RwLock::new(()));
160
161        let (tx, rx) = channel::<()>();
162        for _ in 0..N {
163            let tx = tx.clone();
164            let r = r.clone();
165            thread::spawn(move || {
166                let mut rng = rand::thread_rng();
167                for _ in 0..M {
168                    if rng.gen_bool(1.0 / N as f64) {
169                        drop(r.write());
170                    } else {
171                        drop(r.read());
172                    }
173                }
174                drop(tx);
175            });
176        }
177        drop(tx);
178        let _ = rx.recv();
179    }
180
181    #[test]
182    fn test_rw_arc_no_poison_wr() {
183        let arc = Arc::new(RwLock::new(1));
184        let arc2 = arc.clone();
185        let _: Result<(), _> = thread::spawn(move || {
186            let _lock = arc2.write();
187            panic!();
188        })
189        .join();
190        let lock = arc.read();
191        assert_eq!(*lock, 1);
192    }
193
194    #[test]
195    fn test_rw_arc_no_poison_ww() {
196        let arc = Arc::new(RwLock::new(1));
197        let arc2 = arc.clone();
198        let _: Result<(), _> = thread::spawn(move || {
199            let _lock = arc2.write();
200            panic!();
201        })
202        .join();
203        let lock = arc.write();
204        assert_eq!(*lock, 1);
205    }
206
207    #[test]
208    fn test_rw_arc_no_poison_rr() {
209        let arc = Arc::new(RwLock::new(1));
210        let arc2 = arc.clone();
211        let _: Result<(), _> = thread::spawn(move || {
212            let _lock = arc2.read();
213            panic!();
214        })
215        .join();
216        let lock = arc.read();
217        assert_eq!(*lock, 1);
218    }
219
220    #[test]
221    fn test_rw_arc_no_poison_rw() {
222        let arc = Arc::new(RwLock::new(1));
223        let arc2 = arc.clone();
224        let _: Result<(), _> = thread::spawn(move || {
225            let _lock = arc2.read();
226            panic!()
227        })
228        .join();
229        let lock = arc.write();
230        assert_eq!(*lock, 1);
231    }
232
233    #[test]
234    fn test_ruw_arc() {
235        let arc = Arc::new(RwLock::new(0));
236        let arc2 = arc.clone();
237        let (tx, rx) = channel();
238
239        thread::spawn(move || {
240            for _ in 0..10 {
241                let mut lock = arc2.write();
242                let tmp = *lock;
243                *lock = -1;
244                thread::yield_now();
245                *lock = tmp + 1;
246            }
247            tx.send(()).unwrap();
248        });
249
250        let mut children = Vec::new();
251
252        // Upgradable readers try to catch the writer in the act and also
253        // try to touch the value
254        for _ in 0..5 {
255            let arc3 = arc.clone();
256            children.push(thread::spawn(move || {
257                let lock = arc3.upgradable_read();
258                let tmp = *lock;
259                assert!(tmp >= 0);
260                thread::yield_now();
261                let mut lock = RwLockUpgradableReadGuard::upgrade(lock);
262                assert_eq!(tmp, *lock);
263                *lock = -1;
264                thread::yield_now();
265                *lock = tmp + 1;
266            }));
267        }
268
269        // Readers try to catch the writers in the act
270        for _ in 0..5 {
271            let arc4 = arc.clone();
272            children.push(thread::spawn(move || {
273                let lock = arc4.read();
274                assert!(*lock >= 0);
275            }));
276        }
277
278        // Wait for children to pass their asserts
279        for r in children {
280            assert!(r.join().is_ok());
281        }
282
283        // Wait for writer to finish
284        rx.recv().unwrap();
285        let lock = arc.read();
286        assert_eq!(*lock, 15);
287    }
288
289    #[test]
290    fn test_rw_arc() {
291        let arc = Arc::new(RwLock::new(0));
292        let arc2 = arc.clone();
293        let (tx, rx) = channel();
294
295        thread::spawn(move || {
296            let mut lock = arc2.write();
297            for _ in 0..10 {
298                let tmp = *lock;
299                *lock = -1;
300                thread::yield_now();
301                *lock = tmp + 1;
302            }
303            tx.send(()).unwrap();
304        });
305
306        // Readers try to catch the writer in the act
307        let mut children = Vec::new();
308        for _ in 0..5 {
309            let arc3 = arc.clone();
310            children.push(thread::spawn(move || {
311                let lock = arc3.read();
312                assert!(*lock >= 0);
313            }));
314        }
315
316        // Wait for children to pass their asserts
317        for r in children {
318            assert!(r.join().is_ok());
319        }
320
321        // Wait for writer to finish
322        rx.recv().unwrap();
323        let lock = arc.read();
324        assert_eq!(*lock, 10);
325    }
326
327    #[test]
328    fn test_rw_arc_access_in_unwind() {
329        let arc = Arc::new(RwLock::new(1));
330        let arc2 = arc.clone();
331        let _ = thread::spawn(move || {
332            struct Unwinder {
333                i: Arc<RwLock<isize>>,
334            }
335            impl Drop for Unwinder {
336                fn drop(&mut self) {
337                    let mut lock = self.i.write();
338                    *lock += 1;
339                }
340            }
341            let _u = Unwinder { i: arc2 };
342            panic!();
343        })
344        .join();
345        let lock = arc.read();
346        assert_eq!(*lock, 2);
347    }
348
349    #[test]
350    fn test_rwlock_unsized() {
351        let rw: &RwLock<[i32]> = &RwLock::new([1, 2, 3]);
352        {
353            let b = &mut *rw.write();
354            b[0] = 4;
355            b[2] = 5;
356        }
357        let comp: &[i32] = &[4, 2, 5];
358        assert_eq!(&*rw.read(), comp);
359    }
360
361    #[test]
362    fn test_rwlock_try_read() {
363        let lock = RwLock::new(0isize);
364        {
365            let read_guard = lock.read();
366
367            let read_result = lock.try_read();
368            assert!(
369                read_result.is_some(),
370                "try_read should succeed while read_guard is in scope"
371            );
372
373            drop(read_guard);
374        }
375        {
376            let upgrade_guard = lock.upgradable_read();
377
378            let read_result = lock.try_read();
379            assert!(
380                read_result.is_some(),
381                "try_read should succeed while upgrade_guard is in scope"
382            );
383
384            drop(upgrade_guard);
385        }
386        {
387            let write_guard = lock.write();
388
389            let read_result = lock.try_read();
390            assert!(
391                read_result.is_none(),
392                "try_read should fail while write_guard is in scope"
393            );
394
395            drop(write_guard);
396        }
397    }
398
399    #[test]
400    fn test_rwlock_try_write() {
401        let lock = RwLock::new(0isize);
402        {
403            let read_guard = lock.read();
404
405            let write_result = lock.try_write();
406            assert!(
407                write_result.is_none(),
408                "try_write should fail while read_guard is in scope"
409            );
410            assert!(lock.is_locked());
411            assert!(!lock.is_locked_exclusive());
412
413            drop(read_guard);
414        }
415        {
416            let upgrade_guard = lock.upgradable_read();
417
418            let write_result = lock.try_write();
419            assert!(
420                write_result.is_none(),
421                "try_write should fail while upgrade_guard is in scope"
422            );
423            assert!(lock.is_locked());
424            assert!(!lock.is_locked_exclusive());
425
426            drop(upgrade_guard);
427        }
428        {
429            let write_guard = lock.write();
430
431            let write_result = lock.try_write();
432            assert!(
433                write_result.is_none(),
434                "try_write should fail while write_guard is in scope"
435            );
436            assert!(lock.is_locked());
437            assert!(lock.is_locked_exclusive());
438
439            drop(write_guard);
440        }
441    }
442
443    #[test]
444    fn test_rwlock_try_upgrade() {
445        let lock = RwLock::new(0isize);
446        {
447            let read_guard = lock.read();
448
449            let upgrade_result = lock.try_upgradable_read();
450            assert!(
451                upgrade_result.is_some(),
452                "try_upgradable_read should succeed while read_guard is in scope"
453            );
454
455            drop(read_guard);
456        }
457        {
458            let upgrade_guard = lock.upgradable_read();
459
460            let upgrade_result = lock.try_upgradable_read();
461            assert!(
462                upgrade_result.is_none(),
463                "try_upgradable_read should fail while upgrade_guard is in scope"
464            );
465
466            drop(upgrade_guard);
467        }
468        {
469            let write_guard = lock.write();
470
471            let upgrade_result = lock.try_upgradable_read();
472            assert!(
473                upgrade_result.is_none(),
474                "try_upgradable should fail while write_guard is in scope"
475            );
476
477            drop(write_guard);
478        }
479    }
480
481    #[test]
482    fn test_into_inner() {
483        let m = RwLock::new(NonCopy(10));
484        assert_eq!(m.into_inner(), NonCopy(10));
485    }
486
487    #[test]
488    fn test_into_inner_drop() {
489        struct Foo(Arc<AtomicUsize>);
490        impl Drop for Foo {
491            fn drop(&mut self) {
492                self.0.fetch_add(1, Ordering::SeqCst);
493            }
494        }
495        let num_drops = Arc::new(AtomicUsize::new(0));
496        let m = RwLock::new(Foo(num_drops.clone()));
497        assert_eq!(num_drops.load(Ordering::SeqCst), 0);
498        {
499            let _inner = m.into_inner();
500            assert_eq!(num_drops.load(Ordering::SeqCst), 0);
501        }
502        assert_eq!(num_drops.load(Ordering::SeqCst), 1);
503    }
504
505    #[test]
506    fn test_get_mut() {
507        let mut m = RwLock::new(NonCopy(10));
508        *m.get_mut() = NonCopy(20);
509        assert_eq!(m.into_inner(), NonCopy(20));
510    }
511
512    #[test]
513    fn test_rwlockguard_sync() {
514        fn sync<T: Sync>(_: T) {}
515
516        let rwlock = RwLock::new(());
517        sync(rwlock.read());
518        sync(rwlock.write());
519    }
520
521    #[test]
522    fn test_rwlock_downgrade() {
523        let x = Arc::new(RwLock::new(0));
524        let mut handles = Vec::new();
525        for _ in 0..8 {
526            let x = x.clone();
527            handles.push(thread::spawn(move || {
528                for _ in 0..100 {
529                    let mut writer = x.write();
530                    *writer += 1;
531                    let cur_val = *writer;
532                    let reader = RwLockWriteGuard::downgrade(writer);
533                    assert_eq!(cur_val, *reader);
534                }
535            }));
536        }
537        for handle in handles {
538            handle.join().unwrap()
539        }
540        assert_eq!(*x.read(), 800);
541    }
542
543    #[test]
544    fn test_rwlock_recursive() {
545        let arc = Arc::new(RwLock::new(1));
546        let arc2 = arc.clone();
547        let lock1 = arc.read();
548        let t = thread::spawn(move || {
549            let _lock = arc2.write();
550        });
551
552        if cfg!(not(all(target_env = "sgx", target_vendor = "fortanix"))) {
553            thread::sleep(Duration::from_millis(100));
554        } else {
555            // FIXME: https://github.com/fortanix/rust-sgx/issues/31
556            for _ in 0..100 {
557                thread::yield_now();
558            }
559        }
560
561        // A normal read would block here since there is a pending writer
562        let lock2 = arc.read_recursive();
563
564        // Unblock the thread and join it.
565        drop(lock1);
566        drop(lock2);
567        t.join().unwrap();
568    }
569
570    #[test]
571    fn test_rwlock_debug() {
572        let x = RwLock::new(vec![0u8, 10]);
573
574        assert_eq!(format!("{:?}", x), "RwLock { data: [0, 10] }");
575        let _lock = x.write();
576        assert_eq!(format!("{:?}", x), "RwLock { data: <locked> }");
577    }
578
579    #[test]
580    fn test_clone() {
581        let rwlock = RwLock::new(Arc::new(1));
582        let a = rwlock.read_recursive();
583        let b = a.clone();
584        assert_eq!(Arc::strong_count(&b), 2);
585    }
586
587    #[cfg(feature = "serde")]
588    #[test]
589    fn test_serde() {
590        let contents: Vec<u8> = vec![0, 1, 2];
591        let mutex = RwLock::new(contents.clone());
592
593        let serialized = serialize(&mutex).unwrap();
594        let deserialized: RwLock<Vec<u8>> = deserialize(&serialized).unwrap();
595
596        assert_eq!(*(mutex.read()), *(deserialized.read()));
597        assert_eq!(contents, *(deserialized.read()));
598    }
599
600    #[test]
601    fn test_issue_203() {
602        struct Bar(RwLock<()>);
603
604        impl Drop for Bar {
605            fn drop(&mut self) {
606                let _n = self.0.write();
607            }
608        }
609
610        thread_local! {
611            static B: Bar = Bar(RwLock::new(()));
612        }
613
614        thread::spawn(|| {
615            B.with(|_| ());
616
617            let a = RwLock::new(());
618            let _a = a.read();
619        })
620        .join()
621        .unwrap();
622    }
623
624    #[test]
625    fn test_rw_write_is_locked() {
626        let lock = RwLock::new(0isize);
627        {
628            let _read_guard = lock.read();
629
630            assert!(lock.is_locked());
631            assert!(!lock.is_locked_exclusive());
632        }
633
634        {
635            let _write_guard = lock.write();
636
637            assert!(lock.is_locked());
638            assert!(lock.is_locked_exclusive());
639        }
640    }
641
642    #[test]
643    #[cfg(feature = "arc_lock")]
644    fn test_issue_430() {
645        let lock = std::sync::Arc::new(RwLock::new(0));
646
647        let mut rl = lock.upgradable_read_arc();
648
649        rl.with_upgraded(|_| {
650            println!("lock upgrade");
651        });
652
653        rl.with_upgraded(|_| {
654            println!("lock upgrade");
655        });
656
657        drop(lock);
658    }
659}