diff --git a/Cargo.toml b/Cargo.toml index 31c8006..7df95aa 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -8,4 +8,5 @@ edition = "2021" [dependencies] libc = "0.2.116" ctru-sys = { git = "https://github.com/Meziu/ctru-rs.git" } -spin = { version = "0.9", default-features = false, features = ["rwlock", "std"] } \ No newline at end of file +spin = { version = "0.9", default-features = false, features = ["rwlock", "std"] } +static_assertions = "1.0" \ No newline at end of file diff --git a/src/lib.rs b/src/lib.rs index 623720b..63324fd 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -3,6 +3,8 @@ #![allow(non_camel_case_types)] #![allow(clippy::missing_safety_doc)] +use std::ptr; + /// Call this somewhere to force Rust to link this module. /// The call doesn't need to execute, just exist. /// @@ -13,48 +15,153 @@ pub fn init() {} #[no_mangle] pub unsafe extern "C" fn pthread_create( - _native: *mut libc::pthread_t, - _attr: *const libc::pthread_attr_t, - _f: extern "C" fn(_: *mut libc::c_void) -> *mut libc::c_void, - _value: *mut libc::c_void, + 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, ) -> libc::c_int { - 1 + let attr = attr as *const PThreadAttr; + + let stack_size = (*attr).stack_size.unwrap_or(libc::PTHREAD_STACK_MIN) as ctru_sys::size_t; + + // If no priority value is specified, spawn with the same + // priority as the parent thread + let priority = (*attr).priority.unwrap_or_else(|| pthread_getpriority()); + + // If no affinity is specified, spawn on the default core (determined by + // the application's Exheader) + let affinity = (*attr).affinity.unwrap_or(-2); + + extern "C" fn thread_start(main: *mut libc::c_void) { + unsafe { + Box::from_raw(main as *mut Box *mut libc::c_void>)(); + } + } + + // The closure needs a fat pointer (64 bits) to work since it captures a variable and is thus a + // trait object, but *mut void is only 32 bits. We need double indirection to pass along the + // full closure data. + // We make this closure in the first place because threadCreate expects a void return type, but + // entrypoint returns a pointer so the types are incompatible. + let main: *mut Box *mut libc::c_void> = + Box::into_raw(Box::new(Box::new(move || entrypoint(value)))); + + let handle = ctru_sys::threadCreate( + Some(thread_start), + main as *mut libc::c_void, + stack_size, + priority, + affinity, + false, + ); + + if handle.is_null() { + // There was some error, but libctru doesn't expose the result. + // We assume there was an incorrect parameter (such as too low of a priority). + // We also need to clean up the closure at this time. + drop(Box::from_raw(main)); + return libc::EINVAL; + } + + *native = handle as _; + + 0 } #[no_mangle] pub unsafe extern "C" fn pthread_join( - _native: libc::pthread_t, + native: libc::pthread_t, _value: *mut *mut libc::c_void, ) -> libc::c_int { - 1 + ctru_sys::threadJoin(native as ctru_sys::Thread, u64::MAX); + ctru_sys::threadFree(native as ctru_sys::Thread); + + 0 } #[no_mangle] -pub unsafe extern "C" fn pthread_detach(_thread: libc::pthread_t) -> libc::c_int { - 1 +pub unsafe extern "C" fn pthread_detach(thread: libc::pthread_t) -> libc::c_int { + ctru_sys::threadDetach(thread as ctru_sys::Thread); + + 0 } #[no_mangle] -pub unsafe extern "C" fn pthread_attr_init(_attr: *mut libc::pthread_attr_t) -> libc::c_int { - 1 +pub unsafe extern "C" fn pthread_getpriority() -> libc::c_int { + let mut priority = 0; + ctru_sys::svcGetThreadPriority(&mut priority, ctru_sys::CUR_THREAD_HANDLE); + priority } +/// 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. +struct PThreadAttr { + stack_size: Option, + priority: Option, + affinity: Option, +} + +static_assertions::const_assert!( + std::mem::size_of::() <= std::mem::size_of::() +); + #[no_mangle] -pub unsafe extern "C" fn pthread_attr_destroy(_attr: *mut libc::pthread_attr_t) -> libc::c_int { - 1 +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 { + stack_size: None, + priority: None, + affinity: None, + }; + + 0 +} + +#[no_mangle] +pub unsafe extern "C" fn pthread_attr_destroy(attr: *mut libc::pthread_attr_t) -> libc::c_int { + 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, + attr: *mut libc::pthread_attr_t, + stack_size: libc::size_t, +) -> libc::c_int { + let attr = attr as *mut PThreadAttr; + (*attr).stack_size = Some(stack_size); + + 0 +} + +#[no_mangle] +pub unsafe extern "C" fn pthread_attr_setpriority( + attr: *mut libc::pthread_attr_t, + priority: libc::c_int, ) -> libc::c_int { - 1 + let attr = attr as *mut PThreadAttr; + (*attr).priority = Some(priority); + + 0 +} + +#[no_mangle] +pub unsafe extern "C" fn pthread_attr_setaffinity( + attr: *mut libc::pthread_attr_t, + affinity: libc::c_int, +) -> libc::c_int { + let attr = attr as *mut PThreadAttr; + (*attr).affinity = Some(affinity); + + 0 } #[no_mangle] pub unsafe extern "C" fn sched_yield() -> libc::c_int { - 1 + ctru_sys::svcSleepThread(0); + + 0 } #[no_mangle] @@ -407,7 +514,6 @@ pub unsafe extern "C" fn pthread_sigmask( use spin::rwlock::RwLock; use std::collections::BTreeMap; -use std::ptr; use std::sync::atomic::{AtomicUsize, Ordering}; type Key = usize;