Skip to content

Commit f0d46f3

Browse files
committed
Implement reentrant mutexes and make stdio use them
write_fmt calls write for each formatted field. The default implementation of write_fmt is used, which will call write on not-yet-locked stdout (and write locking after), therefore making print! in multithreaded environment still interleave contents of two separate prints. This patch implements reentrant mutexes, changes stdio handles to use these mutexes and overrides write_fmt to lock the stdio handle for the whole duration of the call.
1 parent 80def6c commit f0d46f3

File tree

8 files changed

+430
-27
lines changed

8 files changed

+430
-27
lines changed

src/libstd/io/stdio.rs

+25-18
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ use cmp;
1616
use fmt;
1717
use io::lazy::Lazy;
1818
use io::{self, BufReader, LineWriter};
19-
use sync::{Arc, Mutex, MutexGuard};
19+
use sync::{Arc, Mutex, ReentrantMutex, MutexGuard, ReentrantMutexGuard};
2020
use sys::stdio;
2121

2222
/// Stdout used by print! and println! macros
@@ -210,7 +210,7 @@ pub struct Stdout {
210210
// FIXME: this should be LineWriter or BufWriter depending on the state of
211211
// stdout (tty or not). Note that if this is not line buffered it
212212
// should also flush-on-panic or some form of flush-on-abort.
213-
inner: Arc<Mutex<LineWriter<StdoutRaw>>>,
213+
inner: Arc<ReentrantMutex<LineWriter<StdoutRaw>>>,
214214
}
215215

216216
/// A locked reference to the a `Stdout` handle.
@@ -219,7 +219,7 @@ pub struct Stdout {
219219
/// method on `Stdout`.
220220
#[stable(feature = "rust1", since = "1.0.0")]
221221
pub struct StdoutLock<'a> {
222-
inner: MutexGuard<'a, LineWriter<StdoutRaw>>,
222+
inner: ReentrantMutexGuard<'a, LineWriter<StdoutRaw>>,
223223
}
224224

225225
/// Constructs a new reference to the standard output of the current process.
@@ -231,13 +231,13 @@ pub struct StdoutLock<'a> {
231231
/// The returned handle implements the `Write` trait.
232232
#[stable(feature = "rust1", since = "1.0.0")]
233233
pub fn stdout() -> Stdout {
234-
static INSTANCE: Lazy<Mutex<LineWriter<StdoutRaw>>> = lazy_init!(stdout_init);
234+
static INSTANCE: Lazy<ReentrantMutex<LineWriter<StdoutRaw>>> = lazy_init!(stdout_init);
235235
return Stdout {
236236
inner: INSTANCE.get().expect("cannot access stdout during shutdown"),
237237
};
238238

239-
fn stdout_init() -> Arc<Mutex<LineWriter<StdoutRaw>>> {
240-
Arc::new(Mutex::new(LineWriter::new(stdout_raw())))
239+
fn stdout_init() -> Arc<ReentrantMutex<LineWriter<StdoutRaw>>> {
240+
Arc::new(ReentrantMutex::new(LineWriter::new(stdout_raw())))
241241
}
242242
}
243243

@@ -264,23 +264,26 @@ impl Write for Stdout {
264264
fn write_all(&mut self, buf: &[u8]) -> io::Result<()> {
265265
self.lock().write_all(buf)
266266
}
267-
// Don't override write_fmt as it's possible to run arbitrary code during a
268-
// write_fmt, allowing the possibility of a recursive lock (aka deadlock)
267+
fn write_fmt(&mut self, args: fmt::Arguments) -> io::Result<()> {
268+
self.lock().write_fmt(args)
269+
}
269270
}
270271
#[stable(feature = "rust1", since = "1.0.0")]
271272
impl<'a> Write for StdoutLock<'a> {
272273
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
273-
self.inner.write(&buf[..cmp::min(buf.len(), OUT_MAX)])
274+
self.inner.borrow_mut().write(&buf[..cmp::min(buf.len(), OUT_MAX)])
275+
}
276+
fn flush(&mut self) -> io::Result<()> {
277+
self.inner.borrow_mut().flush()
274278
}
275-
fn flush(&mut self) -> io::Result<()> { self.inner.flush() }
276279
}
277280

278281
/// A handle to the standard error stream of a process.
279282
///
280283
/// For more information, see `stderr`
281284
#[stable(feature = "rust1", since = "1.0.0")]
282285
pub struct Stderr {
283-
inner: Arc<Mutex<StderrRaw>>,
286+
inner: Arc<ReentrantMutex<StderrRaw>>,
284287
}
285288

286289
/// A locked reference to the a `Stderr` handle.
@@ -289,7 +292,7 @@ pub struct Stderr {
289292
/// method on `Stderr`.
290293
#[stable(feature = "rust1", since = "1.0.0")]
291294
pub struct StderrLock<'a> {
292-
inner: MutexGuard<'a, StderrRaw>,
295+
inner: ReentrantMutexGuard<'a, StderrRaw>,
293296
}
294297

295298
/// Constructs a new reference to the standard error stream of a process.
@@ -300,13 +303,13 @@ pub struct StderrLock<'a> {
300303
/// The returned handle implements the `Write` trait.
301304
#[stable(feature = "rust1", since = "1.0.0")]
302305
pub fn stderr() -> Stderr {
303-
static INSTANCE: Lazy<Mutex<StderrRaw>> = lazy_init!(stderr_init);
306+
static INSTANCE: Lazy<ReentrantMutex<StderrRaw>> = lazy_init!(stderr_init);
304307
return Stderr {
305308
inner: INSTANCE.get().expect("cannot access stderr during shutdown"),
306309
};
307310

308-
fn stderr_init() -> Arc<Mutex<StderrRaw>> {
309-
Arc::new(Mutex::new(stderr_raw()))
311+
fn stderr_init() -> Arc<ReentrantMutex<StderrRaw>> {
312+
Arc::new(ReentrantMutex::new(stderr_raw()))
310313
}
311314
}
312315

@@ -333,14 +336,18 @@ impl Write for Stderr {
333336
fn write_all(&mut self, buf: &[u8]) -> io::Result<()> {
334337
self.lock().write_all(buf)
335338
}
336-
// Don't override write_fmt for the same reasons as Stdout
339+
fn write_fmt(&mut self, args: fmt::Arguments) -> io::Result<()> {
340+
self.lock().write_fmt(args)
341+
}
337342
}
338343
#[stable(feature = "rust1", since = "1.0.0")]
339344
impl<'a> Write for StderrLock<'a> {
340345
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
341-
self.inner.write(&buf[..cmp::min(buf.len(), OUT_MAX)])
346+
self.inner.borrow_mut().write(&buf[..cmp::min(buf.len(), OUT_MAX)])
347+
}
348+
fn flush(&mut self) -> io::Result<()> {
349+
self.inner.borrow_mut().flush()
342350
}
343-
fn flush(&mut self) -> io::Result<()> { self.inner.flush() }
344351
}
345352

346353
/// Resets the task-local stderr handle to the specified writer

src/libstd/sync/mod.rs

+8-6
Original file line numberDiff line numberDiff line change
@@ -20,15 +20,16 @@
2020
pub use alloc::arc::{Arc, Weak};
2121
pub use core::atomic;
2222

23-
pub use self::mutex::{Mutex, MutexGuard, StaticMutex};
24-
pub use self::mutex::MUTEX_INIT;
25-
pub use self::rwlock::{RwLock, StaticRwLock, RW_LOCK_INIT};
26-
pub use self::rwlock::{RwLockReadGuard, RwLockWriteGuard};
23+
pub use self::barrier::{Barrier, BarrierWaitResult};
2724
pub use self::condvar::{Condvar, StaticCondvar, CONDVAR_INIT};
25+
pub use self::mutex::MUTEX_INIT;
26+
pub use self::mutex::{Mutex, MutexGuard, StaticMutex};
2827
pub use self::once::{Once, ONCE_INIT};
29-
pub use self::semaphore::{Semaphore, SemaphoreGuard};
30-
pub use self::barrier::{Barrier, BarrierWaitResult};
3128
pub use self::poison::{PoisonError, TryLockError, TryLockResult, LockResult};
29+
pub use self::remutex::{ReentrantMutex, ReentrantMutexGuard};
30+
pub use self::rwlock::{RwLockReadGuard, RwLockWriteGuard};
31+
pub use self::rwlock::{RwLock, StaticRwLock, RW_LOCK_INIT};
32+
pub use self::semaphore::{Semaphore, SemaphoreGuard};
3233

3334
pub use self::future::Future;
3435

@@ -40,5 +41,6 @@ mod future;
4041
mod mutex;
4142
mod once;
4243
mod poison;
44+
mod remutex;
4345
mod rwlock;
4446
mod semaphore;

src/libstd/sync/mutex.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -212,7 +212,7 @@ impl<T> Mutex<T> {
212212

213213
/// Attempts to acquire this lock.
214214
///
215-
/// If the lock could not be acquired at this time, then `None` is returned.
215+
/// If the lock could not be acquired at this time, then `Err` is returned.
216216
/// Otherwise, an RAII guard is returned. The lock will be unlocked when the
217217
/// guard is dropped.
218218
///

0 commit comments

Comments
 (0)