use rustix_futex_sync::lock_api::{GetThreadId as _, RawMutex as _};
use rustix_futex_sync::{RawCondvar, RawMutex};
use core::mem::size_of;
use core::ptr;
use core::sync::atomic::{AtomicU32, AtomicUsize, Ordering};
use core::time::Duration;
use libc::c_int;
use crate::GetThreadId;
#[allow(non_camel_case_types)]
#[repr(C)]
struct PthreadMutexT {
lock: RawMutex,
count: AtomicU32,
owner: AtomicUsize,
kind: AtomicU32,
_spins: u32,
#[cfg(target_pointer_width = "64")]
_list: [usize; 2],
#[cfg(target_pointer_width = "32")]
_list: [usize; 1],
#[cfg(target_arch = "aarch64")]
_pad: usize,
}
libc_type!(PthreadMutexT, pthread_mutex_t);
fn kind_type(kind: u32) -> c_int {
(kind & 3) as c_int
}
#[allow(non_camel_case_types)]
#[repr(C)]
struct PthreadMutexattrT {
kind: AtomicU32,
#[cfg(target_arch = "aarch64")]
pad0: u32,
}
libc_type!(PthreadMutexattrT, pthread_mutexattr_t);
#[no_mangle]
unsafe extern "C" fn pthread_mutexattr_destroy(attr: *mut PthreadMutexattrT) -> c_int {
libc!(libc::pthread_mutexattr_destroy(checked_cast!(attr)));
ptr::drop_in_place(attr);
0
}
#[no_mangle]
unsafe extern "C" fn pthread_mutexattr_init(attr: *mut PthreadMutexattrT) -> c_int {
libc!(libc::pthread_mutexattr_init(checked_cast!(attr)));
ptr::write(
attr,
PthreadMutexattrT {
kind: AtomicU32::new(libc::PTHREAD_MUTEX_DEFAULT as u32),
#[cfg(target_arch = "aarch64")]
pad0: 0,
},
);
0
}
#[no_mangle]
unsafe extern "C" fn pthread_mutexattr_settype(attr: *mut PthreadMutexattrT, kind: c_int) -> c_int {
libc!(libc::pthread_mutexattr_settype(checked_cast!(attr), kind));
match kind {
libc::PTHREAD_MUTEX_NORMAL
| libc::PTHREAD_MUTEX_ERRORCHECK
| libc::PTHREAD_MUTEX_RECURSIVE => {}
_ => return libc::EINVAL,
}
(*attr).kind.store(kind as u32, Ordering::SeqCst);
0
}
#[no_mangle]
unsafe extern "C" fn pthread_mutexattr_setprotocol(
_attr: *mut PthreadMutexattrT,
protocol: c_int,
) -> c_int {
match protocol {
libc::PTHREAD_PRIO_NONE | libc::PTHREAD_PRIO_INHERIT | libc::PTHREAD_PRIO_PROTECT => 0,
_ => libc::EINVAL,
}
}
#[no_mangle]
unsafe extern "C" fn pthread_mutexattr_gettype(
attr: *mut PthreadMutexattrT,
kind: *mut c_int,
) -> c_int {
*kind = (*attr).kind.load(Ordering::SeqCst) as c_int;
0
}
#[no_mangle]
unsafe extern "C" fn pthread_mutex_destroy(mutex: *mut PthreadMutexT) -> c_int {
libc!(libc::pthread_mutex_destroy(checked_cast!(mutex)));
(*mutex).kind.store(!0, Ordering::SeqCst);
0
}
#[no_mangle]
unsafe extern "C" fn pthread_mutex_init(
mutex: *mut PthreadMutexT,
mutexattr: *const PthreadMutexattrT,
) -> c_int {
libc!(libc::pthread_mutex_init(
checked_cast!(mutex),
checked_cast!(mutexattr)
));
let kind = if mutexattr.is_null() {
libc::PTHREAD_MUTEX_DEFAULT as u32
} else {
(*mutexattr).kind.load(Ordering::SeqCst)
};
match kind as i32 {
libc::PTHREAD_MUTEX_NORMAL
| libc::PTHREAD_MUTEX_RECURSIVE
| libc::PTHREAD_MUTEX_ERRORCHECK => {}
_ => return libc::EINVAL,
}
ptr::write_bytes(mutex.cast::<u8>(), 0, size_of::<PthreadMutexT>());
(*mutex).kind.store(kind, Ordering::SeqCst);
0
}
#[no_mangle]
unsafe extern "C" fn pthread_mutex_lock(mutex: *mut PthreadMutexT) -> c_int {
libc!(libc::pthread_mutex_lock(checked_cast!(mutex)));
let mutex = &*mutex;
match kind_type(mutex.kind.load(Ordering::SeqCst)) {
libc::PTHREAD_MUTEX_RECURSIVE => {
let me = GetThreadId.nonzero_thread_id().get();
if mutex.owner.load(Ordering::Relaxed) == me {
mutex.count.fetch_add(1, Ordering::Relaxed);
return 0;
}
mutex.lock.lock();
mutex.owner.store(me, Ordering::Relaxed);
}
libc::PTHREAD_MUTEX_ERRORCHECK => {
let me = GetThreadId.nonzero_thread_id().get();
if mutex.owner.load(Ordering::Relaxed) == me {
return libc::EDEADLK;
}
mutex.lock.lock();
mutex.owner.store(me, Ordering::Relaxed);
}
_ => mutex.lock.lock(),
}
0
}
#[no_mangle]
unsafe extern "C" fn pthread_mutex_trylock(mutex: *mut PthreadMutexT) -> c_int {
libc!(libc::pthread_mutex_trylock(checked_cast!(mutex)));
let mutex = &*mutex;
match kind_type(mutex.kind.load(Ordering::SeqCst)) {
libc::PTHREAD_MUTEX_RECURSIVE => {
let me = GetThreadId.nonzero_thread_id().get();
if mutex.owner.load(Ordering::Relaxed) == me {
mutex.count.fetch_add(1, Ordering::Relaxed);
return 0;
}
if !mutex.lock.try_lock() {
return libc::EBUSY;
}
mutex.owner.store(me, Ordering::Relaxed);
}
libc::PTHREAD_MUTEX_ERRORCHECK => {
let me = GetThreadId.nonzero_thread_id().get();
if !mutex.lock.try_lock() {
return libc::EBUSY;
}
mutex.owner.store(me, Ordering::Relaxed);
}
_ => {
if !mutex.lock.try_lock() {
return libc::EBUSY;
}
}
}
0
}
#[no_mangle]
unsafe extern "C" fn pthread_mutex_unlock(mutex: *mut PthreadMutexT) -> c_int {
libc!(libc::pthread_mutex_unlock(checked_cast!(mutex)));
let mutex = &*mutex;
match kind_type(mutex.kind.load(Ordering::SeqCst)) {
libc::PTHREAD_MUTEX_RECURSIVE | libc::PTHREAD_MUTEX_ERRORCHECK => {
let me = GetThreadId.nonzero_thread_id().get();
if mutex.owner.load(Ordering::Relaxed) != me {
return libc::EPERM;
}
if mutex.count.load(Ordering::Relaxed) > 0 {
mutex.count.fetch_sub(1, Ordering::Relaxed);
return 0;
}
mutex.owner.store(0, Ordering::Relaxed);
mutex.lock.unlock();
}
_ => {
if !mutex.lock.is_locked() {
return libc::EPERM;
}
mutex.lock.unlock()
}
}
0
}
const SIZEOF_PTHREAD_COND_T: usize = 48;
#[cfg_attr(target_arch = "x86", repr(C, align(4)))]
#[cfg_attr(not(target_arch = "x86"), repr(C, align(8)))]
struct PthreadCondT {
inner: RawCondvar,
attr: PthreadCondattrT,
pad: [u8; SIZEOF_PTHREAD_COND_T - size_of::<RawCondvar>() - size_of::<PthreadCondattrT>()],
}
libc_type!(PthreadCondT, pthread_cond_t);
#[cfg(any(
target_arch = "x86",
target_arch = "x86_64",
target_arch = "arm",
all(target_arch = "aarch64", target_pointer_width = "32"),
target_arch = "riscv64",
))]
const SIZEOF_PTHREAD_CONDATTR_T: usize = 4;
#[cfg(all(target_arch = "aarch64", target_pointer_width = "64"))]
const SIZEOF_PTHREAD_CONDATTR_T: usize = 8;
#[repr(C, align(4))]
struct PthreadCondattrT {
pad: [u8; SIZEOF_PTHREAD_CONDATTR_T],
}
impl Default for PthreadCondattrT {
fn default() -> Self {
Self {
pad: [0_u8; SIZEOF_PTHREAD_CONDATTR_T],
}
}
}
libc_type!(PthreadCondattrT, pthread_condattr_t);
#[no_mangle]
unsafe extern "C" fn pthread_condattr_destroy(attr: *mut PthreadCondattrT) -> c_int {
libc!(libc::pthread_condattr_destroy(checked_cast!(attr)));
ptr::drop_in_place(attr);
0
}
#[no_mangle]
unsafe extern "C" fn pthread_condattr_init(attr: *mut PthreadCondattrT) -> c_int {
libc!(libc::pthread_condattr_init(checked_cast!(attr)));
ptr::write(attr, PthreadCondattrT::default());
0
}
#[no_mangle]
unsafe extern "C" fn pthread_condattr_setclock(
attr: *mut PthreadCondattrT,
clock_id: c_int,
) -> c_int {
libc!(libc::pthread_condattr_setclock(
checked_cast!(attr),
clock_id
));
let _ = attr;
if clock_id == libc::CLOCK_PROCESS_CPUTIME_ID || clock_id == libc::CLOCK_THREAD_CPUTIME_ID {
return libc::EINVAL;
}
0
}
#[no_mangle]
unsafe extern "C" fn pthread_cond_broadcast(cond: *mut PthreadCondT) -> c_int {
libc!(libc::pthread_cond_broadcast(checked_cast!(cond)));
(*cond).inner.notify_all();
0
}
#[no_mangle]
unsafe extern "C" fn pthread_cond_destroy(cond: *mut PthreadCondT) -> c_int {
libc!(libc::pthread_cond_destroy(checked_cast!(cond)));
ptr::drop_in_place(cond);
0
}
#[no_mangle]
unsafe extern "C" fn pthread_cond_init(
cond: *mut PthreadCondT,
attr: *const PthreadCondattrT,
) -> c_int {
libc!(libc::pthread_cond_init(
checked_cast!(cond),
checked_cast!(attr)
));
let attr = if attr.is_null() {
PthreadCondattrT::default()
} else {
ptr::read(attr)
};
ptr::write(
cond,
PthreadCondT {
inner: RawCondvar::new(),
attr,
pad: [0_u8;
SIZEOF_PTHREAD_COND_T - size_of::<RawCondvar>() - size_of::<PthreadCondattrT>()],
},
);
0
}
#[no_mangle]
unsafe extern "C" fn pthread_cond_signal(cond: *mut PthreadCondT) -> c_int {
libc!(libc::pthread_cond_signal(checked_cast!(cond)));
(*cond).inner.notify_one();
0
}
#[no_mangle]
unsafe extern "C" fn pthread_cond_wait(cond: *mut PthreadCondT, lock: *mut PthreadMutexT) -> c_int {
libc!(libc::pthread_cond_wait(
checked_cast!(cond),
checked_cast!(lock)
));
match kind_type((*lock).kind.load(Ordering::SeqCst)) {
libc::PTHREAD_MUTEX_NORMAL => (*cond).inner.wait(&(*lock).lock),
_ => return libc::EINVAL,
}
0
}
#[no_mangle]
unsafe extern "C" fn pthread_cond_timedwait(
cond: *mut PthreadCondT,
lock: *mut PthreadMutexT,
abstime: *const libc::timespec,
) -> c_int {
libc!(libc::pthread_cond_timedwait(
checked_cast!(cond),
checked_cast!(lock),
abstime,
));
let abstime = ptr::read(abstime);
let abstime = Duration::new(
abstime.tv_sec.try_into().unwrap(),
abstime.tv_nsec.try_into().unwrap(),
);
let now = rustix::time::clock_gettime(rustix::time::ClockId::Realtime);
let now = Duration::new(
now.tv_sec.try_into().unwrap(),
now.tv_nsec.try_into().unwrap(),
);
let reltime = abstime.saturating_sub(now);
match kind_type((*lock).kind.load(Ordering::SeqCst)) {
libc::PTHREAD_MUTEX_NORMAL => {
if (*cond).inner.wait_timeout(&(*lock).lock, reltime) {
0
} else {
libc::ETIMEDOUT
}
}
_ => return libc::EINVAL,
}
}