Rustify pthread_rwlock_t.

This commit is contained in:
4lDO2
2023-05-03 15:45:26 +02:00
parent e75381fd1a
commit 439b054486
5 changed files with 149 additions and 105 deletions
+3 -3
View File
@@ -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);
+51 -102
View File
@@ -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::<RlctRwlockAttr>().as_ref();
let attr = attr.cast::<RlctRwlockAttr>().as_ref().copied().unwrap_or_default();
rwlock.cast::<RlctRwlock>().write(RlctRwlock {
state: AtomicInt::new(0),
});
rwlock.cast::<RlctRwlock>().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::<RlctRwlock>();
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::<RlctRwlock>();
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::<RlctRwlock>();
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::<RlctRwlock>();
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::<RlctRwlock>();
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::<RlctRwlockAttr>();
// 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::<RlctRwlockAttr>()).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::<RlctRwlockAttr>().write(RlctRwlockAttr {
// Default according to POSIX.
pshared: PTHREAD_PROCESS_PRIVATE,
});
attr.cast::<RlctRwlockAttr>().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::<RlctRwlockAttr>()).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::<RlctRwlockAttr>()).pshared = pshared;
(*attr.cast::<RlctRwlockAttr>()).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()
}
+24
View File
@@ -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<Self> {
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,
}
}
}
+1
View File
@@ -5,6 +5,7 @@ pub mod mutex;
pub mod once;
pub mod pthread_mutex;
pub mod rwlock;
pub mod semaphore;
pub mod waitval;
+70
View File
@@ -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<&timespec>) {
// 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<&timespec>) {
// 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);
}
}
}