[libos] Implement high-performance mutex
This commit is contained in:
parent
eee7c8651d
commit
9a174aee01
@ -1,5 +1,7 @@
|
|||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
|
pub use mutex::{Mutex, MutexGuard};
|
||||||
pub use rw_lock::RwLock;
|
pub use rw_lock::RwLock;
|
||||||
|
|
||||||
|
pub mod mutex;
|
||||||
pub mod rw_lock;
|
pub mod rw_lock;
|
||||||
|
204
src/libos/src/util/sync/mutex.rs
Normal file
204
src/libos/src/util/sync/mutex.rs
Normal file
@ -0,0 +1,204 @@
|
|||||||
|
use std::{
|
||||||
|
cell::UnsafeCell,
|
||||||
|
hint,
|
||||||
|
ops::{Deref, DerefMut},
|
||||||
|
sync::atomic::{AtomicBool, AtomicU32},
|
||||||
|
};
|
||||||
|
|
||||||
|
use alloc::{boxed::Box, fmt};
|
||||||
|
use atomic::Ordering;
|
||||||
|
|
||||||
|
use crate::process::{futex_wait, futex_wake};
|
||||||
|
|
||||||
|
#[derive(Default)]
|
||||||
|
pub struct Mutex<T> {
|
||||||
|
value: UnsafeCell<T>,
|
||||||
|
inner: Box<MutexInner>,
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe impl<T: Send> Sync for Mutex<T> {}
|
||||||
|
unsafe impl<T: Send> Send for Mutex<T> {}
|
||||||
|
|
||||||
|
pub struct MutexGuard<'a, T: 'a> {
|
||||||
|
inner: &'a Mutex<T>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> !Send for MutexGuard<'_, T> {}
|
||||||
|
unsafe impl<T: Sync> Sync for MutexGuard<'_, T> {}
|
||||||
|
|
||||||
|
impl<T> Mutex<T> {
|
||||||
|
#[inline]
|
||||||
|
pub fn new(val: T) -> Mutex<T> {
|
||||||
|
Self {
|
||||||
|
value: UnsafeCell::new(val),
|
||||||
|
inner: Box::new(MutexInner::new()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn into_inner(self) -> T {
|
||||||
|
self.value.into_inner()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> Mutex<T> {
|
||||||
|
#[inline]
|
||||||
|
pub fn lock(&self) -> MutexGuard<'_, T> {
|
||||||
|
self.inner.lock();
|
||||||
|
MutexGuard { inner: self }
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn try_lock(&self) -> Option<MutexGuard<'_, T>> {
|
||||||
|
self.inner.try_lock().map(|_| MutexGuard { inner: self })
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn unlock(guard: MutexGuard<'_, T>) {
|
||||||
|
drop(guard)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
unsafe fn force_unlock(&self) {
|
||||||
|
self.inner.force_unlock()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn get_mut(&mut self) -> &mut T {
|
||||||
|
self.value.get_mut()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> Deref for MutexGuard<'_, T> {
|
||||||
|
type Target = T;
|
||||||
|
|
||||||
|
fn deref(&self) -> &Self::Target {
|
||||||
|
unsafe { &*self.inner.value.get() }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> DerefMut for MutexGuard<'_, T> {
|
||||||
|
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||||
|
unsafe { &mut *self.inner.value.get() }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> Drop for MutexGuard<'_, T> {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
unsafe {
|
||||||
|
self.inner.force_unlock();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: fmt::Debug> fmt::Debug for MutexGuard<'_, T> {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
|
fmt::Debug::fmt(&**self, f)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: fmt::Debug> fmt::Debug for Mutex<T> {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
match self.try_lock() {
|
||||||
|
Some(guard) => write!(f, "Mutex {{ value: ")
|
||||||
|
.and_then(|()| (*guard).fmt(f))
|
||||||
|
.and_then(|()| write!(f, "}}")),
|
||||||
|
None => {
|
||||||
|
write!(f, "Mutex {{ <locked> }}")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Default)]
|
||||||
|
struct MutexInner {
|
||||||
|
/// 0: unlocked
|
||||||
|
/// 1: locked, no other threads waiting
|
||||||
|
/// 2: locked, and other threads waiting (contended)
|
||||||
|
lock: AtomicU32,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl MutexInner {
|
||||||
|
#[inline]
|
||||||
|
pub fn new() -> MutexInner {
|
||||||
|
Self {
|
||||||
|
lock: AtomicU32::new(0),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn lock(&self) {
|
||||||
|
if self
|
||||||
|
.lock
|
||||||
|
.compare_exchange(0, 1, Ordering::Acquire, Ordering::Relaxed)
|
||||||
|
.is_err()
|
||||||
|
{
|
||||||
|
self.lock_contended();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn try_lock(&self) -> Option<u32> {
|
||||||
|
self.lock
|
||||||
|
.compare_exchange(0, 1, Ordering::Acquire, Ordering::Relaxed)
|
||||||
|
.ok()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
unsafe fn force_unlock(&self) {
|
||||||
|
if self.lock.swap(0, Ordering::Release) == 2 {
|
||||||
|
self.wake();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cold]
|
||||||
|
fn lock_contended(&self) {
|
||||||
|
let mut state = self.spin();
|
||||||
|
|
||||||
|
if state == 0 {
|
||||||
|
match self
|
||||||
|
.lock
|
||||||
|
.compare_exchange(0, 1, Ordering::Acquire, Ordering::Relaxed)
|
||||||
|
{
|
||||||
|
Ok(_) => return, // Locked!
|
||||||
|
Err(s) => state = s,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
loop {
|
||||||
|
if state != 2 && self.lock.swap(2, Ordering::Acquire) == 0 {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Wait for the futex to change state, assuming it is still 2.
|
||||||
|
let ret = futex_wait(&self.lock as *const _ as *const i32, 2, &None);
|
||||||
|
|
||||||
|
// Spin again after waking up.
|
||||||
|
state = self.spin();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn spin(&self) -> u32 {
|
||||||
|
let mut spin = 1000;
|
||||||
|
loop {
|
||||||
|
// We only use `load` (and not `swap` or `compare_exchange`)
|
||||||
|
// while spinning, to be easier on the caches.
|
||||||
|
let state = self.lock.load(Ordering::Relaxed);
|
||||||
|
|
||||||
|
// We stop spinning when the mutex is unlocked (0),
|
||||||
|
// but also when it's contended (2).
|
||||||
|
if state != 1 || spin == 0 {
|
||||||
|
return state;
|
||||||
|
}
|
||||||
|
|
||||||
|
core::hint::spin_loop();
|
||||||
|
spin -= 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cold]
|
||||||
|
fn wake(&self) {
|
||||||
|
futex_wake(&self.lock as *const _ as *const i32, 1);
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user