Compare commits

...

3 Commits

Author SHA1 Message Date
Andrea Ciliberti 9e9b47ad65 Refactor 2 years ago
Andrea Ciliberti bed08695a0 Refactor and suggestions 2 years ago
Andrea Ciliberti f74314dcdb Test update to upstream pthread 2 years ago
  1. 117
      src/condvar.rs
  2. 4
      src/lib.rs
  3. 26
      src/misc.rs
  4. 91
      src/mutex.rs
  5. 169
      src/rwlock.rs
  6. 87
      src/thread/attr.rs
  7. 53
      src/thread/mod.rs
  8. 8
      src/thread_keys.rs

117
src/condvar.rs

@ -1,117 +0,0 @@ @@ -1,117 +0,0 @@
//! PThread condition variables implemented using libctru.
#[no_mangle]
pub unsafe extern "C" fn pthread_cond_init(
cond: *mut libc::pthread_cond_t,
_attr: *const libc::pthread_condattr_t,
) -> libc::c_int {
ctru_sys::CondVar_Init(cond as _);
0
}
#[no_mangle]
pub unsafe extern "C" fn pthread_cond_signal(cond: *mut libc::pthread_cond_t) -> libc::c_int {
ctru_sys::CondVar_WakeUp(cond as _, 1);
0
}
#[no_mangle]
pub unsafe extern "C" fn pthread_cond_broadcast(cond: *mut libc::pthread_cond_t) -> libc::c_int {
ctru_sys::CondVar_WakeUp(cond as _, -1);
0
}
#[no_mangle]
pub unsafe extern "C" fn pthread_cond_wait(
cond: *mut libc::pthread_cond_t,
lock: *mut libc::pthread_mutex_t,
) -> libc::c_int {
ctru_sys::CondVar_Wait(cond as _, lock as _);
0
}
#[no_mangle]
pub unsafe extern "C" fn pthread_cond_timedwait(
cond: *mut libc::pthread_cond_t,
lock: *mut libc::pthread_mutex_t,
abstime: *const libc::timespec,
) -> libc::c_int {
// 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, std::ptr::null_mut());
if r != 0 {
return r;
}
// 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 = ctru_sys::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]
pub unsafe extern "C" fn pthread_cond_destroy(_cond: *mut libc::pthread_cond_t) -> libc::c_int {
0
}
#[no_mangle]
pub extern "C" fn pthread_condattr_init(_attr: *const libc::pthread_condattr_t) -> libc::c_int {
0
}
#[no_mangle]
pub extern "C" fn pthread_condattr_destroy(_attr: *const libc::pthread_condattr_t) -> libc::c_int {
0
}
#[no_mangle]
pub extern "C" fn pthread_condattr_getclock(
_attr: *const libc::pthread_condattr_t,
clock_id: *mut libc::clockid_t,
) -> libc::c_int {
unsafe {
*clock_id = libc::CLOCK_REALTIME;
}
0
}
#[no_mangle]
pub extern "C" fn pthread_condattr_setclock(
_attr: *mut libc::pthread_condattr_t,
clock_id: libc::clockid_t,
) -> libc::c_int {
// only one clock is supported, so all other options are considered an error
if clock_id == libc::CLOCK_REALTIME {
0
} else {
libc::EINVAL
}
}

4
src/lib.rs

@ -2,9 +2,5 @@ @@ -2,9 +2,5 @@
#![allow(non_camel_case_types)]
#![allow(clippy::missing_safety_doc)]
mod condvar;
mod misc;
mod mutex;
mod rwlock;
mod thread;
mod thread_keys;

26
src/misc.rs

@ -1,26 +0,0 @@ @@ -1,26 +0,0 @@
//! Miscellaneous pthread functions
#[no_mangle]
pub unsafe extern "C" fn sched_yield() -> libc::c_int {
ctru_sys::svcSleepThread(0);
0
}
#[no_mangle]
pub unsafe extern "C" fn pthread_sigmask(
_how: ::libc::c_int,
_set: *const libc::sigset_t,
_oldset: *mut libc::sigset_t,
) -> ::libc::c_int {
-1
}
#[no_mangle]
pub extern "C" fn pthread_atfork(
_prepare: Option<unsafe extern "C" fn()>,
_parent: Option<unsafe extern "C" fn()>,
_child: Option<unsafe extern "C" fn()>,
) -> libc::c_int {
0
}

91
src/mutex.rs

@ -1,91 +0,0 @@ @@ -1,91 +0,0 @@
//! PThread mutex implemented using libctru.
#[no_mangle]
pub unsafe extern "C" fn pthread_mutexattr_init(
_attr: *mut libc::pthread_mutexattr_t,
) -> libc::c_int {
0
}
#[no_mangle]
pub unsafe extern "C" fn pthread_mutexattr_settype(
attr: *mut libc::pthread_mutexattr_t,
_type: libc::c_int,
) -> libc::c_int {
let attr: *mut libc::c_int = attr as _;
*attr = _type as _;
0
}
#[no_mangle]
pub unsafe extern "C" fn pthread_mutex_init(
lock: *mut libc::pthread_mutex_t,
attr: *const libc::pthread_mutexattr_t,
) -> libc::c_int {
let lock = lock as *mut u8;
let attr: libc::c_int = *(attr as *const libc::c_int);
if attr == libc::PTHREAD_MUTEX_NORMAL {
ctru_sys::LightLock_Init(lock as _);
} else if attr == libc::PTHREAD_MUTEX_RECURSIVE {
ctru_sys::RecursiveLock_Init(lock as _)
}
*(lock.offset(39)) = attr as u8;
0
}
#[no_mangle]
pub unsafe extern "C" fn pthread_mutex_destroy(_lock: *mut libc::pthread_mutex_t) -> libc::c_int {
0
}
#[no_mangle]
pub unsafe extern "C" fn pthread_mutex_lock(lock: *mut libc::pthread_mutex_t) -> libc::c_int {
let lock = lock as *const u8;
if *(lock.offset(39)) as u8 == libc::PTHREAD_MUTEX_NORMAL as u8 {
ctru_sys::LightLock_Lock(lock as _);
} else if *(lock.offset(39)) as u8 == libc::PTHREAD_MUTEX_RECURSIVE as u8 {
ctru_sys::RecursiveLock_Lock(lock as _);
}
0
}
#[no_mangle]
pub unsafe extern "C" fn pthread_mutex_trylock(lock: *mut libc::pthread_mutex_t) -> libc::c_int {
let lock = lock as *const u8;
if *(lock.offset(39)) as u8 == libc::PTHREAD_MUTEX_NORMAL as u8 {
return ctru_sys::LightLock_TryLock(lock as _);
} else if *(lock.offset(39)) as u8 == libc::PTHREAD_MUTEX_RECURSIVE as u8 {
return ctru_sys::RecursiveLock_TryLock(lock as _);
}
-1
}
#[no_mangle]
pub unsafe extern "C" fn pthread_mutex_unlock(lock: *mut libc::pthread_mutex_t) -> libc::c_int {
let lock = lock as *const u8;
if *(lock.offset(39)) as u8 == libc::PTHREAD_MUTEX_NORMAL as u8 {
ctru_sys::LightLock_Unlock(lock as _);
} else if *(lock.offset(39)) as u8 == libc::PTHREAD_MUTEX_RECURSIVE as u8 {
ctru_sys::RecursiveLock_Unlock(lock as _);
}
0
}
#[no_mangle]
pub unsafe extern "C" fn pthread_mutexattr_destroy(
_attr: *mut libc::pthread_mutexattr_t,
) -> libc::c_int {
0
}

169
src/rwlock.rs

@ -1,169 +0,0 @@ @@ -1,169 +0,0 @@
//! PThread read-write lock implemented using libctru.
use crate::{condvar, mutex};
struct rwlock_clear {
mutex: libc::pthread_mutex_t,
cvar: i32,
num_readers: i32,
writer_active: bool,
initialized: bool,
}
/// Initializes the rwlock internal members if they weren't already
fn init_rwlock(lock: *mut libc::pthread_rwlock_t) {
let lock = lock as *mut rwlock_clear;
unsafe {
if !(*lock).initialized {
let mut attr = std::mem::MaybeUninit::<libc::pthread_mutexattr_t>::uninit();
mutex::pthread_mutexattr_init(attr.as_mut_ptr());
let mut attr = attr.assume_init();
mutex::pthread_mutexattr_settype(&mut attr, libc::PTHREAD_MUTEX_NORMAL);
mutex::pthread_mutex_init(&mut (*lock).mutex, &attr);
condvar::pthread_cond_init(&mut (*lock).cvar as *mut i32 as *mut _, core::ptr::null());
(*lock).num_readers = 0;
(*lock).writer_active = false;
(*lock).initialized = true;
}
}
}
#[no_mangle]
pub unsafe extern "C" fn pthread_rwlock_init(
lock: *mut libc::pthread_rwlock_t,
_attr: *const libc::pthread_rwlockattr_t,
) -> libc::c_int {
init_rwlock(lock);
0
}
#[no_mangle]
pub unsafe extern "C" fn pthread_rwlock_destroy(_lock: *mut libc::pthread_rwlock_t) -> libc::c_int {
0
}
#[no_mangle]
pub unsafe extern "C" fn pthread_rwlock_rdlock(lock: *mut libc::pthread_rwlock_t) -> libc::c_int {
init_rwlock(lock);
let lock = lock as *mut rwlock_clear;
mutex::pthread_mutex_lock(&mut (*lock).mutex);
while (*lock).writer_active {
condvar::pthread_cond_wait(&mut (*lock).cvar as *mut i32 as _, &mut (*lock).mutex);
}
(*lock).num_readers += 1;
mutex::pthread_mutex_unlock(&mut (*lock).mutex);
0
}
#[no_mangle]
pub unsafe extern "C" fn pthread_rwlock_tryrdlock(
lock: *mut libc::pthread_rwlock_t,
) -> libc::c_int {
init_rwlock(lock);
let lock = lock as *mut rwlock_clear;
if mutex::pthread_mutex_trylock(&mut (*lock).mutex) != 0 {
return -1;
}
while (*lock).writer_active {
condvar::pthread_cond_wait(&mut (*lock).cvar as *mut i32 as _, &mut (*lock).mutex);
}
(*lock).num_readers += 1;
mutex::pthread_mutex_unlock(&mut (*lock).mutex);
0
}
#[no_mangle]
pub unsafe extern "C" fn pthread_rwlock_wrlock(lock: *mut libc::pthread_rwlock_t) -> libc::c_int {
init_rwlock(lock);
let lock = lock as *mut rwlock_clear;
mutex::pthread_mutex_lock(&mut (*lock).mutex);
while (*lock).writer_active || (*lock).num_readers > 0 {
condvar::pthread_cond_wait(&mut (*lock).cvar as *mut i32 as _, &mut (*lock).mutex);
}
(*lock).writer_active = true;
mutex::pthread_mutex_unlock(&mut (*lock).mutex);
0
}
#[no_mangle]
pub unsafe extern "C" fn pthread_rwlock_trywrlock(
lock: *mut libc::pthread_rwlock_t,
) -> libc::c_int {
init_rwlock(lock);
let lock = lock as *mut rwlock_clear;
if mutex::pthread_mutex_trylock(&mut (*lock).mutex) != 0 {
return -1;
}
while (*lock).writer_active || (*lock).num_readers > 0 {
condvar::pthread_cond_wait(&mut (*lock).cvar as *mut i32 as _, &mut (*lock).mutex);
}
(*lock).writer_active = true;
mutex::pthread_mutex_unlock(&mut (*lock).mutex);
0
}
#[no_mangle]
pub unsafe extern "C" fn pthread_rwlock_unlock(lock: *mut libc::pthread_rwlock_t) -> libc::c_int {
init_rwlock(lock);
let lock = lock as *mut rwlock_clear;
mutex::pthread_mutex_lock(&mut (*lock).mutex);
// If there are readers and no writer => Must be a reader
if (*lock).num_readers > 0 && !(*lock).writer_active {
(*lock).num_readers -= 1;
// If there are no more readers, signal to a waiting writer
if (*lock).num_readers == 0 {
condvar::pthread_cond_signal(&mut (*lock).cvar as *mut i32 as _);
}
// If there are no readers and a writer => Must be a writer
} else if (*lock).num_readers == 0 && (*lock).writer_active {
(*lock).writer_active = false;
condvar::pthread_cond_broadcast(&mut (*lock).cvar as *mut i32 as _);
}
mutex::pthread_mutex_unlock(&mut (*lock).mutex);
0
}
#[no_mangle]
pub unsafe extern "C" fn pthread_rwlockattr_init(
_attr: *mut libc::pthread_rwlockattr_t,
) -> libc::c_int {
0
}
#[no_mangle]
pub unsafe extern "C" fn pthread_rwlockattr_destroy(
_attr: *mut libc::pthread_rwlockattr_t,
) -> libc::c_int {
0
}

87
src/thread/attr.rs

@ -1,92 +1,16 @@ @@ -1,92 +1,16 @@
use static_assertions::const_assert;
use std::mem;
/// Internal struct for storing pthread attribute data
/// Must be less than or equal to the size of `libc::pthread_attr_t`. We assert
/// this below via static_assertions.
pub struct PThreadAttr {
pub(crate) stack_size: libc::size_t,
pub(crate) priority: libc::c_int,
pub(crate) processor_id: libc::c_int,
}
const_assert!(mem::size_of::<PThreadAttr>() <= mem::size_of::<libc::pthread_attr_t>());
impl Default for PThreadAttr {
fn default() -> Self {
// Note: we are ignoring the result here, but errors shouldn't occur
// since we're using a valid handle.
let mut priority = 0;
unsafe { ctru_sys::svcGetThreadPriority(&mut priority, ctru_sys::CUR_THREAD_HANDLE) };
PThreadAttr {
stack_size: libc::PTHREAD_STACK_MIN,
// If no priority value is specified, spawn with the same priority
// as the current thread
priority,
// If no processor is specified, spawn on the default core.
// (determined by the application's Exheader)
processor_id: -2,
}
}
}
#[no_mangle]
pub unsafe extern "C" fn pthread_attr_init(attr: *mut libc::pthread_attr_t) -> libc::c_int {
let attr = attr as *mut PThreadAttr;
*attr = PThreadAttr::default();
0
}
#[no_mangle]
pub unsafe extern "C" fn pthread_attr_destroy(attr: *mut libc::pthread_attr_t) -> libc::c_int {
std::ptr::drop_in_place(attr as *mut PThreadAttr);
0
}
#[no_mangle]
pub unsafe extern "C" fn pthread_attr_setstacksize(
attr: *mut libc::pthread_attr_t,
stack_size: libc::size_t,
) -> libc::c_int {
let attr = attr as *mut PThreadAttr;
(*attr).stack_size = stack_size;
0
}
#[no_mangle]
pub unsafe extern "C" fn pthread_attr_getschedparam(
attr: *const libc::pthread_attr_t,
param: *mut libc::sched_param,
) -> libc::c_int {
let attr = attr as *const PThreadAttr;
(*param).sched_priority = (*attr).priority;
0
}
#[no_mangle]
pub unsafe extern "C" fn pthread_attr_setschedparam(
attr: *mut libc::pthread_attr_t,
param: *const libc::sched_param,
) -> libc::c_int {
let attr = attr as *mut PThreadAttr;
(*attr).priority = (*param).sched_priority;
// TODO: we could validate the priority here if we wanted?
0
}
const_assert!(mem::size_of::<ctru_sys::pthread_attr_t>() <= mem::size_of::<libc::pthread_attr_t>());
// No way to currently use any of these functions, since the thread creation syscall doesn't support them.
/*
#[no_mangle]
pub unsafe extern "C" fn pthread_attr_getprocessorid_np(
attr: *const libc::pthread_attr_t,
processor_id: *mut libc::c_int,
) -> libc::c_int {
let attr = attr as *mut PThreadAttr;
let attr = attr as *mut pthread_attr_t;
(*processor_id) = (*attr).processor_id;
0
@ -97,10 +21,11 @@ pub unsafe extern "C" fn pthread_attr_setprocessorid_np( @@ -97,10 +21,11 @@ pub unsafe extern "C" fn pthread_attr_setprocessorid_np(
attr: *mut libc::pthread_attr_t,
processor_id: libc::c_int,
) -> libc::c_int {
let attr = attr as *mut PThreadAttr;
let attr = attr as *mut pthread_attr_t;
(*attr).processor_id = processor_id;
// TODO: we could validate the processor ID here if we wanted?
0
}
*/

53
src/thread.rs → src/thread/mod.rs

@ -1,6 +1,5 @@ @@ -1,6 +1,5 @@
//! PThread threads implemented using libctru.
use attr::PThreadAttr;
use spin::rwlock::RwLock;
use std::collections::BTreeMap;
use std::ptr;
@ -47,16 +46,18 @@ impl<T> Clone for ShareablePtr<T> { @@ -47,16 +46,18 @@ impl<T> Clone for ShareablePtr<T> {
}
#[no_mangle]
pub unsafe extern "C" fn pthread_create(
pub unsafe extern "C" fn __syscall_thread_create(
native: *mut libc::pthread_t,
attr: *const libc::pthread_attr_t,
entrypoint: extern "C" fn(_: *mut libc::c_void) -> *mut libc::c_void,
value: *mut libc::c_void,
arg: *mut libc::c_void,
_stackaddr: *const libc::c_void,
stacksize: libc::size_t,
) -> libc::c_int {
let attr = attr as *const PThreadAttr;
let stack_size = (*attr).stack_size;
let priority = (*attr).priority;
let processor_id = (*attr).processor_id;
let stack_size = stacksize;
// let priority = (*attr).schedparam.sched_priority;
let mut priority = 0;
let _ = ctru_sys::svcGetThreadPriority(&mut priority, ctru_sys::CUR_THREAD_HANDLE);
// let processor_id = (*attr).processor_id;
let thread_id = NEXT_ID.fetch_add(1, Ordering::SeqCst) as libc::pthread_t;
@ -74,7 +75,7 @@ pub unsafe extern "C" fn pthread_create( @@ -74,7 +75,7 @@ pub unsafe extern "C" fn pthread_create(
let main: *mut Box<dyn FnOnce()> = Box::into_raw(Box::new(Box::new(move || {
THREAD_ID = thread_id;
let result = entrypoint(value);
let result = entrypoint(arg);
// Update the threads map with the result, and remove this thread if
// it's detached.
@ -99,7 +100,7 @@ pub unsafe extern "C" fn pthread_create( @@ -99,7 +100,7 @@ pub unsafe extern "C" fn pthread_create(
main as *mut libc::c_void,
stack_size,
priority,
processor_id,
0, // Normal CPU
false,
);
@ -138,19 +139,17 @@ pub unsafe extern "C" fn pthread_create( @@ -138,19 +139,17 @@ pub unsafe extern "C" fn pthread_create(
}
#[no_mangle]
pub unsafe extern "C" fn pthread_join(
thread_id: libc::pthread_t,
return_value: *mut *mut libc::c_void,
) -> libc::c_int {
pub unsafe extern "C" fn __syscall_thread_join(thread_id: libc::pthread_t) -> *const libc::c_void {
// The return value is returned as a pointer
if thread_id == MAIN_THREAD_ID {
// This is not a valid thread to join on
return libc::EINVAL;
return std::ptr::null();
}
let thread_map = THREADS.read();
let Some(&pthread) = thread_map.get(&thread_id) else {
// This is not a valid thread ID
return libc::ESRCH
return std::ptr::null();
};
// We need to drop our read guard so it doesn't stay locked while joining
// the thread.
@ -158,13 +157,12 @@ pub unsafe extern "C" fn pthread_join( @@ -158,13 +157,12 @@ pub unsafe extern "C" fn pthread_join(
if pthread.is_detached {
// Cannot join on a detached thread
return libc::EINVAL;
return std::ptr::null();
}
let result = ctru_sys::threadJoin(pthread.thread.0, u64::MAX);
if ctru_sys::R_FAILED(result) {
// TODO: improve the error code by checking the result further?
return libc::EINVAL;
return std::ptr::null();
}
ctru_sys::threadFree(pthread.thread.0);
@ -172,16 +170,14 @@ pub unsafe extern "C" fn pthread_join( @@ -172,16 +170,14 @@ pub unsafe extern "C" fn pthread_join(
// This should always be Some, but we use an if let just in case.
if let Some(thread_data) = thread_data {
if !return_value.is_null() {
*return_value = thread_data.result.0;
}
return thread_data.result.0;
}
0
std::ptr::null()
}
#[no_mangle]
pub unsafe extern "C" fn pthread_detach(thread_id: libc::pthread_t) -> libc::c_int {
pub unsafe extern "C" fn __syscall_thread_detach(thread_id: libc::pthread_t) -> libc::c_int {
if thread_id == MAIN_THREAD_ID {
// This is not a valid thread to detach
return libc::EINVAL;
@ -210,7 +206,7 @@ pub unsafe extern "C" fn pthread_detach(thread_id: libc::pthread_t) -> libc::c_i @@ -210,7 +206,7 @@ pub unsafe extern "C" fn pthread_detach(thread_id: libc::pthread_t) -> libc::c_i
}
#[no_mangle]
pub unsafe extern "C" fn pthread_self() -> libc::pthread_t {
pub unsafe extern "C" fn __syscall_thread_self() -> libc::pthread_t {
let thread_id = THREAD_ID;
if thread_id == MAIN_THREAD_ID {
@ -240,6 +236,13 @@ pub unsafe extern "C" fn pthread_self() -> libc::pthread_t { @@ -240,6 +236,13 @@ pub unsafe extern "C" fn pthread_self() -> libc::pthread_t {
thread_id
}
#[no_mangle]
pub unsafe extern "C" fn sched_yield() -> libc::c_int {
ctru_sys::svcSleepThread(0);
0
}
/// Closes a kernel handle on drop.
struct Handle(ctru_sys::Handle);

8
src/thread_keys.rs

@ -20,7 +20,7 @@ fn is_valid_key(key: Key) -> bool { @@ -20,7 +20,7 @@ fn is_valid_key(key: Key) -> bool {
}
#[no_mangle]
pub unsafe extern "C" fn pthread_key_create(
pub unsafe extern "C" fn __syscall_tls_create(
key: *mut libc::pthread_key_t,
destructor: Option<Destructor>,
) -> libc::c_int {
@ -33,7 +33,7 @@ pub unsafe extern "C" fn pthread_key_create( @@ -33,7 +33,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 {
pub unsafe extern "C" fn __syscall_tls_delete(key: libc::pthread_key_t) -> libc::c_int {
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,
@ -46,7 +46,7 @@ pub unsafe extern "C" fn pthread_key_delete(key: libc::pthread_key_t) -> libc::c @@ -46,7 +46,7 @@ pub unsafe extern "C" fn pthread_key_delete(key: libc::pthread_key_t) -> libc::c
}
#[no_mangle]
pub unsafe extern "C" fn pthread_getspecific(key: libc::pthread_key_t) -> *mut libc::c_void {
pub unsafe extern "C" fn __syscall_tls_get(key: libc::pthread_key_t) -> *mut libc::c_void {
if let Some(&value) = LOCALS.get(&(key as Key)) {
value as _
} else {
@ -56,7 +56,7 @@ pub unsafe extern "C" fn pthread_getspecific(key: libc::pthread_key_t) -> *mut l @@ -56,7 +56,7 @@ pub unsafe extern "C" fn pthread_getspecific(key: libc::pthread_key_t) -> *mut l
}
#[no_mangle]
pub unsafe extern "C" fn pthread_setspecific(
pub unsafe extern "C" fn __syscall_tls_set(
key: libc::pthread_key_t,
value: *const libc::c_void,
) -> libc::c_int {

Loading…
Cancel
Save