Rustify pthread_rwlock_t.
This commit is contained in:
@@ -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
@@ -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()
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@ pub mod mutex;
|
||||
|
||||
pub mod once;
|
||||
pub mod pthread_mutex;
|
||||
pub mod rwlock;
|
||||
pub mod semaphore;
|
||||
pub mod waitval;
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user