Skip to content

Commit 45aa6c8

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 45aa6c8

File tree

13 files changed

+446
-34
lines changed

13 files changed

+446
-34
lines changed

src/libstd/io/stdio.rs

+25-17
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ use io::lazy::Lazy;
1818
use io::{self, BufReader, LineWriter};
1919
use sync::{Arc, Mutex, MutexGuard};
2020
use sys::stdio;
21+
use sys_common::remutex::{ReentrantMutex, ReentrantMutexGuard};
2122

2223
/// Stdout used by print! and println! macros
2324
thread_local! {
@@ -210,7 +211,7 @@ pub struct Stdout {
210211
// FIXME: this should be LineWriter or BufWriter depending on the state of
211212
// stdout (tty or not). Note that if this is not line buffered it
212213
// should also flush-on-panic or some form of flush-on-abort.
213-
inner: Arc<Mutex<LineWriter<StdoutRaw>>>,
214+
inner: Arc<ReentrantMutex<RefCell<LineWriter<StdoutRaw>>>>,
214215
}
215216

216217
/// A locked reference to the a `Stdout` handle.
@@ -219,7 +220,7 @@ pub struct Stdout {
219220
/// method on `Stdout`.
220221
#[stable(feature = "rust1", since = "1.0.0")]
221222
pub struct StdoutLock<'a> {
222-
inner: MutexGuard<'a, LineWriter<StdoutRaw>>,
223+
inner: ReentrantMutexGuard<'a, RefCell<LineWriter<StdoutRaw>>>,
223224
}
224225

225226
/// Constructs a new reference to the standard output of the current process.
@@ -231,13 +232,13 @@ pub struct StdoutLock<'a> {
231232
/// The returned handle implements the `Write` trait.
232233
#[stable(feature = "rust1", since = "1.0.0")]
233234
pub fn stdout() -> Stdout {
234-
static INSTANCE: Lazy<Mutex<LineWriter<StdoutRaw>>> = lazy_init!(stdout_init);
235+
static INSTANCE: Lazy<ReentrantMutex<RefCell<LineWriter<StdoutRaw>>>> = lazy_init!(stdout_init);
235236
return Stdout {
236237
inner: INSTANCE.get().expect("cannot access stdout during shutdown"),
237238
};
238239

239-
fn stdout_init() -> Arc<Mutex<LineWriter<StdoutRaw>>> {
240-
Arc::new(Mutex::new(LineWriter::new(stdout_raw())))
240+
fn stdout_init() -> Arc<ReentrantMutex<RefCell<LineWriter<StdoutRaw>>>> {
241+
Arc::new(ReentrantMutex::new(RefCell::new(LineWriter::new(stdout_raw()))))
241242
}
242243
}
243244

@@ -264,23 +265,26 @@ impl Write for Stdout {
264265
fn write_all(&mut self, buf: &[u8]) -> io::Result<()> {
265266
self.lock().write_all(buf)
266267
}
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)
268+
fn write_fmt(&mut self, args: fmt::Arguments) -> io::Result<()> {
269+
self.lock().write_fmt(args)
270+
}
269271
}
270272
#[stable(feature = "rust1", since = "1.0.0")]
271273
impl<'a> Write for StdoutLock<'a> {
272274
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
273-
self.inner.write(&buf[..cmp::min(buf.len(), OUT_MAX)])
275+
self.inner.borrow_mut().write(&buf[..cmp::min(buf.len(), OUT_MAX)])
276+
}
277+
fn flush(&mut self) -> io::Result<()> {
278+
self.inner.borrow_mut().flush()
274279
}
275-
fn flush(&mut self) -> io::Result<()> { self.inner.flush() }
276280
}
277281

278282
/// A handle to the standard error stream of a process.
279283
///
280284
/// For more information, see `stderr`
281285
#[stable(feature = "rust1", since = "1.0.0")]
282286
pub struct Stderr {
283-
inner: Arc<Mutex<StderrRaw>>,
287+
inner: Arc<ReentrantMutex<RefCell<StderrRaw>>>,
284288
}
285289

286290
/// A locked reference to the a `Stderr` handle.
@@ -289,7 +293,7 @@ pub struct Stderr {
289293
/// method on `Stderr`.
290294
#[stable(feature = "rust1", since = "1.0.0")]
291295
pub struct StderrLock<'a> {
292-
inner: MutexGuard<'a, StderrRaw>,
296+
inner: ReentrantMutexGuard<'a, RefCell<StderrRaw>>,
293297
}
294298

295299
/// Constructs a new reference to the standard error stream of a process.
@@ -300,13 +304,13 @@ pub struct StderrLock<'a> {
300304
/// The returned handle implements the `Write` trait.
301305
#[stable(feature = "rust1", since = "1.0.0")]
302306
pub fn stderr() -> Stderr {
303-
static INSTANCE: Lazy<Mutex<StderrRaw>> = lazy_init!(stderr_init);
307+
static INSTANCE: Lazy<ReentrantMutex<RefCell<StderrRaw>>> = lazy_init!(stderr_init);
304308
return Stderr {
305309
inner: INSTANCE.get().expect("cannot access stderr during shutdown"),
306310
};
307311

308-
fn stderr_init() -> Arc<Mutex<StderrRaw>> {
309-
Arc::new(Mutex::new(stderr_raw()))
312+
fn stderr_init() -> Arc<ReentrantMutex<RefCell<StderrRaw>>> {
313+
Arc::new(ReentrantMutex::new(RefCell::new(stderr_raw())))
310314
}
311315
}
312316

@@ -333,14 +337,18 @@ impl Write for Stderr {
333337
fn write_all(&mut self, buf: &[u8]) -> io::Result<()> {
334338
self.lock().write_all(buf)
335339
}
336-
// Don't override write_fmt for the same reasons as Stdout
340+
fn write_fmt(&mut self, args: fmt::Arguments) -> io::Result<()> {
341+
self.lock().write_fmt(args)
342+
}
337343
}
338344
#[stable(feature = "rust1", since = "1.0.0")]
339345
impl<'a> Write for StderrLock<'a> {
340346
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
341-
self.inner.write(&buf[..cmp::min(buf.len(), OUT_MAX)])
347+
self.inner.borrow_mut().write(&buf[..cmp::min(buf.len(), OUT_MAX)])
348+
}
349+
fn flush(&mut self) -> io::Result<()> {
350+
self.inner.borrow_mut().flush()
342351
}
343-
fn flush(&mut self) -> io::Result<()> { self.inner.flush() }
344352
}
345353

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

src/libstd/sync/condvar.rs

+3-3
Original file line numberDiff line numberDiff line change
@@ -11,12 +11,12 @@
1111
use prelude::v1::*;
1212

1313
use sync::atomic::{AtomicUsize, Ordering, ATOMIC_USIZE_INIT};
14-
use sync::poison::{self, LockResult};
15-
use sys::time::SteadyTime;
14+
use sync::{mutex, MutexGuard, PoisonError};
1615
use sys_common::condvar as sys;
1716
use sys_common::mutex as sys_mutex;
17+
use sys_common::poison::{self, LockResult};
18+
use sys::time::SteadyTime;
1819
use time::Duration;
19-
use sync::{mutex, MutexGuard, PoisonError};
2020

2121
/// A Condition Variable
2222
///

src/libstd/sync/mod.rs

+6-7
Original file line numberDiff line numberDiff line change
@@ -20,15 +20,15 @@
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};
28+
pub use sys_common::poison::{PoisonError, TryLockError, TryLockResult, LockResult};
29+
pub use self::rwlock::{RwLockReadGuard, RwLockWriteGuard};
30+
pub use self::rwlock::{RwLock, StaticRwLock, RW_LOCK_INIT};
2931
pub use self::semaphore::{Semaphore, SemaphoreGuard};
30-
pub use self::barrier::{Barrier, BarrierWaitResult};
31-
pub use self::poison::{PoisonError, TryLockError, TryLockResult, LockResult};
3232

3333
pub use self::future::Future;
3434

@@ -39,6 +39,5 @@ mod condvar;
3939
mod future;
4040
mod mutex;
4141
mod once;
42-
mod poison;
4342
mod rwlock;
4443
mod semaphore;

src/libstd/sync/mutex.rs

+3-3
Original file line numberDiff line numberDiff line change
@@ -11,11 +11,11 @@
1111
use prelude::v1::*;
1212

1313
use cell::UnsafeCell;
14+
use fmt;
1415
use marker;
1516
use ops::{Deref, DerefMut};
16-
use sync::poison::{self, TryLockError, TryLockResult, LockResult};
1717
use sys_common::mutex as sys;
18-
use fmt;
18+
use sys_common::poison::{self, TryLockError, TryLockResult, LockResult};
1919

2020
/// A mutual exclusion primitive useful for protecting shared data
2121
///
@@ -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
///

src/libstd/sync/rwlock.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -11,11 +11,11 @@
1111
use prelude::v1::*;
1212

1313
use cell::UnsafeCell;
14+
use fmt;
1415
use marker;
1516
use ops::{Deref, DerefMut};
16-
use sync::poison::{self, LockResult, TryLockError, TryLockResult};
17+
use sys_common::poison::{self, LockResult, TryLockError, TryLockResult};
1718
use sys_common::rwlock as sys;
18-
use fmt;
1919

2020
/// A reader-writer lock
2121
///

src/libstd/sys/common/mod.rs

+2
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,8 @@ pub mod condvar;
2929
pub mod mutex;
3030
pub mod net;
3131
pub mod net2;
32+
pub mod poison;
33+
pub mod remutex;
3234
pub mod rwlock;
3335
pub mod stack;
3436
pub mod thread;
File renamed without changes.

0 commit comments

Comments
 (0)