From 928a446f12322d55b8889dc6a0d2afacc7ea566d Mon Sep 17 00:00:00 2001 From: Wildan M Date: Sun, 29 Mar 2026 10:51:04 +0700 Subject: [PATCH 1/2] Implement clockid_t handling to pthread_rwlock timeout --- src/header/pthread/rwlock.rs | 56 ++++++++++++++++++++++++++---------- src/sync/rwlock.rs | 48 +++++++++++++++++++++++++------ 2 files changed, 81 insertions(+), 23 deletions(-) diff --git a/src/header/pthread/rwlock.rs b/src/header/pthread/rwlock.rs index af943966ec..a923c385fe 100644 --- a/src/header/pthread/rwlock.rs +++ b/src/header/pthread/rwlock.rs @@ -2,7 +2,7 @@ use super::*; use crate::header::errno::{EBUSY, EINVAL}; -use crate::pthread::Pshared; +use crate::{error::ResultExt, header::time::CLOCK_REALTIME, pthread::Pshared}; #[unsafe(no_mangle)] pub unsafe extern "C" fn pthread_rwlock_init( @@ -23,27 +23,52 @@ pub unsafe extern "C" fn pthread_rwlock_init( } #[unsafe(no_mangle)] pub unsafe extern "C" fn pthread_rwlock_rdlock(rwlock: *mut pthread_rwlock_t) -> c_int { - unsafe { get(rwlock) }.acquire_read_lock(None); - - 0 + unsafe { get(rwlock) } + .acquire_read_lock(None) + .map(|_| 0) + .or_minus_one_errno() +} +#[unsafe(no_mangle)] +pub unsafe extern "C" fn pthread_rwlock_clockrdlock( + rwlock: *mut pthread_rwlock_t, + clock_id: clockid_t, + abstime: *const timespec, +) -> c_int { + unsafe { get(rwlock) } + .acquire_read_lock(Some((unsafe { &*abstime }, clock_id))) + .map(|_| 0) + .or_minus_one_errno() } #[unsafe(no_mangle)] pub unsafe extern "C" fn pthread_rwlock_timedrdlock( rwlock: *mut pthread_rwlock_t, - timeout: *const timespec, + abstime: *const timespec, ) -> c_int { - unsafe { get(rwlock) }.acquire_read_lock(Some(unsafe { &*timeout })); - - 0 + unsafe { get(rwlock) } + .acquire_read_lock(Some((unsafe { &*abstime }, CLOCK_REALTIME))) + .map(|_| 0) + .or_minus_one_errno() +} +#[unsafe(no_mangle)] +pub unsafe extern "C" fn pthread_rwlock_clockwrlock( + rwlock: *mut pthread_rwlock_t, + clock_id: clockid_t, + abstime: *const timespec, +) -> c_int { + unsafe { get(rwlock) } + .acquire_write_lock(Some((unsafe { &*abstime }, clock_id))) + .map(|_| 0) + .or_minus_one_errno() } #[unsafe(no_mangle)] pub unsafe extern "C" fn pthread_rwlock_timedwrlock( rwlock: *mut pthread_rwlock_t, - timeout: *const timespec, + abstime: *const timespec, ) -> c_int { - unsafe { get(rwlock) }.acquire_write_lock(Some(unsafe { &*timeout })); - - 0 + unsafe { get(rwlock) } + .acquire_write_lock(Some((unsafe { &*abstime }, CLOCK_REALTIME))) + .map(|_| 0) + .or_minus_one_errno() } #[unsafe(no_mangle)] pub unsafe extern "C" fn pthread_rwlock_tryrdlock(rwlock: *mut pthread_rwlock_t) -> c_int { @@ -67,9 +92,10 @@ pub unsafe extern "C" fn pthread_rwlock_unlock(rwlock: *mut pthread_rwlock_t) -> } #[unsafe(no_mangle)] pub unsafe extern "C" fn pthread_rwlock_wrlock(rwlock: *mut pthread_rwlock_t) -> c_int { - unsafe { get(rwlock) }.acquire_write_lock(None); - - 0 + unsafe { get(rwlock) } + .acquire_write_lock(None) + .map(|_| 0) + .or_minus_one_errno() } #[unsafe(no_mangle)] diff --git a/src/sync/rwlock.rs b/src/sync/rwlock.rs index d5ca2140a1..1c885960f4 100644 --- a/src/sync/rwlock.rs +++ b/src/sync/rwlock.rs @@ -4,7 +4,16 @@ use core::{ sync::atomic::{AtomicU32, Ordering}, }; -use crate::{header::bits_time::timespec, pthread::Pshared}; +use crate::{ + error::{Errno, Result}, + header::{ + bits_time::timespec, + errno::EINVAL, + time::{CLOCK_MONOTONIC, CLOCK_REALTIME, timespec_realtime_to_monotonic}, + }, + platform::types::clockid_t, + pthread::Pshared, +}; pub struct InnerRwLock { state: AtomicU32, @@ -25,7 +34,25 @@ impl InnerRwLock { state: AtomicU32::new(0), } } - pub fn acquire_write_lock(&self, deadline: Option<×pec>) { + fn translate_timeout(deadline: Option<(×pec, i32)>) -> Result, Errno> { + let relative = match deadline { + // FUTEX expect monotonic clock + Some((abstime, CLOCK_MONOTONIC)) => Some(abstime.clone()), + Some((abstime, CLOCK_REALTIME)) => { + Some(timespec_realtime_to_monotonic(abstime.clone())?) + } + None => None, + _ => { + return Err(Errno(EINVAL)); + } + }; + Ok(relative) + } + pub fn acquire_write_lock( + &self, + deadline: Option<(×pec, clockid_t)>, + ) -> Result<(), Errno> { + let relative = Self::translate_timeout(deadline)?; let mut waiting_wr = self.state.load(Ordering::Relaxed) & WAITING_WR; loop { @@ -35,7 +62,7 @@ impl InnerRwLock { Ordering::Acquire, Ordering::Relaxed, ) { - Ok(_) => return, + Ok(_) => break, Err(actual) => { let expected = actual; let expected = if actual & COUNT_MASK != EXCLUSIVE { @@ -50,7 +77,7 @@ impl InnerRwLock { waiting_wr = expected & WAITING_WR; if actual & COUNT_MASK > 0 { - let _ = crate::sync::futex_wait(&self.state, expected, deadline); + let _ = crate::sync::futex_wait(&self.state, expected, relative.as_ref()); } else { // We must avoid blocking indefinitely in our `futex_wait()`, in this case // where it's possible that `self.state == expected` but our futex might @@ -61,12 +88,17 @@ impl InnerRwLock { } } } + + Ok(()) } - pub fn acquire_read_lock(&self, deadline: Option<×pec>) { + pub fn acquire_read_lock(&self, deadline: Option<(×pec, clockid_t)>) -> Result<(), Errno> { + let relative = Self::translate_timeout(deadline)?; // TODO: timeout while let Err(old) = self.try_acquire_read_lock() { - crate::sync::futex_wait(&self.state, old, deadline); + crate::sync::futex_wait(&self.state, old, relative.as_ref()); } + + Ok(()) } pub fn try_acquire_read_lock(&self) -> Result<(), u32> { let mut cached = self.state.load(Ordering::Acquire); @@ -167,12 +199,12 @@ impl RwLock { impl RwLock { pub fn read(&self) -> ReadGuard<'_, T> { - self.inner.acquire_read_lock(None); + let _ = self.inner.acquire_read_lock(None); unsafe { ReadGuard::new(self) } } pub fn write(&self) -> WriteGuard<'_, T> { - self.inner.acquire_write_lock(None); + let _ = self.inner.acquire_write_lock(None); unsafe { WriteGuard::new(self) } } From f5dd08dc4911de6dd9ef2f9e34f6e680a687387f Mon Sep 17 00:00:00 2001 From: Wildan M Date: Sun, 29 Mar 2026 11:31:08 +0700 Subject: [PATCH 2/2] Add and fix pthread rwlock timeout tests --- src/header/pthread/rwlock.rs | 50 +++++++++++++++++----------------- src/sync/rwlock.rs | 13 ++++++--- tests/pthread/rwlock_trylock.c | 19 +++++++++++-- 3 files changed, 51 insertions(+), 31 deletions(-) diff --git a/src/header/pthread/rwlock.rs b/src/header/pthread/rwlock.rs index a923c385fe..916579bd86 100644 --- a/src/header/pthread/rwlock.rs +++ b/src/header/pthread/rwlock.rs @@ -2,7 +2,7 @@ use super::*; use crate::header::errno::{EBUSY, EINVAL}; -use crate::{error::ResultExt, header::time::CLOCK_REALTIME, pthread::Pshared}; +use crate::{header::time::CLOCK_REALTIME, pthread::Pshared}; #[unsafe(no_mangle)] pub unsafe extern "C" fn pthread_rwlock_init( @@ -23,10 +23,10 @@ pub unsafe extern "C" fn pthread_rwlock_init( } #[unsafe(no_mangle)] pub unsafe extern "C" fn pthread_rwlock_rdlock(rwlock: *mut pthread_rwlock_t) -> c_int { - unsafe { get(rwlock) } - .acquire_read_lock(None) - .map(|_| 0) - .or_minus_one_errno() + match unsafe { get(rwlock) }.acquire_read_lock(None) { + Ok(()) => 0, + Err(e) => e.0, + } } #[unsafe(no_mangle)] pub unsafe extern "C" fn pthread_rwlock_clockrdlock( @@ -34,20 +34,20 @@ pub unsafe extern "C" fn pthread_rwlock_clockrdlock( clock_id: clockid_t, abstime: *const timespec, ) -> c_int { - unsafe { get(rwlock) } - .acquire_read_lock(Some((unsafe { &*abstime }, clock_id))) - .map(|_| 0) - .or_minus_one_errno() + match unsafe { get(rwlock) }.acquire_read_lock(Some((unsafe { &*abstime }, clock_id))) { + Ok(()) => 0, + Err(e) => e.0, + } } #[unsafe(no_mangle)] pub unsafe extern "C" fn pthread_rwlock_timedrdlock( rwlock: *mut pthread_rwlock_t, abstime: *const timespec, ) -> c_int { - unsafe { get(rwlock) } - .acquire_read_lock(Some((unsafe { &*abstime }, CLOCK_REALTIME))) - .map(|_| 0) - .or_minus_one_errno() + match unsafe { get(rwlock) }.acquire_read_lock(Some((unsafe { &*abstime }, CLOCK_REALTIME))) { + Ok(()) => 0, + Err(e) => e.0, + } } #[unsafe(no_mangle)] pub unsafe extern "C" fn pthread_rwlock_clockwrlock( @@ -55,20 +55,20 @@ pub unsafe extern "C" fn pthread_rwlock_clockwrlock( clock_id: clockid_t, abstime: *const timespec, ) -> c_int { - unsafe { get(rwlock) } - .acquire_write_lock(Some((unsafe { &*abstime }, clock_id))) - .map(|_| 0) - .or_minus_one_errno() + match unsafe { get(rwlock) }.acquire_write_lock(Some((unsafe { &*abstime }, clock_id))) { + Ok(()) => 0, + Err(e) => e.0, + } } #[unsafe(no_mangle)] pub unsafe extern "C" fn pthread_rwlock_timedwrlock( rwlock: *mut pthread_rwlock_t, abstime: *const timespec, ) -> c_int { - unsafe { get(rwlock) } - .acquire_write_lock(Some((unsafe { &*abstime }, CLOCK_REALTIME))) - .map(|_| 0) - .or_minus_one_errno() + match unsafe { get(rwlock) }.acquire_write_lock(Some((unsafe { &*abstime }, CLOCK_REALTIME))) { + Ok(()) => 0, + Err(e) => e.0, + } } #[unsafe(no_mangle)] pub unsafe extern "C" fn pthread_rwlock_tryrdlock(rwlock: *mut pthread_rwlock_t) -> c_int { @@ -92,10 +92,10 @@ pub unsafe extern "C" fn pthread_rwlock_unlock(rwlock: *mut pthread_rwlock_t) -> } #[unsafe(no_mangle)] pub unsafe extern "C" fn pthread_rwlock_wrlock(rwlock: *mut pthread_rwlock_t) -> c_int { - unsafe { get(rwlock) } - .acquire_write_lock(None) - .map(|_| 0) - .or_minus_one_errno() + match unsafe { get(rwlock) }.acquire_write_lock(None) { + Ok(()) => 0, + Err(e) => e.0, + } } #[unsafe(no_mangle)] diff --git a/src/sync/rwlock.rs b/src/sync/rwlock.rs index 1c885960f4..4a56eee6af 100644 --- a/src/sync/rwlock.rs +++ b/src/sync/rwlock.rs @@ -8,7 +8,7 @@ use crate::{ error::{Errno, Result}, header::{ bits_time::timespec, - errno::EINVAL, + errno::{EINVAL, ETIMEDOUT}, time::{CLOCK_MONOTONIC, CLOCK_REALTIME, timespec_realtime_to_monotonic}, }, platform::types::clockid_t, @@ -77,7 +77,10 @@ impl InnerRwLock { waiting_wr = expected & WAITING_WR; if actual & COUNT_MASK > 0 { - let _ = crate::sync::futex_wait(&self.state, expected, relative.as_ref()); + match crate::sync::futex_wait(&self.state, expected, relative.as_ref()) { + super::FutexWaitResult::TimedOut => return Err(Errno(ETIMEDOUT)), + _ => {} + } } else { // We must avoid blocking indefinitely in our `futex_wait()`, in this case // where it's possible that `self.state == expected` but our futex might @@ -93,9 +96,11 @@ impl InnerRwLock { } pub fn acquire_read_lock(&self, deadline: Option<(×pec, clockid_t)>) -> Result<(), Errno> { let relative = Self::translate_timeout(deadline)?; - // TODO: timeout while let Err(old) = self.try_acquire_read_lock() { - crate::sync::futex_wait(&self.state, old, relative.as_ref()); + match crate::sync::futex_wait(&self.state, old, relative.as_ref()) { + super::FutexWaitResult::TimedOut => return Err(Errno(ETIMEDOUT)), + _ => {} + } } Ok(()) diff --git a/tests/pthread/rwlock_trylock.c b/tests/pthread/rwlock_trylock.c index 9990a0cdb3..8a8274e53f 100644 --- a/tests/pthread/rwlock_trylock.c +++ b/tests/pthread/rwlock_trylock.c @@ -4,8 +4,9 @@ #include #include #include +#include -// Same test logic as test_rwlock_try_write in rustc/library/std/sync/rwlock/tests.rs +// Adapted from test_rwlock_try_write in rustc/library/std/sync/rwlock/tests.rs int main(void) { int status; @@ -31,11 +32,25 @@ int main(void) { ERROR_IF(pthread_rwlock_rdlock, status, != 0); status = pthread_rwlock_trywrlock(&rwlock); - assert(status == EBUSY); + UNEXP_IF(pthread_rwlock_trywrlock, status, != EBUSY); + + struct timespec ts; + clock_gettime(CLOCK_REALTIME, &ts); + ts.tv_nsec += 200 * 1000000; + status = pthread_rwlock_timedwrlock(&rwlock, &ts); + UNEXP_IF(pthread_rwlock_timedwrlock, status, != ETIMEDOUT); status = pthread_rwlock_unlock(&rwlock); ERROR_IF(pthread_rwlock_unlock, status, != 0); + status = pthread_rwlock_trywrlock(&rwlock); + ERROR_IF(pthread_rwlock_rdlock, status, != 0); + + clock_gettime(CLOCK_REALTIME, &ts); + ts.tv_nsec += 200 * 1000000; + status = pthread_rwlock_timedrdlock(&rwlock, &ts); + UNEXP_IF(pthread_rwlock_timedrdlock, status, != ETIMEDOUT); + status = pthread_rwlock_destroy(&rwlock); ERROR_IF(pthread_rwlock_destroy, status, != 0);