Skip to content

Commit 17c7614

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 17c7614

File tree

8 files changed

+383
-27
lines changed

8 files changed

+383
-27
lines changed

src/libstd/io/stdio.rs

+22-19
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, ReentrantMutex, ReentrantMutexGuard};
2020
use sys::stdio;
2121

2222
/// Stdout used by print! and println! macros
@@ -96,7 +96,7 @@ impl Write for StderrRaw {
9696
/// of `Stdin` must be executed with care.
9797
#[stable(feature = "rust1", since = "1.0.0")]
9898
pub struct Stdin {
99-
inner: Arc<Mutex<BufReader<StdinRaw>>>,
99+
inner: Arc<ReentrantMutex<BufReader<StdinRaw>>>,
100100
}
101101

102102
/// A locked reference to the a `Stdin` handle.
@@ -105,7 +105,7 @@ pub struct Stdin {
105105
/// constructed via the `lock` method on `Stdin`.
106106
#[stable(feature = "rust1", since = "1.0.0")]
107107
pub struct StdinLock<'a> {
108-
inner: MutexGuard<'a, BufReader<StdinRaw>>,
108+
inner: ReentrantMutexGuard<'a, BufReader<StdinRaw>>,
109109
}
110110

111111
/// Create a new handle to the global standard input stream of this process.
@@ -119,17 +119,17 @@ pub struct StdinLock<'a> {
119119
/// locked version, `StdinLock`, implements both `Read` and `BufRead`, however.
120120
#[stable(feature = "rust1", since = "1.0.0")]
121121
pub fn stdin() -> Stdin {
122-
static INSTANCE: Lazy<Mutex<BufReader<StdinRaw>>> = lazy_init!(stdin_init);
122+
static INSTANCE: Lazy<ReentrantMutex<BufReader<StdinRaw>>> = lazy_init!(stdin_init);
123123
return Stdin {
124124
inner: INSTANCE.get().expect("cannot access stdin during shutdown"),
125125
};
126126

127-
fn stdin_init() -> Arc<Mutex<BufReader<StdinRaw>>> {
127+
fn stdin_init() -> Arc<ReentrantMutex<BufReader<StdinRaw>>> {
128128
// The default buffer capacity is 64k, but apparently windows
129129
// doesn't like 64k reads on stdin. See #13304 for details, but the
130130
// idea is that on windows we use a slightly smaller buffer that's
131131
// been seen to be acceptable.
132-
Arc::new(Mutex::new(if cfg!(windows) {
132+
Arc::new(ReentrantMutex::new(if cfg!(windows) {
133133
BufReader::with_capacity(8 * 1024, stdin_raw())
134134
} else {
135135
BufReader::new(stdin_raw())
@@ -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,8 +264,9 @@ 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> {
@@ -280,7 +281,7 @@ impl<'a> Write for StdoutLock<'a> {
280281
/// For more information, see `stderr`
281282
#[stable(feature = "rust1", since = "1.0.0")]
282283
pub struct Stderr {
283-
inner: Arc<Mutex<StderrRaw>>,
284+
inner: Arc<ReentrantMutex<StderrRaw>>,
284285
}
285286

286287
/// A locked reference to the a `Stderr` handle.
@@ -289,7 +290,7 @@ pub struct Stderr {
289290
/// method on `Stderr`.
290291
#[stable(feature = "rust1", since = "1.0.0")]
291292
pub struct StderrLock<'a> {
292-
inner: MutexGuard<'a, StderrRaw>,
293+
inner: ReentrantMutexGuard<'a, StderrRaw>,
293294
}
294295

295296
/// Constructs a new reference to the standard error stream of a process.
@@ -300,13 +301,13 @@ pub struct StderrLock<'a> {
300301
/// The returned handle implements the `Write` trait.
301302
#[stable(feature = "rust1", since = "1.0.0")]
302303
pub fn stderr() -> Stderr {
303-
static INSTANCE: Lazy<Mutex<StderrRaw>> = lazy_init!(stderr_init);
304+
static INSTANCE: Lazy<ReentrantMutex<StderrRaw>> = lazy_init!(stderr_init);
304305
return Stderr {
305306
inner: INSTANCE.get().expect("cannot access stderr during shutdown"),
306307
};
307308

308-
fn stderr_init() -> Arc<Mutex<StderrRaw>> {
309-
Arc::new(Mutex::new(stderr_raw()))
309+
fn stderr_init() -> Arc<ReentrantMutex<StderrRaw>> {
310+
Arc::new(ReentrantMutex::new(stderr_raw()))
310311
}
311312
}
312313

@@ -333,7 +334,9 @@ impl Write for Stderr {
333334
fn write_all(&mut self, buf: &[u8]) -> io::Result<()> {
334335
self.lock().write_all(buf)
335336
}
336-
// Don't override write_fmt for the same reasons as Stdout
337+
fn write_fmt(&mut self, args: fmt::Arguments) -> io::Result<()> {
338+
self.lock().write_fmt(args)
339+
}
337340
}
338341
#[stable(feature = "rust1", since = "1.0.0")]
339342
impl<'a> Write for StderrLock<'a> {

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)