From f9c14d68e5d20930a7a2aa9d3832f38551102a26 Mon Sep 17 00:00:00 2001 From: Putta Khunchalee Date: Tue, 12 Nov 2024 19:39:01 +0700 Subject: [PATCH] Removes user-mode gmtx (#1095) --- Cargo.toml | 1 - src/gmtx/Cargo.toml | 13 -- src/gmtx/src/guard.rs | 125 ---------------- src/gmtx/src/lib.rs | 329 ------------------------------------------ 4 files changed, 468 deletions(-) delete mode 100644 src/gmtx/Cargo.toml delete mode 100644 src/gmtx/src/guard.rs delete mode 100644 src/gmtx/src/lib.rs diff --git a/Cargo.toml b/Cargo.toml index 91c31c01c..5ad5c1790 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -7,7 +7,6 @@ members = [ "kernel", "macros", "src/fs", - "src/gmtx", "src/llt", "src/obconf", "src/param", diff --git a/src/gmtx/Cargo.toml b/src/gmtx/Cargo.toml deleted file mode 100644 index cf8694e1e..000000000 --- a/src/gmtx/Cargo.toml +++ /dev/null @@ -1,13 +0,0 @@ -[package] -name = "gmtx" -version = "0.1.0" -edition = "2021" - -[target.'cfg(unix)'.dependencies] -libc = "0.2" - -[target.'cfg(windows)'.dependencies] -windows-sys = { version = "0.52", features = ["Win32_System_Threading"] } - -[target.'cfg(target_os = "macos")'.dependencies] -ulock-sys = "0.1.0" diff --git a/src/gmtx/src/guard.rs b/src/gmtx/src/guard.rs deleted file mode 100644 index 03258954f..000000000 --- a/src/gmtx/src/guard.rs +++ /dev/null @@ -1,125 +0,0 @@ -use crate::{GroupGuard, Gutex}; -use std::fmt::{Display, Formatter}; -use std::marker::PhantomData; -use std::ops::{Deref, DerefMut}; - -/// RAII structure used to release the shared read access of a lock when dropped. -#[allow(dead_code)] -pub struct GutexReadGuard<'a, T> { - mtx: &'a Gutex, - lock: GroupGuard<'a>, -} - -impl<'a, T> GutexReadGuard<'a, T> { - pub(crate) fn new(lock: GroupGuard<'a>, mtx: &'a Gutex) -> Self { - Self { lock, mtx } - } -} - -impl<'a, T> Drop for GutexReadGuard<'a, T> { - fn drop(&mut self) { - unsafe { *self.mtx.active.get() -= 1 }; - } -} - -impl<'a, T> Deref for GutexReadGuard<'a, T> { - type Target = T; - - fn deref(&self) -> &Self::Target { - unsafe { &*self.mtx.value.get() } - } -} - -impl<'a, T: Display> Display for GutexReadGuard<'a, T> { - fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { - self.deref().fmt(f) - } -} - -unsafe impl<'a, T: Sync> Sync for GutexReadGuard<'a, T> {} - -/// RAII structure used to release the exclusive write access of a lock when dropped. -#[allow(dead_code)] -pub struct GutexWriteGuard<'a, T> { - active: *mut usize, - value: *mut T, - lock: GroupGuard<'a>, -} - -impl<'a, T> GutexWriteGuard<'a, T> { - /// # Safety - /// `active` and `value` must be protected by `lock`. - pub(crate) unsafe fn new(active: *mut usize, value: *mut T, lock: GroupGuard<'a>) -> Self { - Self { - active, - value, - lock, - } - } - - pub fn map &mut O>(self, f: F) -> GutexWriteGuard<'a, O> { - let active = self.active; - let value = f(unsafe { &mut *self.value }); - let lock = GroupGuard { - group: self.lock.group, - phantom: PhantomData, - }; - - std::mem::forget(self); - - GutexWriteGuard { - active, - value, - lock, - } - } -} - -impl<'a, T> GutexWriteGuard<'a, Option> { - pub fn ok_or(self, err: E) -> Result, E> { - match unsafe { (*self.value).as_mut() } { - Some(v) => { - let v = GutexWriteGuard { - active: self.active, - value: v as *mut T, - lock: GroupGuard { - group: self.lock.group, - phantom: PhantomData, - }, - }; - - std::mem::forget(self); - Ok(v) - } - None => Err(err), - } - } -} - -impl<'a, T> Drop for GutexWriteGuard<'a, T> { - fn drop(&mut self) { - unsafe { *self.active = 0 }; - } -} - -impl<'a, T> Deref for GutexWriteGuard<'a, T> { - type Target = T; - - fn deref(&self) -> &Self::Target { - unsafe { &*self.value } - } -} - -impl<'a, T> DerefMut for GutexWriteGuard<'a, T> { - fn deref_mut(&mut self) -> &mut Self::Target { - unsafe { &mut *self.value } - } -} - -impl<'a, T: Display> Display for GutexWriteGuard<'a, T> { - fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { - self.deref().fmt(f) - } -} - -unsafe impl<'a, T: Sync> Sync for GutexWriteGuard<'a, T> {} diff --git a/src/gmtx/src/lib.rs b/src/gmtx/src/lib.rs deleted file mode 100644 index 1041a2d09..000000000 --- a/src/gmtx/src/lib.rs +++ /dev/null @@ -1,329 +0,0 @@ -pub use self::guard::*; -use std::cell::UnsafeCell; -use std::io::Error; -use std::marker::PhantomData; -use std::rc::Rc; -use std::sync::atomic::Ordering; -use std::sync::Arc; - -mod guard; - -/// A mutex that grant exclusive access to a group of members. -/// -/// The [`std::sync::Mutex`] and the related types are prone to deadlock when using on a multiple -/// struct fields like this: -/// -/// ``` -/// use std::sync::Mutex; -/// -/// pub struct Foo { -/// field1: Mutex<()>, -/// field2: Mutex<()>, -/// } -/// ``` -/// -/// The order to acquire the lock must be the same everywhere otherwise the deadlock is possible. -/// Maintaining the lock order manually are cumbersome task so we introduce this type to handle this -/// instead. -/// -/// How this type are working is simple. Any locks on any member will lock the same mutex in the -/// group, which mean there are only one mutex in the group. It have the same effect as the -/// following code: -/// -/// ``` -/// use std::sync::Mutex; -/// -/// pub struct Foo { -/// data: Mutex, -/// } -/// -/// struct Data { -/// field1: (), -/// field2: (), -/// } -/// ``` -/// -/// The bonus point of this type is it will allow recursive lock for read-only access so you will -/// never end up deadlock yourself. It will panic if you try to acquire write access while the -/// readers are still active the same as [`std::cell::RefCell`]. -#[derive(Debug)] -pub struct Gutex { - group: Arc, - active: UnsafeCell, - value: UnsafeCell, -} - -impl Gutex { - pub fn get_mut(&mut self) -> &mut T { - self.value.get_mut() - } - - /// # Panics - /// If there are an active writer. - pub fn read(&self) -> GutexReadGuard<'_, T> { - // Check if there are an active writer. - let lock = self.group.lock(); - let active = self.active.get(); - - unsafe { - if *active == usize::MAX { - panic!("attempt to acquire the read lock while there are an active write lock"); - } else if *active == (usize::MAX - 1) { - // This should never happen because stack overflow should be triggering first. - panic!("maximum number of active readers has been reached"); - } - - *active += 1; - } - - GutexReadGuard::new(lock, self) - } - - /// # Panics - /// If there are any active reader or writer. - pub fn write(&self) -> GutexWriteGuard<'_, T> { - // Check if there are active reader or writer. - let lock = self.group.lock(); - let active = self.active.get(); - - // SAFETY: This is safe because we own the lock that protect both active and value. - unsafe { - if *active != 0 { - panic!( - "attempt to acquire the write lock while there are an active reader or writer" - ); - } - - *active = usize::MAX; - - GutexWriteGuard::new(&mut *active, &mut *self.value.get(), lock) - } - } -} - -unsafe impl Send for Gutex {} -unsafe impl Sync for Gutex {} - -/// Represents a group of [`Gutex`]. -#[derive(Debug)] -pub struct GutexGroup { - owning: ThreadId, - active: UnsafeCell, -} - -impl GutexGroup { - pub fn new() -> Arc { - Arc::new(Self { - owning: ThreadId::new(0), - active: UnsafeCell::new(0), - }) - } - - pub fn spawn(self: &Arc, value: T) -> Gutex { - Gutex { - group: self.clone(), - active: UnsafeCell::new(0), - value: UnsafeCell::new(value), - } - } - - fn lock(&self) -> GroupGuard<'_> { - // Check if the calling thread already own the lock. - let current = Self::current_thread(); - - if current == self.owning.load(Ordering::Relaxed) { - // SAFETY: This is safe because the current thread own the lock. - return unsafe { GroupGuard::new(self) }; - } - - // Acquire the lock. - while let Err(owning) = - self.owning - .compare_exchange(0, current, Ordering::Acquire, Ordering::Relaxed) - { - // Wait for the lock to unlock. - unsafe { Self::wait_unlock(self.owning.as_ptr(), owning) }; - } - - // SAFETY: This is safe because the current thread acquire the lock successfully by the - // above compare_exchange(). - unsafe { GroupGuard::new(self) } - } - - #[cfg(target_os = "linux")] - fn current_thread() -> i32 { - unsafe { libc::gettid() } - } - - #[cfg(target_os = "macos")] - fn current_thread() -> u64 { - let mut id = 0; - assert_eq!(unsafe { libc::pthread_threadid_np(0, &mut id) }, 0); - id - } - - #[cfg(target_os = "windows")] - fn current_thread() -> u32 { - unsafe { windows_sys::Win32::System::Threading::GetCurrentThreadId() } - } - - #[cfg(target_os = "linux")] - unsafe fn wait_unlock(addr: *mut i32, owning: i32) { - use libc::{syscall, SYS_futex, EAGAIN, FUTEX_PRIVATE_FLAG, FUTEX_WAIT}; - - if unsafe { syscall(SYS_futex, addr, FUTEX_WAIT | FUTEX_PRIVATE_FLAG, owning, 0) } < 0 { - let e = Error::last_os_error(); - - if e.raw_os_error().unwrap() != EAGAIN { - panic!("FUTEX_WAIT failed: {e}"); - } - } - } - - #[cfg(target_os = "macos")] - unsafe fn wait_unlock(addr: *mut u64, owning: u64) { - use ulock_sys::__ulock_wait; - use ulock_sys::darwin19::UL_COMPARE_AND_WAIT64; - - if __ulock_wait(UL_COMPARE_AND_WAIT64, addr.cast(), owning, 0) != 0 { - panic!("__ulock_wait() failed: {}", Error::last_os_error()); - } - } - - #[cfg(target_os = "windows")] - unsafe fn wait_unlock(addr: *mut u32, owning: u32) { - use windows_sys::Win32::System::Threading::{WaitOnAddress, INFINITE}; - - if unsafe { WaitOnAddress(addr.cast(), &owning as *const u32 as _, 4, INFINITE) } == 0 { - panic!("WaitOnAddress() failed: {}", Error::last_os_error()); - } - } - - #[cfg(target_os = "linux")] - unsafe fn wake_one(addr: *mut i32) { - use libc::{syscall, SYS_futex, FUTEX_PRIVATE_FLAG, FUTEX_WAKE}; - - if unsafe { syscall(SYS_futex, addr, FUTEX_WAKE | FUTEX_PRIVATE_FLAG, 1) } < 0 { - panic!("FUTEX_WAKE failed: {}", Error::last_os_error()); - } - } - - #[cfg(target_os = "macos")] - unsafe fn wake_one(addr: *mut u64) { - use libc::ENOENT; - use ulock_sys::__ulock_wake; - use ulock_sys::darwin19::UL_COMPARE_AND_WAIT64; - - if __ulock_wake(UL_COMPARE_AND_WAIT64, addr.cast(), 0) != 0 { - // __ulock_wake will return ENOENT if no other threads being waiting on the address. - let e = Error::last_os_error(); - - if e.raw_os_error().unwrap() != ENOENT { - panic!("__ulock_wake() failed: {e}"); - } - } - } - - #[cfg(target_os = "windows")] - unsafe fn wake_one(addr: *mut u32) { - use windows_sys::Win32::System::Threading::WakeByAddressSingle; - - unsafe { WakeByAddressSingle(addr.cast()) }; - } -} - -unsafe impl Send for GutexGroup {} -unsafe impl Sync for GutexGroup {} - -/// An RAII object used to release the lock on [`GutexGroup`]. This type cannot be send because it -/// will cause data race on the group when dropping if more than one [`GroupGuard`] are active. -struct GroupGuard<'a> { - pub group: &'a GutexGroup, - pub phantom: PhantomData>, // For !Send and !Sync. -} - -impl<'a> GroupGuard<'a> { - /// # Safety - /// The group must be locked by the calling thread with no active references to any of its - /// field. - unsafe fn new(group: &'a GutexGroup) -> Self { - *group.active.get() += 1; - - Self { - group, - phantom: PhantomData, - } - } -} - -impl<'a> Drop for GroupGuard<'a> { - fn drop(&mut self) { - // Decrease the active lock. - unsafe { - let active = self.group.active.get(); - - *active -= 1; - - if *active != 0 { - return; - } - } - - // Release the lock. - self.group.owning.store(0, Ordering::Release); - - unsafe { GutexGroup::wake_one(self.group.owning.as_ptr()) }; - } -} - -#[cfg(target_os = "linux")] -type ThreadId = std::sync::atomic::AtomicI32; - -#[cfg(target_os = "macos")] -type ThreadId = std::sync::atomic::AtomicU64; - -#[cfg(target_os = "windows")] -type ThreadId = std::sync::atomic::AtomicU32; - -#[cfg(test)] -mod tests { - use super::*; - use std::sync::Barrier; - use std::time::Duration; - - #[test] - fn group_lock() { - let b = Arc::new(Barrier::new(2)); - let v = Arc::new(GutexGroup::new().spawn(0)); - let mut l = v.write(); - let t = std::thread::spawn({ - let b = b.clone(); - let v = v.clone(); - - move || { - // Wait for parent thread. - let mut l = v.write(); - - b.wait(); - - assert_eq!(*l, 1); - - // Notify the parent thread. - std::thread::sleep(Duration::from_secs(1)); - - *l = 2; - } - }); - - // Notify the inner thread. - *l = 1; - drop(l); - - // Wait for the inner thread value. - b.wait(); - - assert_eq!(*v.read(), 2); - - t.join().unwrap(); - } -}