From 439b054486c0e62f6aaad147454bc2ae07bd194e Mon Sep 17 00:00:00 2001 From: 4lDO2 <4lDO2@protonmail.com> Date: Wed, 3 May 2023 15:45:26 +0200 Subject: [PATCH] Rustify pthread_rwlock_t. --- src/header/bits_pthread/mod.rs | 6 +- src/header/pthread/rwlock.rs | 153 +++++++++++---------------------- src/pthread/mod.rs | 24 ++++++ src/sync/mod.rs | 1 + src/sync/rwlock.rs | 70 +++++++++++++++ 5 files changed, 149 insertions(+), 105 deletions(-) create mode 100644 src/sync/rwlock.rs diff --git a/src/header/bits_pthread/mod.rs b/src/header/bits_pthread/mod.rs index f9c282b213..7c852a4a0d 100644 --- a/src/header/bits_pthread/mod.rs +++ b/src/header/bits_pthread/mod.rs @@ -20,8 +20,8 @@ pub union pthread_attr_t { } #[repr(C)] pub union pthread_rwlockattr_t { - __relibc_internal_size: [c_uchar; 4], - __relibc_internal_align: c_int, + __relibc_internal_size: [c_uchar; 1], + __relibc_internal_align: c_uchar, } #[repr(C)] pub union pthread_rwlock_t { @@ -98,7 +98,7 @@ macro_rules! assert_equal_size( ); assert_equal_size!(pthread_attr_t, RlctAttr); assert_equal_size!(pthread_rwlock_t, RlctRwlock); -assert_equal_size!(pthread_rwlock_t, RlctRwlockAttr); +assert_equal_size!(pthread_rwlockattr_t, RlctRwlockAttr); assert_equal_size!(pthread_barrier_t, RlctBarrier); assert_equal_size!(pthread_barrierattr_t, RlctBarrierAttr); assert_equal_size!(pthread_mutex_t, RlctMutex); diff --git a/src/header/pthread/rwlock.rs b/src/header/pthread/rwlock.rs index 6303e3eabd..54ebabbe43 100644 --- a/src/header/pthread/rwlock.rs +++ b/src/header/pthread/rwlock.rs @@ -1,154 +1,103 @@ use super::*; -use core::sync::atomic::{AtomicI32 as AtomicInt, Ordering}; - use crate::header::errno::EBUSY; -// PTHREAD_RWLOCK_INITIALIZER +use crate::pthread::Pshared; -// TODO: Optimize for short waits and long waits, using AtomicLock::wait_until, but still -// supporting timeouts. -// TODO: Add futex ops that use bitmasks. - -const EXCLUSIVE: u32 = (1 << (u32::BITS - 1)) - 1; -// Separate "waiting for wrlocks" and "waiting for rdlocks"? -//const WAITING: u32 = 1 << (u32::BITS - 1); - -#[no_mangle] -pub unsafe extern "C" fn pthread_rwlock_destroy(rwlock: *mut pthread_rwlock_t) -> c_int { - // (Informing the compiler that this pointer is valid, might improve optimizations.) - let _rwlock: &pthread_rwlock_t = &*rwlock; - 0 -} #[no_mangle] pub unsafe extern "C" fn pthread_rwlock_init(rwlock: *mut pthread_rwlock_t, attr: *const pthread_rwlockattr_t) -> c_int { - let attr = attr.cast::().as_ref(); + let attr = attr.cast::().as_ref().copied().unwrap_or_default(); - rwlock.cast::().write(RlctRwlock { - state: AtomicInt::new(0), - }); + rwlock.cast::().write(RlctRwlock::new(attr.pshared)); 0 } #[no_mangle] pub unsafe extern "C" fn pthread_rwlock_rdlock(rwlock: *mut pthread_rwlock_t) -> c_int { - pthread_rwlock_timedrdlock(rwlock, core::ptr::null()) + get(rwlock).acquire_read_lock(None); + + 0 } #[no_mangle] pub unsafe extern "C" fn pthread_rwlock_timedrdlock(rwlock: *mut pthread_rwlock_t, timeout: *const timespec) -> c_int { - let rwlock = &*rwlock.cast::(); - let timeout = timeout.as_ref(); + get(rwlock).acquire_read_lock(Some(&*timeout)); - loop { - if pthread_rwlock_tryrdlock(rwlock as *const _ as *mut _) == EBUSY { - crate::sync::futex_wait(&rwlock.state, EXCLUSIVE as i32, timeout); - } - return 0; - } + 0 } #[no_mangle] pub unsafe extern "C" fn pthread_rwlock_timedwrlock(rwlock: *mut pthread_rwlock_t, timeout: *const timespec) -> c_int { - let rwlock = &*rwlock.cast::(); - let timeout = timeout.as_ref(); + get(rwlock).acquire_write_lock(Some(&*timeout)); - /*loop { - if pthread_rwlock_trywrlock(rwlock as *const _ as *mut _) == EBUSY { - crate::sync::futex_wait(&rwlock.state, EXCLUSIVE as i32, timeout); - } - return 0; - }*/ - loop { - match rwlock.state.compare_exchange(0, EXCLUSIVE as i32, Ordering::Acquire, Ordering::Relaxed) { - Ok(_) => return 0, - Err(value) => { - // TODO: More than just forwarding the timeout. - crate::sync::futex_wait(&rwlock.state, value, timeout); - } - } - } + 0 } #[no_mangle] pub unsafe extern "C" fn pthread_rwlock_tryrdlock(rwlock: *mut pthread_rwlock_t) -> c_int { - let rwlock = &*rwlock.cast::(); - - let mut cached = rwlock.state.load(Ordering::Acquire) as u32; - - loop { - let old = if cached == EXCLUSIVE { 0 } else { cached }; - let new = old + 1; - - assert_ne!(new, EXCLUSIVE, "overflow"); - - match rwlock.state.compare_exchange_weak(cached as i32, new as i32, Ordering::Acquire, Ordering::Relaxed) { - Ok(_) => return 0, - Err(value) if value as u32 == EXCLUSIVE => return EBUSY, - Err(value) => { - cached = value as u32; - core::hint::spin_loop(); - } - } - } -} -#[no_mangle] -pub unsafe extern "C" fn pthread_rwlock_trywrlock(rwlock: *mut pthread_rwlock_t) -> c_int { - let rwlock = &*rwlock.cast::(); - - match rwlock.state.compare_exchange(0, EXCLUSIVE as i32, Ordering::Acquire, Ordering::Relaxed) { - Ok(_) => 0, + match get(rwlock).try_acquire_read_lock() { + Ok(()) => 0, Err(_) => EBUSY, } } #[no_mangle] -pub unsafe extern "C" fn pthread_rwlock_unlock(rwlock: *const pthread_rwlock_t) -> c_int { - let rwlock = &*rwlock.cast::(); - - let old = rwlock.state.swap(0, Ordering::Release) as u32; - - if old == EXCLUSIVE { - crate::sync::futex_wake(&rwlock.state, i32::MAX); +pub unsafe extern "C" fn pthread_rwlock_trywrlock(rwlock: *mut pthread_rwlock_t) -> c_int { + match get(rwlock).try_acquire_write_lock() { + Ok(()) => 0, + Err(_) => EBUSY, } +} +#[no_mangle] +pub unsafe extern "C" fn pthread_rwlock_unlock(rwlock: *mut pthread_rwlock_t) -> c_int { + get(rwlock).unlock(); 0 } #[no_mangle] pub unsafe extern "C" fn pthread_rwlock_wrlock(rwlock: *mut pthread_rwlock_t) -> c_int { - pthread_rwlock_timedwrlock(rwlock, core::ptr::null()) -} - -#[no_mangle] -pub unsafe extern "C" fn pthread_rwlockattr_destroy(attr: *mut pthread_rwlockattr_t) -> c_int { - let _attr = &mut *attr.cast::(); - - // No-op - 0 -} - -#[no_mangle] -pub unsafe extern "C" fn pthread_rwlockattr_getpshared(attr: *const pthread_rwlockattr_t, pshared: *mut c_int) -> c_int { - core::ptr::write(pshared, (*attr.cast::()).pshared); + get(rwlock).acquire_write_lock(None); 0 } #[no_mangle] pub unsafe extern "C" fn pthread_rwlockattr_init(attr: *mut pthread_rwlockattr_t) -> c_int { - attr.cast::().write(RlctRwlockAttr { - // Default according to POSIX. - pshared: PTHREAD_PROCESS_PRIVATE, - }); + attr.cast::().write(RlctRwlockAttr::default()); + + 0 +} + +#[no_mangle] +pub unsafe extern "C" fn pthread_rwlockattr_getpshared(attr: *const pthread_rwlockattr_t, pshared_out: *mut c_int) -> c_int { + core::ptr::write(pshared_out, (*attr.cast::()).pshared.raw()); 0 } #[no_mangle] pub unsafe extern "C" fn pthread_rwlockattr_setpshared(attr: *mut pthread_rwlockattr_t, pshared: c_int) -> c_int { - (*attr.cast::()).pshared = pshared; + (*attr.cast::()).pshared = Pshared::from_raw(pshared).expect("invalid pshared in pthread_rwlockattr_setpshared"); + 0 } +#[no_mangle] +pub unsafe extern "C" fn pthread_rwlockattr_destroy(attr: *mut pthread_rwlockattr_t) -> c_int { + core::ptr::drop_in_place(attr); + + 0 +} +#[no_mangle] +pub unsafe extern "C" fn pthread_rwlock_destroy(rwlock: *mut pthread_rwlock_t) -> c_int { + core::ptr::drop_in_place(rwlock); + + 0 +} + +pub(crate) type RlctRwlock = crate::sync::rwlock::Rwlock; + +#[derive(Clone, Copy, Default)] pub(crate) struct RlctRwlockAttr { - pub pshared: c_int, + pshared: Pshared, } -pub(crate) struct RlctRwlock { - pub state: AtomicInt, +#[inline] +unsafe fn get<'a>(ptr: *mut pthread_rwlock_t) -> &'a RlctRwlock { + &*ptr.cast() } diff --git a/src/pthread/mod.rs b/src/pthread/mod.rs index 82b4ba70de..14a9aee7cf 100644 --- a/src/pthread/mod.rs +++ b/src/pthread/mod.rs @@ -384,3 +384,27 @@ static PTHREAD_SELF: Cell<*mut Pthread> = Cell::new(core::ptr::null_mut()); /*pub(crate) fn current_thread_index() -> u32 { current_thread().expect("current thread not present").index }*/ + +#[derive(Clone, Copy, Default, Debug)] +pub enum Pshared { + #[default] + Private, + + Shared, +} +impl Pshared { + pub const fn from_raw(raw: c_int) -> Option { + Some(match raw { + header::PTHREAD_PROCESS_PRIVATE => Self::Private, + header::PTHREAD_PROCESS_SHARED => Self::Shared, + + _ => return None, + }) + } + pub const fn raw(self) -> c_int { + match self { + Self::Private => header::PTHREAD_PROCESS_PRIVATE, + Self::Shared => header::PTHREAD_PROCESS_SHARED, + } + } +} diff --git a/src/sync/mod.rs b/src/sync/mod.rs index a77e4aa3bf..d07fc4c0e2 100644 --- a/src/sync/mod.rs +++ b/src/sync/mod.rs @@ -5,6 +5,7 @@ pub mod mutex; pub mod once; pub mod pthread_mutex; +pub mod rwlock; pub mod semaphore; pub mod waitval; diff --git a/src/sync/rwlock.rs b/src/sync/rwlock.rs new file mode 100644 index 0000000000..e424936599 --- /dev/null +++ b/src/sync/rwlock.rs @@ -0,0 +1,70 @@ +use core::sync::atomic::{AtomicU32, Ordering}; + +use crate::header::time::timespec; +use crate::pthread::Pshared; + +pub struct Rwlock { + state: AtomicU32, +} +// PTHREAD_RWLOCK_INITIALIZER is defined as "all zeroes". + +const EXCLUSIVE: u32 = (1 << (u32::BITS - 1)) - 1; +// Separate "waiting for wrlocks" and "waiting for rdlocks"? +//const WAITING: u32 = 1 << (u32::BITS - 1); + +// TODO: Optimize for short waits and long waits, using AtomicLock::wait_until, but still +// supporting timeouts. +// TODO: Add futex ops that use bitmasks. + +impl Rwlock { + pub fn new(_pshared: Pshared) -> Self { + Self { + state: AtomicU32::new(0), + } + } + pub fn acquire_write_lock(&self, _timeout: Option<×pec>) { + // TODO: timeout + while let Err(old) = self.try_acquire_read_lock() { + crate::sync::futex_wait(&self.state, old, None); + } + } + pub fn acquire_read_lock(&self, _timeout: Option<×pec>) { + // TODO: timeout + while let Err(old) = self.try_acquire_write_lock() { + crate::sync::futex_wait(&self.state, old, None); + } + } + pub fn try_acquire_read_lock(&self) -> Result<(), u32> { + let mut cached = self.state.load(Ordering::Acquire); + + loop { + let old = if cached == EXCLUSIVE { 0 } else { cached }; + let new = old + 1; + + // TODO: Return with error code instead? + assert_ne!(new, EXCLUSIVE, "maximum number of rwlock readers reached"); + + match self.state.compare_exchange_weak(cached, new, Ordering::Acquire, Ordering::Relaxed) { + Ok(_) => return Ok(()), + + Err(value) if value == EXCLUSIVE => return Err(EXCLUSIVE), + Err(value) => { + cached = value; + // TODO: SCHED_YIELD? + core::hint::spin_loop(); + } + } + } + } + pub fn try_acquire_write_lock(&self) -> Result<(), u32> { + self.state.compare_exchange(0, EXCLUSIVE, Ordering::Acquire, Ordering::Relaxed).map(|_| ()) + } + + pub fn unlock(&self) { + let old = self.state.swap(0, Ordering::Release); + + if old == EXCLUSIVE { + crate::sync::futex_wake(&self.state, i32::MAX); + } + } +}