diff --git a/Cargo.toml b/Cargo.toml index 18434e0..54b894a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -7,4 +7,4 @@ edition = "2021" [dependencies] libc = "0.2.116" -once_cell = "1.0" \ No newline at end of file +spin = { version = "0.9", default-features = false, features = ["rwlock", "std"] } \ No newline at end of file diff --git a/src/lib.rs b/src/lib.rs index 137a85f..b9eaf68 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -131,9 +131,40 @@ pub unsafe extern "C" fn pthread_cond_timedwait( lock: *mut libc::pthread_mutex_t, abstime: *const libc::timespec, ) -> libc::c_int { - let nsec: i64 = ((*abstime).tv_sec as i64 * 1_000_000_000) + (*abstime).tv_nsec as i64; + // libctru expects a duration, but we have an absolute timestamp. + // Convert to a duration before calling libctru. + + // Get the current time so we can make a duration + let mut now = libc::timeval { + tv_sec: 0, + tv_usec: 0, + }; + let r = libc::gettimeofday(&mut now, ptr::null_mut()); + if r != 0 { + return r; + } - CondVar_WaitTimeout(cond as _, lock as _, nsec) + // Calculate the duration + let duration_nsec = (*abstime) + .tv_sec + // Get the difference in seconds + .saturating_sub(now.tv_sec) + // Convert to nanoseconds + .saturating_mul(1_000_000_000) + // Add the difference in nanoseconds + .saturating_add((*abstime).tv_nsec as i64) + .saturating_sub(now.tv_usec as i64 * 1_000) + // Don't go negative + .max(0); + + let r = CondVar_WaitTimeout(cond as _, lock as _, duration_nsec); + + // CondVar_WaitTimeout returns a boolean which is true (nonzero) if it timed out + if r == 0 { + 0 + } else { + libc::ETIMEDOUT + } } #[no_mangle] @@ -399,26 +430,24 @@ pub unsafe extern "C" fn pthread_rwlockattr_destroy( // THREAD KEYS IMPLEMENTATION FOR RUST STD -use once_cell::sync::Lazy; +use spin::rwlock::RwLock; use std::collections::BTreeMap; use std::ptr; use std::sync::atomic::{AtomicUsize, Ordering}; -use std::sync::{PoisonError, RwLock}; type Key = usize; type Destructor = unsafe extern "C" fn(*mut libc::c_void); static NEXT_KEY: AtomicUsize = AtomicUsize::new(1); -static KEYS: Lazy>>> = Lazy::new(RwLock::default); +// This is a spin-lock RwLock which yields the thread every loop +static KEYS: RwLock>, spin::Yield> = RwLock::new(BTreeMap::new()); #[thread_local] static mut LOCALS: BTreeMap = BTreeMap::new(); fn is_valid_key(key: Key) -> bool { - KEYS.read() - .unwrap_or_else(PoisonError::into_inner) - .contains_key(&(key as Key)) + KEYS.read().contains_key(&(key as Key)) } #[no_mangle] @@ -427,9 +456,7 @@ pub unsafe extern "C" fn pthread_key_create( destructor: Option, ) -> libc::c_int { let new_key = NEXT_KEY.fetch_add(1, Ordering::SeqCst); - KEYS.write() - .unwrap_or_else(PoisonError::into_inner) - .insert(new_key, destructor); + KEYS.write().insert(new_key, destructor); *key = new_key as libc::pthread_key_t; @@ -438,11 +465,7 @@ pub unsafe extern "C" fn pthread_key_create( #[no_mangle] pub unsafe extern "C" fn pthread_key_delete(key: libc::pthread_key_t) -> libc::c_int { - match KEYS - .write() - .unwrap_or_else(PoisonError::into_inner) - .remove(&(key as Key)) - { + match KEYS.write().remove(&(key as Key)) { // We had a entry, so it was a valid key. // It's officially undefined behavior if they use the key after this, // so don't worry about cleaning up LOCALS, especially since we can't