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