Line data Source code
1 : //! Simple utilities akin to what's in [`std::sync`] but designed to work with shared memory.
2 :
3 : use std::mem::MaybeUninit;
4 : use std::ptr::NonNull;
5 :
6 : use nix::errno::Errno;
7 :
8 : pub type RwLock<T> = lock_api::RwLock<PthreadRwLock, T>;
9 : pub type RwLockReadGuard<'a, T> = lock_api::RwLockReadGuard<'a, PthreadRwLock, T>;
10 : pub type RwLockWriteGuard<'a, T> = lock_api::RwLockWriteGuard<'a, PthreadRwLock, T>;
11 : pub type ValueReadGuard<'a, T> = lock_api::MappedRwLockReadGuard<'a, PthreadRwLock, T>;
12 : pub type ValueWriteGuard<'a, T> = lock_api::MappedRwLockWriteGuard<'a, PthreadRwLock, T>;
13 :
14 : /// Shared memory read-write lock.
15 : pub struct PthreadRwLock(Option<NonNull<libc::pthread_rwlock_t>>);
16 :
17 : /// Simple macro that calls a function in the libc namespace and panics if return value is nonzero.
18 : macro_rules! libc_checked {
19 : ($fn_name:ident ( $($arg:expr),* )) => {{
20 : let res = libc::$fn_name($($arg),*);
21 : if res != 0 {
22 : panic!("{} failed with {}", stringify!($fn_name), Errno::from_raw(res));
23 : }
24 : }};
25 : }
26 :
27 : impl PthreadRwLock {
28 : /// Creates a new `PthreadRwLock` on top of a pointer to a pthread rwlock.
29 : ///
30 : /// # Safety
31 : /// `lock` must be non-null. Every unsafe operation will panic in the event of an error.
32 26 : pub unsafe fn new(lock: *mut libc::pthread_rwlock_t) -> Self {
33 : unsafe {
34 26 : let mut attrs = MaybeUninit::uninit();
35 26 : libc_checked!(pthread_rwlockattr_init(attrs.as_mut_ptr()));
36 26 : libc_checked!(pthread_rwlockattr_setpshared(
37 26 : attrs.as_mut_ptr(),
38 : libc::PTHREAD_PROCESS_SHARED
39 : ));
40 26 : libc_checked!(pthread_rwlock_init(lock, attrs.as_mut_ptr()));
41 : // Safety: POSIX specifies that "any function affecting the attributes
42 : // object (including destruction) shall not affect any previously
43 : // initialized read-write locks".
44 26 : libc_checked!(pthread_rwlockattr_destroy(attrs.as_mut_ptr()));
45 26 : Self(Some(NonNull::new_unchecked(lock)))
46 : }
47 26 : }
48 :
49 810972 : fn inner(&self) -> NonNull<libc::pthread_rwlock_t> {
50 810972 : match self.0 {
51 : None => {
52 0 : panic!("PthreadRwLock constructed badly - something likely used RawRwLock::INIT")
53 : }
54 810972 : Some(x) => x,
55 : }
56 810972 : }
57 : }
58 :
59 : unsafe impl lock_api::RawRwLock for PthreadRwLock {
60 : type GuardMarker = lock_api::GuardSend;
61 : const INIT: Self = Self(None);
62 :
63 0 : fn try_lock_shared(&self) -> bool {
64 : unsafe {
65 0 : let res = libc::pthread_rwlock_tryrdlock(self.inner().as_ptr());
66 0 : match res {
67 0 : 0 => true,
68 0 : libc::EAGAIN => false,
69 0 : _ => panic!(
70 0 : "pthread_rwlock_tryrdlock failed with {}",
71 0 : Errno::from_raw(res)
72 : ),
73 : }
74 : }
75 0 : }
76 :
77 0 : fn try_lock_exclusive(&self) -> bool {
78 : unsafe {
79 0 : let res = libc::pthread_rwlock_trywrlock(self.inner().as_ptr());
80 0 : match res {
81 0 : 0 => true,
82 0 : libc::EAGAIN => false,
83 0 : _ => panic!("try_wrlock failed with {}", Errno::from_raw(res)),
84 : }
85 : }
86 0 : }
87 :
88 111259 : fn lock_shared(&self) {
89 : unsafe {
90 111259 : libc_checked!(pthread_rwlock_rdlock(self.inner().as_ptr()));
91 : }
92 111259 : }
93 :
94 294227 : fn lock_exclusive(&self) {
95 : unsafe {
96 294227 : libc_checked!(pthread_rwlock_wrlock(self.inner().as_ptr()));
97 : }
98 294227 : }
99 :
100 294227 : unsafe fn unlock_exclusive(&self) {
101 : unsafe {
102 294227 : libc_checked!(pthread_rwlock_unlock(self.inner().as_ptr()));
103 : }
104 294227 : }
105 :
106 111259 : unsafe fn unlock_shared(&self) {
107 : unsafe {
108 111259 : libc_checked!(pthread_rwlock_unlock(self.inner().as_ptr()));
109 : }
110 111259 : }
111 : }
|