]>
Commit | Line | Data |
---|---|---|
532ac7d7 | 1 | use crate::cell::UnsafeCell; |
dc9dc135 | 2 | use crate::mem::MaybeUninit; |
29967ef6 | 3 | use crate::sys::cvt_nz; |
1a4d82fc | 4 | |
dfeec247 XL |
5 | pub struct Mutex { |
6 | inner: UnsafeCell<libc::pthread_mutex_t>, | |
7 | } | |
1a4d82fc | 8 | |
29967ef6 XL |
9 | pub type MovableMutex = Box<Mutex>; |
10 | ||
1a4d82fc | 11 | #[inline] |
92a42be0 | 12 | pub unsafe fn raw(m: &Mutex) -> *mut libc::pthread_mutex_t { |
1a4d82fc JJ |
13 | m.inner.get() |
14 | } | |
15 | ||
c34b1796 | 16 | unsafe impl Send for Mutex {} |
1a4d82fc JJ |
17 | unsafe impl Sync for Mutex {} |
18 | ||
c34b1796 | 19 | #[allow(dead_code)] // sys isn't exported yet |
1a4d82fc | 20 | impl Mutex { |
62682a34 | 21 | pub const fn new() -> Mutex { |
b7449926 XL |
22 | // Might be moved to a different address, so it is better to avoid |
23 | // initialization of potentially opaque OS data before it landed. | |
24 | // Be very careful using this newly constructed `Mutex`, reentrant | |
25 | // locking is undefined behavior until `init` is called! | |
92a42be0 | 26 | Mutex { inner: UnsafeCell::new(libc::PTHREAD_MUTEX_INITIALIZER) } |
1a4d82fc JJ |
27 | } |
28 | #[inline] | |
3157f602 XL |
29 | pub unsafe fn init(&mut self) { |
30 | // Issue #33770 | |
31 | // | |
32 | // A pthread mutex initialized with PTHREAD_MUTEX_INITIALIZER will have | |
33 | // a type of PTHREAD_MUTEX_DEFAULT, which has undefined behavior if you | |
f9f354fc XL |
34 | // try to re-lock it from the same thread when you already hold a lock |
35 | // (https://pubs.opengroup.org/onlinepubs/9699919799/functions/pthread_mutex_init.html). | |
36 | // This is the case even if PTHREAD_MUTEX_DEFAULT == PTHREAD_MUTEX_NORMAL | |
37 | // (https://github.com/rust-lang/rust/issues/33770#issuecomment-220847521) -- in that | |
38 | // case, `pthread_mutexattr_settype(PTHREAD_MUTEX_DEFAULT)` will of course be the same | |
39 | // as setting it to `PTHREAD_MUTEX_NORMAL`, but not setting any mode will result in | |
40 | // a Mutex where re-locking is UB. | |
3157f602 XL |
41 | // |
42 | // In practice, glibc takes advantage of this undefined behavior to | |
43 | // implement hardware lock elision, which uses hardware transactional | |
44 | // memory to avoid acquiring the lock. While a transaction is in | |
45 | // progress, the lock appears to be unlocked. This isn't a problem for | |
46 | // other threads since the transactional memory will abort if a conflict | |
f9f354fc | 47 | // is detected, however no abort is generated when re-locking from the |
3157f602 XL |
48 | // same thread. |
49 | // | |
50 | // Since locking the same mutex twice will result in two aliasing &mut | |
51 | // references, we instead create the mutex with type | |
52 | // PTHREAD_MUTEX_NORMAL which is guaranteed to deadlock if we try to | |
53 | // re-lock it from the same thread, thus avoiding undefined behavior. | |
dc9dc135 | 54 | let mut attr = MaybeUninit::<libc::pthread_mutexattr_t>::uninit(); |
29967ef6 XL |
55 | cvt_nz(libc::pthread_mutexattr_init(attr.as_mut_ptr())).unwrap(); |
56 | let attr = PthreadMutexAttr(&mut attr); | |
57 | cvt_nz(libc::pthread_mutexattr_settype(attr.0.as_mut_ptr(), libc::PTHREAD_MUTEX_NORMAL)) | |
58 | .unwrap(); | |
59 | cvt_nz(libc::pthread_mutex_init(self.inner.get(), attr.0.as_ptr())).unwrap(); | |
3157f602 XL |
60 | } |
61 | #[inline] | |
1a4d82fc | 62 | pub unsafe fn lock(&self) { |
92a42be0 | 63 | let r = libc::pthread_mutex_lock(self.inner.get()); |
1a4d82fc JJ |
64 | debug_assert_eq!(r, 0); |
65 | } | |
66 | #[inline] | |
67 | pub unsafe fn unlock(&self) { | |
92a42be0 | 68 | let r = libc::pthread_mutex_unlock(self.inner.get()); |
1a4d82fc JJ |
69 | debug_assert_eq!(r, 0); |
70 | } | |
71 | #[inline] | |
72 | pub unsafe fn try_lock(&self) -> bool { | |
92a42be0 | 73 | libc::pthread_mutex_trylock(self.inner.get()) == 0 |
1a4d82fc JJ |
74 | } |
75 | #[inline] | |
85aaf69f | 76 | #[cfg(not(target_os = "dragonfly"))] |
1a4d82fc | 77 | pub unsafe fn destroy(&self) { |
92a42be0 | 78 | let r = libc::pthread_mutex_destroy(self.inner.get()); |
1a4d82fc JJ |
79 | debug_assert_eq!(r, 0); |
80 | } | |
85aaf69f SL |
81 | #[inline] |
82 | #[cfg(target_os = "dragonfly")] | |
83 | pub unsafe fn destroy(&self) { | |
92a42be0 | 84 | let r = libc::pthread_mutex_destroy(self.inner.get()); |
85aaf69f | 85 | // On DragonFly pthread_mutex_destroy() returns EINVAL if called on a |
92a42be0 | 86 | // mutex that was just initialized with libc::PTHREAD_MUTEX_INITIALIZER. |
85aaf69f SL |
87 | // Once it is used (locked/unlocked) or pthread_mutex_init() is called, |
88 | // this behaviour no longer occurs. | |
89 | debug_assert!(r == 0 || r == libc::EINVAL); | |
90 | } | |
1a4d82fc | 91 | } |
9346a6ac | 92 | |
dfeec247 XL |
93 | pub struct ReentrantMutex { |
94 | inner: UnsafeCell<libc::pthread_mutex_t>, | |
95 | } | |
9346a6ac AL |
96 | |
97 | unsafe impl Send for ReentrantMutex {} | |
98 | unsafe impl Sync for ReentrantMutex {} | |
99 | ||
100 | impl ReentrantMutex { | |
ba9703b0 | 101 | pub const unsafe fn uninitialized() -> ReentrantMutex { |
dc9dc135 | 102 | ReentrantMutex { inner: UnsafeCell::new(libc::PTHREAD_MUTEX_INITIALIZER) } |
d9579d0f AL |
103 | } |
104 | ||
ba9703b0 | 105 | pub unsafe fn init(&self) { |
dc9dc135 | 106 | let mut attr = MaybeUninit::<libc::pthread_mutexattr_t>::uninit(); |
29967ef6 XL |
107 | cvt_nz(libc::pthread_mutexattr_init(attr.as_mut_ptr())).unwrap(); |
108 | let attr = PthreadMutexAttr(&mut attr); | |
109 | cvt_nz(libc::pthread_mutexattr_settype(attr.0.as_mut_ptr(), libc::PTHREAD_MUTEX_RECURSIVE)) | |
110 | .unwrap(); | |
111 | cvt_nz(libc::pthread_mutex_init(self.inner.get(), attr.0.as_ptr())).unwrap(); | |
9346a6ac AL |
112 | } |
113 | ||
114 | pub unsafe fn lock(&self) { | |
92a42be0 | 115 | let result = libc::pthread_mutex_lock(self.inner.get()); |
9346a6ac AL |
116 | debug_assert_eq!(result, 0); |
117 | } | |
118 | ||
119 | #[inline] | |
120 | pub unsafe fn try_lock(&self) -> bool { | |
92a42be0 | 121 | libc::pthread_mutex_trylock(self.inner.get()) == 0 |
9346a6ac AL |
122 | } |
123 | ||
124 | pub unsafe fn unlock(&self) { | |
92a42be0 | 125 | let result = libc::pthread_mutex_unlock(self.inner.get()); |
9346a6ac AL |
126 | debug_assert_eq!(result, 0); |
127 | } | |
128 | ||
129 | pub unsafe fn destroy(&self) { | |
92a42be0 | 130 | let result = libc::pthread_mutex_destroy(self.inner.get()); |
9346a6ac AL |
131 | debug_assert_eq!(result, 0); |
132 | } | |
133 | } | |
29967ef6 XL |
134 | |
135 | struct PthreadMutexAttr<'a>(&'a mut MaybeUninit<libc::pthread_mutexattr_t>); | |
136 | ||
137 | impl Drop for PthreadMutexAttr<'_> { | |
138 | fn drop(&mut self) { | |
139 | unsafe { | |
140 | let result = libc::pthread_mutexattr_destroy(self.0.as_mut_ptr()); | |
141 | debug_assert_eq!(result, 0); | |
142 | } | |
143 | } | |
144 | } |