Skip to content

Commit 619163e

Browse files
committed
Add futex-based ReentrantMutex on Linux.
1 parent 2f46de2 commit 619163e

File tree

2 files changed

+88
-6
lines changed

2 files changed

+88
-6
lines changed

library/std/src/sys/unix/locks/futex.rs

Lines changed: 87 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
1+
use crate::cell::UnsafeCell;
12
use crate::sync::atomic::{
2-
AtomicI32,
3+
AtomicI32, AtomicUsize,
34
Ordering::{Acquire, Relaxed, Release},
45
};
56
use crate::sys::futex::{futex_wait, futex_wake, futex_wake_all};
7+
use crate::sys_common::thread_info::current_thread_unique_ptr;
68
use crate::time::Duration;
79

810
pub type MovableMutex = Mutex;
@@ -162,3 +164,87 @@ impl Condvar {
162164
r
163165
}
164166
}
167+
168+
/// A reentrant mutex. Used by stdout().lock() and friends.
169+
///
170+
/// The 'owner' field tracks which thread has locked the mutex.
171+
///
172+
/// We use current_thread_unique_ptr() as the thread identifier,
173+
/// which is just the address of a thread local variable.
174+
///
175+
/// If `owner` is set to the identifier of the current thread,
176+
/// we assume the mutex is already locked and instead of locking it again,
177+
/// we increment `lock_count`.
178+
///
179+
/// When unlocking, we decrement `lock_count`, and only unlock the mutex when
180+
/// it reaches zero.
181+
///
182+
/// `lock_count` is protected by the mutex and only accessed by the thread that has
183+
/// locked the mutex, so needs no synchronization.
184+
///
185+
/// `owner` can be checked by other threads that want to see if they already
186+
/// hold the lock, so needs to be atomic. If it compares equal, we're on the
187+
/// same thread that holds the mutex and memory access can use relaxed ordering
188+
/// since we're not dealing with multiple threads. If it compares unequal,
189+
/// synchronization is left to the mutex, making relaxed memory ordering for
190+
/// the `owner` field fine in all cases.
191+
pub struct ReentrantMutex {
192+
mutex: Mutex,
193+
owner: AtomicUsize,
194+
lock_count: UnsafeCell<u32>,
195+
}
196+
197+
unsafe impl Send for ReentrantMutex {}
198+
unsafe impl Sync for ReentrantMutex {}
199+
200+
impl ReentrantMutex {
201+
#[inline]
202+
pub const unsafe fn uninitialized() -> Self {
203+
Self { mutex: Mutex::new(), owner: AtomicUsize::new(0), lock_count: UnsafeCell::new(0) }
204+
}
205+
206+
#[inline]
207+
pub unsafe fn init(&self) {}
208+
209+
#[inline]
210+
pub unsafe fn destroy(&self) {}
211+
212+
pub unsafe fn try_lock(&self) -> bool {
213+
let this_thread = current_thread_unique_ptr();
214+
if self.owner.load(Relaxed) == this_thread {
215+
self.increment_lock_count();
216+
true
217+
} else if self.mutex.try_lock() {
218+
self.owner.store(this_thread, Relaxed);
219+
*self.lock_count.get() = 1;
220+
true
221+
} else {
222+
false
223+
}
224+
}
225+
226+
pub unsafe fn lock(&self) {
227+
let this_thread = current_thread_unique_ptr();
228+
if self.owner.load(Relaxed) == this_thread {
229+
self.increment_lock_count();
230+
} else {
231+
self.mutex.lock();
232+
self.owner.store(this_thread, Relaxed);
233+
*self.lock_count.get() = 1;
234+
}
235+
}
236+
237+
unsafe fn increment_lock_count(&self) {
238+
*self.lock_count.get() = (*self.lock_count.get())
239+
.checked_add(1)
240+
.expect("lock count overflow in reentrant mutex");
241+
}
242+
243+
pub unsafe fn unlock(&self) {
244+
*self.lock_count.get() -= 1;
245+
if *self.lock_count.get() == 0 {
246+
self.owner.store(0, Relaxed);
247+
self.mutex.unlock();
248+
}
249+
}
250+
}

library/std/src/sys/unix/locks/mod.rs

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,8 @@ cfg_if::cfg_if! {
44
target_os = "android",
55
))] {
66
mod futex;
7-
#[allow(dead_code)]
8-
mod pthread_mutex; // Only used for PthreadMutexAttr, needed by pthread_remutex.
9-
mod pthread_remutex; // FIXME: Implement this using a futex
107
mod pthread_rwlock; // FIXME: Implement this using a futex
11-
pub use futex::{Mutex, MovableMutex, Condvar, MovableCondvar};
12-
pub use pthread_remutex::ReentrantMutex;
8+
pub use futex::{Mutex, MovableMutex, Condvar, MovableCondvar, ReentrantMutex};
139
pub use pthread_rwlock::{RwLock, MovableRwLock};
1410
} else {
1511
mod pthread_mutex;

0 commit comments

Comments
 (0)