Skip to content

Commit ac7b9dd

Browse files
committed
Audit usage of NativeMutex
Once a native mutex has been used once, it is never allowed to be moved again. This is because some pthreads implementations take pointers inside the mutex itself. This commit adds stern wording around the methods on native mutexes, and fixes one use case in the codebase. The Mutex type in libsync was susceptible to movement, so the inner static mutex is now boxed to ensure that the address of the native mutex is constant.
1 parent b612ae9 commit ac7b9dd

File tree

3 files changed

+87
-4
lines changed

3 files changed

+87
-4
lines changed

src/librustrt/mutex.rs

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,18 @@ impl StaticNativeMutex {
115115
/// // critical section...
116116
/// } // automatically unlocked in `_guard`'s destructor
117117
/// ```
118+
///
119+
/// # Unsafety
120+
///
121+
/// This method is unsafe because it will not function correctly if this
122+
/// mutex has been *moved* since it was last used. The mutex can move an
123+
/// arbitrary number of times before its first usage, but once a mutex has
124+
/// been used once it is no longer allowed to move (or otherwise it invokes
125+
/// undefined behavior).
126+
///
127+
/// Additionally, this type does not take into account any form of
128+
/// scheduling model. This will unconditionally block the *os thread* which
129+
/// is not always desired.
118130
pub unsafe fn lock<'a>(&'a self) -> LockGuard<'a> {
119131
self.inner.lock();
120132

@@ -123,6 +135,10 @@ impl StaticNativeMutex {
123135

124136
/// Attempts to acquire the lock. The value returned is `Some` if
125137
/// the attempt succeeded.
138+
///
139+
/// # Unsafety
140+
///
141+
/// This method is unsafe for the same reasons as `lock`.
126142
pub unsafe fn trylock<'a>(&'a self) -> Option<LockGuard<'a>> {
127143
if self.inner.trylock() {
128144
Some(LockGuard { lock: self })
@@ -135,6 +151,12 @@ impl StaticNativeMutex {
135151
///
136152
/// These needs to be paired with a call to `.unlock_noguard`. Prefer using
137153
/// `.lock`.
154+
///
155+
/// # Unsafety
156+
///
157+
/// This method is unsafe for the same reasons as `lock`. Additionally, this
158+
/// does not guarantee that the mutex will ever be unlocked, and it is
159+
/// undefined to drop an already-locked mutex.
138160
pub unsafe fn lock_noguard(&self) { self.inner.lock() }
139161

140162
/// Attempts to acquire the lock without creating a
@@ -143,22 +165,42 @@ impl StaticNativeMutex {
143165
///
144166
/// If `true` is returned, this needs to be paired with a call to
145167
/// `.unlock_noguard`. Prefer using `.trylock`.
168+
///
169+
/// # Unsafety
170+
///
171+
/// This method is unsafe for the same reasons as `lock_noguard`.
146172
pub unsafe fn trylock_noguard(&self) -> bool {
147173
self.inner.trylock()
148174
}
149175

150176
/// Unlocks the lock. This assumes that the current thread already holds the
151177
/// lock.
178+
///
179+
/// # Unsafety
180+
///
181+
/// This method is unsafe for the same reasons as `lock`. Additionally, it
182+
/// is not guaranteed that this is unlocking a previously locked mutex. It
183+
/// is undefined to unlock an unlocked mutex.
152184
pub unsafe fn unlock_noguard(&self) { self.inner.unlock() }
153185

154186
/// Block on the internal condition variable.
155187
///
156188
/// This function assumes that the lock is already held. Prefer
157189
/// using `LockGuard.wait` since that guarantees that the lock is
158190
/// held.
191+
///
192+
/// # Unsafety
193+
///
194+
/// This method is unsafe for the same reasons as `lock`. Additionally, this
195+
/// is unsafe because the mutex may not be currently locked.
159196
pub unsafe fn wait_noguard(&self) { self.inner.wait() }
160197

161198
/// Signals a thread in `wait` to wake up
199+
///
200+
/// # Unsafety
201+
///
202+
/// This method is unsafe for the same reasons as `lock`. Additionally, this
203+
/// is unsafe because the mutex may not be currently locked.
162204
pub unsafe fn signal_noguard(&self) { self.inner.signal() }
163205

164206
/// This function is especially unsafe because there are no guarantees made
@@ -181,6 +223,7 @@ impl NativeMutex {
181223
/// already hold the lock.
182224
///
183225
/// # Example
226+
///
184227
/// ```rust
185228
/// use std::rt::mutex::NativeMutex;
186229
/// unsafe {
@@ -192,12 +235,22 @@ impl NativeMutex {
192235
/// } // automatically unlocked in `_guard`'s destructor
193236
/// }
194237
/// ```
238+
///
239+
/// # Unsafety
240+
///
241+
/// This method is unsafe due to the same reasons as
242+
/// `StaticNativeMutex::lock`.
195243
pub unsafe fn lock<'a>(&'a self) -> LockGuard<'a> {
196244
self.inner.lock()
197245
}
198246

199247
/// Attempts to acquire the lock. The value returned is `Some` if
200248
/// the attempt succeeded.
249+
///
250+
/// # Unsafety
251+
///
252+
/// This method is unsafe due to the same reasons as
253+
/// `StaticNativeMutex::trylock`.
201254
pub unsafe fn trylock<'a>(&'a self) -> Option<LockGuard<'a>> {
202255
self.inner.trylock()
203256
}
@@ -206,6 +259,11 @@ impl NativeMutex {
206259
///
207260
/// These needs to be paired with a call to `.unlock_noguard`. Prefer using
208261
/// `.lock`.
262+
///
263+
/// # Unsafety
264+
///
265+
/// This method is unsafe due to the same reasons as
266+
/// `StaticNativeMutex::lock_noguard`.
209267
pub unsafe fn lock_noguard(&self) { self.inner.lock_noguard() }
210268

211269
/// Attempts to acquire the lock without creating a
@@ -214,22 +272,42 @@ impl NativeMutex {
214272
///
215273
/// If `true` is returned, this needs to be paired with a call to
216274
/// `.unlock_noguard`. Prefer using `.trylock`.
275+
///
276+
/// # Unsafety
277+
///
278+
/// This method is unsafe due to the same reasons as
279+
/// `StaticNativeMutex::trylock_noguard`.
217280
pub unsafe fn trylock_noguard(&self) -> bool {
218281
self.inner.trylock_noguard()
219282
}
220283

221284
/// Unlocks the lock. This assumes that the current thread already holds the
222285
/// lock.
286+
///
287+
/// # Unsafety
288+
///
289+
/// This method is unsafe due to the same reasons as
290+
/// `StaticNativeMutex::unlock_noguard`.
223291
pub unsafe fn unlock_noguard(&self) { self.inner.unlock_noguard() }
224292

225293
/// Block on the internal condition variable.
226294
///
227295
/// This function assumes that the lock is already held. Prefer
228296
/// using `LockGuard.wait` since that guarantees that the lock is
229297
/// held.
298+
///
299+
/// # Unsafety
300+
///
301+
/// This method is unsafe due to the same reasons as
302+
/// `StaticNativeMutex::wait_noguard`.
230303
pub unsafe fn wait_noguard(&self) { self.inner.wait_noguard() }
231304

232305
/// Signals a thread in `wait` to wake up
306+
///
307+
/// # Unsafety
308+
///
309+
/// This method is unsafe due to the same reasons as
310+
/// `StaticNativeMutex::signal_noguard`.
233311
pub unsafe fn signal_noguard(&self) { self.inner.signal_noguard() }
234312
}
235313

src/librustrt/unwind.rs

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,6 @@ use libc::c_void;
7373

7474
use local::Local;
7575
use task::{Task, Result};
76-
use exclusive::Exclusive;
7776

7877
use uw = libunwind;
7978

@@ -88,7 +87,6 @@ struct Exception {
8887
}
8988

9089
pub type Callback = fn(msg: &Any:Send, file: &'static str, line: uint);
91-
type Queue = Exclusive<Vec<Callback>>;
9290

9391
// Variables used for invoking callbacks when a task starts to unwind.
9492
//

src/libsync/mutex.rs

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -97,7 +97,14 @@ pub static NATIVE_BLOCKED: uint = 1 << 2;
9797
/// drop(guard); // unlock the lock
9898
/// ```
9999
pub struct Mutex {
100-
lock: StaticMutex,
100+
// Note that this static mutex is in a *box*, not inlined into the struct
101+
// itself. This is done for memory safety reasons with the usage of a
102+
// StaticNativeMutex inside the static mutex above. Once a native mutex has
103+
// been used once, its address can never change (it can't be moved). This
104+
// mutex type can be safely moved at any time, so to ensure that the native
105+
// mutex is used correctly we box the inner lock to give it a constant
106+
// address.
107+
lock: Box<StaticMutex>,
101108
}
102109

103110
#[deriving(PartialEq, Show)]
@@ -458,7 +465,7 @@ impl Mutex {
458465
/// Creates a new mutex in an unlocked state ready for use.
459466
pub fn new() -> Mutex {
460467
Mutex {
461-
lock: StaticMutex {
468+
lock: box StaticMutex {
462469
state: atomics::AtomicUint::new(0),
463470
flavor: Unsafe::new(Unlocked),
464471
green_blocker: Unsafe::new(0),

0 commit comments

Comments
 (0)