Skip to content

Commit e542f32

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 e542f32

File tree

7 files changed

+135
-27
lines changed

7 files changed

+135
-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
///

src/libstd/sys/unix/mutex.rs

+36
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ use prelude::v1::*;
1212

1313
use cell::UnsafeCell;
1414
use sys::sync as ffi;
15+
use mem;
1516

1617
pub struct Mutex { inner: UnsafeCell<ffi::pthread_mutex_t> }
1718

@@ -67,3 +68,38 @@ impl Mutex {
6768
debug_assert!(r == 0 || r == libc::EINVAL);
6869
}
6970
}
71+
72+
pub struct ReentrantMutex { inner: UnsafeCell<ffi::pthread_mutex_t> }
73+
74+
unsafe impl Send for ReentrantMutex {}
75+
unsafe impl Sync for ReentrantMutex {}
76+
77+
impl ReentrantMutex {
78+
pub unsafe fn new() -> ReentrantMutex {
79+
let mut mutex: ffi::pthread_mutex_t = mem::uninitialized();
80+
let mut attr: ffi::pthread_mutexattr_t = mem::uninitialized();
81+
debug_assert_eq!(ffi::pthread_mutexattr_init(&mut attr as *mut _), 0);
82+
debug_assert_eq!(ffi::pthread_mutexattr_settype(&mut attr as *mut _,
83+
ffi::PTHREAD_MUTEX_RECURSIVE), 0);
84+
debug_assert_eq!(ffi::pthread_mutex_init(&mut mutex as *mut _, &attr as *const _), 0);
85+
debug_assert_eq!(ffi::pthread_mutexattr_destroy(&mut attr as *mut _), 0);
86+
ReentrantMutex { inner: UnsafeCell { value: mutex } }
87+
}
88+
89+
pub unsafe fn lock(&self) {
90+
debug_assert_eq!(ffi::pthread_mutex_lock(self.inner.get()), 0);
91+
}
92+
93+
#[inline]
94+
pub unsafe fn try_lock(&self) -> bool {
95+
ffi::pthread_mutex_trylock(self.inner.get()) == 0
96+
}
97+
98+
pub unsafe fn unlock(&self) {
99+
debug_assert_eq!(ffi::pthread_mutex_unlock(self.inner.get()), 0);
100+
}
101+
102+
pub unsafe fn destroy(&self) {
103+
debug_assert_eq!(ffi::pthread_mutex_destroy(self.inner.get()), 0);
104+
}
105+
}

src/libstd/sys/unix/sync.rs

+27-1
Original file line numberDiff line numberDiff line change
@@ -12,17 +12,25 @@
1212

1313
use libc;
1414

15-
pub use self::os::{PTHREAD_MUTEX_INITIALIZER, pthread_mutex_t};
15+
pub use self::os::{PTHREAD_MUTEX_INITIALIZER, PTHREAD_MUTEX_RECURSIVE, pthread_mutex_t,
16+
pthread_mutexattr_t};
1617
pub use self::os::{PTHREAD_COND_INITIALIZER, pthread_cond_t};
1718
pub use self::os::{PTHREAD_RWLOCK_INITIALIZER, pthread_rwlock_t};
1819

1920
extern {
2021
// mutexes
22+
pub fn pthread_mutex_init(lock: *mut pthread_mutex_t, attr: *const pthread_mutexattr_t)
23+
-> libc::c_int;
2124
pub fn pthread_mutex_destroy(lock: *mut pthread_mutex_t) -> libc::c_int;
2225
pub fn pthread_mutex_lock(lock: *mut pthread_mutex_t) -> libc::c_int;
2326
pub fn pthread_mutex_trylock(lock: *mut pthread_mutex_t) -> libc::c_int;
2427
pub fn pthread_mutex_unlock(lock: *mut pthread_mutex_t) -> libc::c_int;
2528

29+
pub fn pthread_mutexattr_init(attr: *mut pthread_mutexattr_t) -> libc::c_int;
30+
pub fn pthread_mutexattr_destroy(attr: *mut pthread_mutexattr_t) -> libc::c_int;
31+
pub fn pthread_mutexattr_settype(attr: *mut pthread_mutexattr_t, _type: libc::c_int)
32+
-> libc::c_int;
33+
2634
// cvars
2735
pub fn pthread_cond_wait(cond: *mut pthread_cond_t,
2836
lock: *mut pthread_mutex_t) -> libc::c_int;
@@ -52,12 +60,14 @@ mod os {
5260
use libc;
5361

5462
pub type pthread_mutex_t = *mut libc::c_void;
63+
pub type pthread_mutexattr_t = *mut libc::c_void;
5564
pub type pthread_cond_t = *mut libc::c_void;
5665
pub type pthread_rwlock_t = *mut libc::c_void;
5766

5867
pub const PTHREAD_MUTEX_INITIALIZER: pthread_mutex_t = 0 as *mut _;
5968
pub const PTHREAD_COND_INITIALIZER: pthread_cond_t = 0 as *mut _;
6069
pub const PTHREAD_RWLOCK_INITIALIZER: pthread_rwlock_t = 0 as *mut _;
70+
pub const PTHREAD_MUTEX_RECURSIVE: libc::c_int = 2;
6171
}
6272

6373
#[cfg(any(target_os = "macos", target_os = "ios"))]
@@ -95,6 +105,11 @@ mod os {
95105
__opaque: [u8; __PTHREAD_MUTEX_SIZE__],
96106
}
97107
#[repr(C)]
108+
pub struct pthread_mutexattr_t {
109+
__sig: libc::c_long,
110+
__opaque: [u8; 4],
111+
}
112+
#[repr(C)]
98113
pub struct pthread_cond_t {
99114
__sig: libc::c_long,
100115
__opaque: [u8; __PTHREAD_COND_SIZE__],
@@ -117,6 +132,8 @@ mod os {
117132
__sig: _PTHREAD_RWLOCK_SIG_INIT,
118133
__opaque: [0; __PTHREAD_RWLOCK_SIZE__],
119134
};
135+
136+
pub const PTHREAD_MUTEX_RECURSIVE: libc::c_int = 8;
120137
}
121138

122139
#[cfg(target_os = "linux")]
@@ -161,6 +178,11 @@ mod os {
161178
size: [u8; __SIZEOF_PTHREAD_MUTEX_T],
162179
}
163180
#[repr(C)]
181+
pub struct pthread_mutexattr_t {
182+
__align: libc::c_longlong,
183+
size: [u8; 4],
184+
}
185+
#[repr(C)]
164186
pub struct pthread_cond_t {
165187
__align: libc::c_longlong,
166188
size: [u8; __SIZEOF_PTHREAD_COND_T],
@@ -183,6 +205,7 @@ mod os {
183205
__align: 0,
184206
size: [0; __SIZEOF_PTHREAD_RWLOCK_T],
185207
};
208+
pub const PTHREAD_MUTEX_RECURSIVE: libc::c_int = 1;
186209
}
187210
#[cfg(target_os = "android")]
188211
mod os {
@@ -191,6 +214,8 @@ mod os {
191214
#[repr(C)]
192215
pub struct pthread_mutex_t { value: libc::c_int }
193216
#[repr(C)]
217+
pub struct pthread_mutexattr_t { value: libc::c_long }
218+
#[repr(C)]
194219
pub struct pthread_cond_t { value: libc::c_int }
195220
#[repr(C)]
196221
pub struct pthread_rwlock_t {
@@ -218,4 +243,5 @@ mod os {
218243
pendingWriters: 0,
219244
reserved: [0 as *mut _; 4],
220245
};
246+
pub const PTHREAD_MUTEX_RECURSIVE: libc::c_int = 1;
221247
}

src/libstd/sys/windows/mutex.rs

+32
Original file line numberDiff line numberDiff line change
@@ -57,3 +57,35 @@ impl Mutex {
5757
// ...
5858
}
5959
}
60+
61+
pub struct ReentrantMutex { inner: UnsafeCell<ffi::CRITICAL_SECTION> }
62+
63+
unsafe impl Send for ReentrantMutex {}
64+
unsafe impl Sync for ReentrantMutex {}
65+
66+
impl ReentrantMutex {
67+
pub unsafe fn new() -> ReentrantMutex {
68+
let section = CRITICAL_SECTION_INIT;
69+
ffi::InitializeCriticalSection(section);
70+
ReentrantMutex {
71+
inner: UnsafeCell { value: section }
72+
}
73+
}
74+
75+
pub unsafe fn lock(&self) {
76+
ffi::EnterCriticalSection(self.inner.get());
77+
}
78+
79+
#[inline]
80+
pub unsafe fn try_lock(&self) -> bool {
81+
ffi::TryEnterCriticalSection(self.inner.get()) != 0
82+
}
83+
84+
pub unsafe fn unlock(&self) {
85+
ffi::LeaveCriticalSection(self.inner.get());
86+
}
87+
88+
pub unsafe fn destroy(&self) {
89+
ffi::DeleteCriticalSection(self.inner.get());
90+
}
91+
}

src/libstd/sys/windows/sync.rs

+9
Original file line numberDiff line numberDiff line change
@@ -19,11 +19,14 @@ pub type ULONG = c_ulong;
1919
pub struct CONDITION_VARIABLE { pub ptr: LPVOID }
2020
#[repr(C)]
2121
pub struct SRWLOCK { pub ptr: LPVOID }
22+
#[repr(C)]
23+
pub struct CRITICAL_SECTION { pub ptr: LPVOID }
2224

2325
pub const CONDITION_VARIABLE_INIT: CONDITION_VARIABLE = CONDITION_VARIABLE {
2426
ptr: 0 as *mut _,
2527
};
2628
pub const SRWLOCK_INIT: SRWLOCK = SRWLOCK { ptr: 0 as *mut _ };
29+
pub const CRITICAL_SECTION_INIT: CRITICAL_SECTION = CRITICAL_SECTION { ptr: 0 as *mut _ };
2730

2831
extern "system" {
2932
// condition variables
@@ -41,4 +44,10 @@ extern "system" {
4144
pub fn ReleaseSRWLockShared(SRWLock: PSRWLOCK);
4245
pub fn TryAcquireSRWLockExclusive(SRWLock: PSRWLOCK) -> BOOLEAN;
4346
pub fn TryAcquireSRWLockShared(SRWLock: PSRWLOCK) -> BOOLEAN;
47+
48+
pub fn InitializeCriticalSection(CriticalSection: CRITICAL_SECTION);
49+
pub fn EnterCriticalSection(CriticalSection: CRITICAL_SECTION);
50+
pub fn TryEnterCriticalSection(CriticalSection: CRITICAL_SECTION) -> BOOLEAN;
51+
pub fn LeaveCriticalSection(CriticalSection: CRITICAL_SECTION);
52+
pub fn DeleteCriticalSection(CriticalSection: CRITICAL_SECTION);
4453
}

0 commit comments

Comments
 (0)