From 27a560102054d5c4c349aebc05723817dc5016fd Mon Sep 17 00:00:00 2001 From: AzureMarker <mark.drobnak@gmail.com> Date: Wed, 9 Feb 2022 21:37:56 -0800 Subject: [PATCH 1/6] Change estimated error code when thread spawning fails --- src/lib.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 63324fd..77b7311 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -57,10 +57,10 @@ pub unsafe extern "C" fn pthread_create( 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 assume there was permissions issue (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; + return libc::EPERM; } *native = handle as _; From ba5c5d787e57ee6c62153a10ff68a70036395407 Mon Sep 17 00:00:00 2001 From: AzureMarker <mark.drobnak@gmail.com> Date: Fri, 11 Feb 2022 21:22:43 -0800 Subject: [PATCH 2/6] Change to more standard priority function interfaces --- src/lib.rs | 63 ++++++++++++++++++++++++++++++++++-------------------- 1 file changed, 40 insertions(+), 23 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 77b7311..0314cb2 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -21,16 +21,9 @@ pub unsafe extern "C" fn pthread_create( value: *mut libc::c_void, ) -> libc::c_int { 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); + let stack_size = (*attr).stack_size as ctru_sys::size_t; + let priority = (*attr).priority; + let affinity = (*attr).affinity; extern "C" fn thread_start(main: *mut libc::c_void) { unsafe { @@ -97,9 +90,25 @@ pub unsafe extern "C" fn pthread_getpriority() -> libc::c_int { /// 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<libc::size_t>, - priority: Option<libc::c_int>, - affinity: Option<libc::c_int>, + stack_size: libc::size_t, + priority: libc::c_int, + affinity: libc::c_int, +} + +impl Default for PThreadAttr { + fn default() -> Self { + PThreadAttr { + stack_size: libc::PTHREAD_STACK_MIN, + + // If no priority value is specified, spawn with the same + // priority as the current thread + priority: unsafe { pthread_getpriority() }, + + // If no affinity is specified, spawn on the default core (determined by + // the application's Exheader) + affinity: -2, + } + } } static_assertions::const_assert!( @@ -109,11 +118,7 @@ static_assertions::const_assert!( #[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 { - stack_size: None, - priority: None, - affinity: None, - }; + *attr = PThreadAttr::default(); 0 } @@ -130,18 +135,30 @@ pub unsafe extern "C" fn pthread_attr_setstacksize( stack_size: libc::size_t, ) -> libc::c_int { let attr = attr as *mut PThreadAttr; - (*attr).stack_size = Some(stack_size); + (*attr).stack_size = stack_size; 0 } #[no_mangle] -pub unsafe extern "C" fn pthread_attr_setpriority( +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, - priority: libc::c_int, + param: *const libc::sched_param, ) -> libc::c_int { let attr = attr as *mut PThreadAttr; - (*attr).priority = Some(priority); + (*attr).priority = (*param).sched_priority; + + // TODO: we could validate the priority here if we wanted? 0 } @@ -152,7 +169,7 @@ pub unsafe extern "C" fn pthread_attr_setaffinity( affinity: libc::c_int, ) -> libc::c_int { let attr = attr as *mut PThreadAttr; - (*attr).affinity = Some(affinity); + (*attr).affinity = affinity; 0 } From 717938f682593af9d91cadf0b6a14e3fe8adb420 Mon Sep 17 00:00:00 2001 From: AzureMarker <mark.drobnak@gmail.com> Date: Sat, 12 Feb 2022 19:16:35 -0800 Subject: [PATCH 3/6] Rename affinity to "ideal processor" This follows the 3dbrew.org wiki naming. Also added a get version. --- src/lib.rs | 31 ++++++++++++++++++++++--------- 1 file changed, 22 insertions(+), 9 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 0314cb2..4db289e 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -23,7 +23,7 @@ pub unsafe extern "C" fn pthread_create( let attr = attr as *const PThreadAttr; let stack_size = (*attr).stack_size as ctru_sys::size_t; let priority = (*attr).priority; - let affinity = (*attr).affinity; + let ideal_processor = (*attr).ideal_processor; extern "C" fn thread_start(main: *mut libc::c_void) { unsafe { @@ -44,7 +44,7 @@ pub unsafe extern "C" fn pthread_create( main as *mut libc::c_void, stack_size, priority, - affinity, + ideal_processor, false, ); @@ -92,7 +92,7 @@ pub unsafe extern "C" fn pthread_getpriority() -> libc::c_int { struct PThreadAttr { stack_size: libc::size_t, priority: libc::c_int, - affinity: libc::c_int, + ideal_processor: libc::c_int, } impl Default for PThreadAttr { @@ -104,9 +104,9 @@ impl Default for PThreadAttr { // priority as the current thread priority: unsafe { pthread_getpriority() }, - // If no affinity is specified, spawn on the default core (determined by - // the application's Exheader) - affinity: -2, + // If no processor is specified, spawn on the default core. + // (determined by the application's Exheader) + ideal_processor: -2, } } } @@ -164,12 +164,25 @@ pub unsafe extern "C" fn pthread_attr_setschedparam( } #[no_mangle] -pub unsafe extern "C" fn pthread_attr_setaffinity( +pub unsafe extern "C" fn pthread_attr_getidealprocessor_np( + attr: *const libc::pthread_attr_t, + ideal_processor: *mut libc::c_int, +) -> libc::c_int { + let attr = attr as *mut PThreadAttr; + (*ideal_processor) = (*attr).ideal_processor; + + 0 +} + +#[no_mangle] +pub unsafe extern "C" fn pthread_attr_setidealprocessor_np( attr: *mut libc::pthread_attr_t, - affinity: libc::c_int, + ideal_processor: libc::c_int, ) -> libc::c_int { let attr = attr as *mut PThreadAttr; - (*attr).affinity = affinity; + (*attr).ideal_processor = ideal_processor; + + // TODO: we could validate the processor ID here if we wanted? 0 } From d1815bcb9b750fc1a8a86e02cf944ce801438488 Mon Sep 17 00:00:00 2001 From: AzureMarker <mark.drobnak@gmail.com> Date: Sat, 12 Feb 2022 19:38:30 -0800 Subject: [PATCH 4/6] Add pthread_self and improve thread function error handling --- src/lib.rs | 31 +++++++++++++++++++++++++++---- 1 file changed, 27 insertions(+), 4 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 4db289e..4d19f8f 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -13,6 +13,9 @@ pub fn init() {} // PTHREAD LAYER TO CALL LIBCTRU +/// The main thread's thread ID. It is "null" because libctru didn't spawn it. +const MAIN_THREAD_ID: libc::pthread_t = 0; + #[no_mangle] pub unsafe extern "C" fn pthread_create( native: *mut libc::pthread_t, @@ -39,7 +42,7 @@ pub unsafe extern "C" fn pthread_create( let main: *mut Box<dyn FnOnce() -> *mut libc::c_void> = Box::into_raw(Box::new(Box::new(move || entrypoint(value)))); - let handle = ctru_sys::threadCreate( + let thread = ctru_sys::threadCreate( Some(thread_start), main as *mut libc::c_void, stack_size, @@ -48,7 +51,7 @@ pub unsafe extern "C" fn pthread_create( false, ); - if handle.is_null() { + if thread.is_null() { // There was some error, but libctru doesn't expose the result. // We assume there was permissions issue (such as too low of a priority). // We also need to clean up the closure at this time. @@ -56,7 +59,7 @@ pub unsafe extern "C" fn pthread_create( return libc::EPERM; } - *native = handle as _; + *native = thread as _; 0 } @@ -66,7 +69,17 @@ pub unsafe extern "C" fn pthread_join( native: libc::pthread_t, _value: *mut *mut libc::c_void, ) -> libc::c_int { - ctru_sys::threadJoin(native as ctru_sys::Thread, u64::MAX); + if native == MAIN_THREAD_ID { + // This is not a valid thread to join on + return libc::EINVAL; + } + + let result = ctru_sys::threadJoin(native as ctru_sys::Thread, u64::MAX); + if ctru_sys::R_FAILED(result) { + // TODO: improve the error code by checking the result further? + return libc::EINVAL; + } + ctru_sys::threadFree(native as ctru_sys::Thread); 0 @@ -74,11 +87,21 @@ pub unsafe extern "C" fn pthread_join( #[no_mangle] pub unsafe extern "C" fn pthread_detach(thread: libc::pthread_t) -> libc::c_int { + if native == MAIN_THREAD_ID { + // This is not a valid thread to detach + return libc::EINVAL; + } + ctru_sys::threadDetach(thread as ctru_sys::Thread); 0 } +#[no_mangle] +pub unsafe extern "C" fn pthread_self() -> libc::pthread_t { + ctru_sys::threadGetCurrent() as libc::pthread_t +} + #[no_mangle] pub unsafe extern "C" fn pthread_getpriority() -> libc::c_int { let mut priority = 0; From cac16d639cd59111a718062bd3d4991f9a346197 Mon Sep 17 00:00:00 2001 From: AzureMarker <mark.drobnak@gmail.com> Date: Sat, 12 Feb 2022 21:06:27 -0800 Subject: [PATCH 5/6] Replace pthread_getpriority with more standard version (and add set fn) In the case of the main thread we use a hack since there's no nice API for getting the main thread's handle (see code). --- src/lib.rs | 117 ++++++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 112 insertions(+), 5 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 4d19f8f..7424de0 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -87,7 +87,7 @@ pub unsafe extern "C" fn pthread_join( #[no_mangle] pub unsafe extern "C" fn pthread_detach(thread: libc::pthread_t) -> libc::c_int { - if native == MAIN_THREAD_ID { + if thread == MAIN_THREAD_ID { // This is not a valid thread to detach return libc::EINVAL; } @@ -102,11 +102,110 @@ pub unsafe extern "C" fn pthread_self() -> libc::pthread_t { ctru_sys::threadGetCurrent() as libc::pthread_t } +unsafe fn get_main_thread_handle() -> Result<ctru_sys::Handle, ctru_sys::Result> { + // Unfortunately I don't know of any better way to get the main thread's + // handle, other than via svcGetThreadList and svcOpenThread. + // Experimentally, the main thread ID is always the first in the list. + let mut thread_ids = [0; 1]; + let mut thread_ids_count = 0; + let result = ctru_sys::svcGetThreadList( + &mut thread_ids_count, + thread_ids.as_mut_ptr(), + thread_ids.len() as i32, + ctru_sys::CUR_PROCESS_HANDLE, + ); + if ctru_sys::R_FAILED(result) { + return Err(result); + } + + // Get the main thread's handle + let main_thread_id = thread_ids[0]; + let mut main_thread_handle = 0; + let result = ctru_sys::svcOpenThread( + &mut main_thread_handle, + ctru_sys::CUR_PROCESS_HANDLE, + main_thread_id, + ); + if ctru_sys::R_FAILED(result) { + return Err(result); + } + + Ok(main_thread_handle) +} + +unsafe fn get_thread_handle(native: libc::pthread_t) -> Result<ctru_sys::Handle, ctru_sys::Result> { + if native == MAIN_THREAD_ID { + get_main_thread_handle() + } else { + Ok(ctru_sys::threadGetHandle(native as ctru_sys::Thread)) + } +} + #[no_mangle] -pub unsafe extern "C" fn pthread_getpriority() -> libc::c_int { +pub unsafe extern "C" fn pthread_getschedparam( + native: libc::pthread_t, + policy: *mut libc::c_int, + param: *mut libc::sched_param, +) -> libc::c_int { + let handle = match get_thread_handle(native) { + Ok(handle) => handle, + Err(_) => return libc::ESRCH, + }; + + if handle == u32::MAX { + // The thread has already finished + return libc::ESRCH; + } + let mut priority = 0; - ctru_sys::svcGetThreadPriority(&mut priority, ctru_sys::CUR_THREAD_HANDLE); - priority + let result = ctru_sys::svcGetThreadPriority(&mut priority, handle); + if ctru_sys::R_FAILED(result) { + // Some error occurred. This is the only error defined for this + // function, so return it. Maybe the thread exited while this function + // was exiting? + return libc::ESRCH; + } + + (*param).sched_priority = priority; + + // SCHED_FIFO is closest to how the cooperative app core works, while + // SCHED_RR is closest to how the preemptive sys core works. + // However, we don't have an API to get the current processor ID of a chosen + // thread (only the current), so we just always return SCHED_FIFO. + (*policy) = libc::SCHED_FIFO; + + 0 +} + +#[no_mangle] +pub unsafe extern "C" fn pthread_setschedparam( + native: libc::pthread_t, + policy: libc::c_int, + param: *const libc::sched_param, +) -> libc::c_int { + if policy != libc::SCHED_FIFO { + // We only accept SCHED_FIFO. See the note in pthread_getschedparam. + return libc::EINVAL; + } + + let handle = match get_thread_handle(native) { + Ok(handle) => handle, + Err(_) => return libc::EINVAL, + }; + + if handle == u32::MAX { + // The thread has already finished + return libc::ESRCH; + } + + let result = ctru_sys::svcSetThreadPriority(handle, (*param).sched_priority); + if ctru_sys::R_FAILED(result) { + // Probably the priority is out of the permissible bounds + // TODO: improve the error code by checking the result further? + return libc::EPERM; + } + + 0 } /// Internal struct for storing pthread attribute data @@ -120,12 +219,20 @@ struct PThreadAttr { impl Default for PThreadAttr { fn default() -> Self { + let thread_id = unsafe { libc::pthread_self() }; + let mut policy = 0; + let mut sched_param = libc::sched_param { sched_priority: 0 }; + + unsafe { libc::pthread_getschedparam(thread_id, &mut policy, &mut sched_param) }; + + let priority = sched_param.sched_priority; + PThreadAttr { stack_size: libc::PTHREAD_STACK_MIN, // If no priority value is specified, spawn with the same // priority as the current thread - priority: unsafe { pthread_getpriority() }, + priority, // If no processor is specified, spawn on the default core. // (determined by the application's Exheader) From 88d12c91ad8ae61cbcb14a2c58334a213d392374 Mon Sep 17 00:00:00 2001 From: AzureMarker <mark.drobnak@gmail.com> Date: Sun, 13 Feb 2022 18:43:00 -0800 Subject: [PATCH 6/6] Implement pthread_getprocessorid_np --- src/lib.rs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/lib.rs b/src/lib.rs index 7424de0..2741e38 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -208,6 +208,11 @@ pub unsafe extern "C" fn pthread_setschedparam( 0 } +#[no_mangle] +pub unsafe extern "C" fn pthread_getprocessorid_np() -> libc::c_int { + ctru_sys::svcGetProcessorID() +} + /// 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.