Skip to content
Open
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions library/std/src/os/horizon/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,4 @@

pub mod fs;
pub(crate) mod raw;
pub mod thread;
88 changes: 88 additions & 0 deletions library/std/src/os/horizon/thread.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
//! Nintendo 3DS-specific extensions to primitives in the [`std::thread`] module.
//!
//! All 3DS models have at least two CPU cores available to spawn threads on:
//! The application core (appcore) and the system core (syscore). The New 3DS
//! has an additional two cores, the first of which can also run user-created
//! threads.
//!
//! Threads spawned on the appcore are cooperative rather than preemptive. This
//! means that threads must explicitly yield control to other threads (whether
//! via synchronization primitives or explicit calls to `yield_now`) when they
//! are not actively performing work. Failure to do so may result in control
//! flow being stuck in an inactive thread while the other threads are powerless
//! to continue their work.
//!
//! However, it is possible to spawn one fully preemptive thread on the syscore
//! by using a service call to reserve a slice of time for a thread to run.
//! Attempting to run more than one thread at a time on the syscore will result
//! in an error.
//!
//! [`std::thread`]: crate::thread
#![unstable(feature = "horizon_thread_ext", issue = "none")]
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I looked at creating a tracking issue for this, but held off for now due to this note in the template:

If the new feature is small, it may be fine to skip the RFC process. In that
case, you can use use issue = "none" in your initial implementation PR. The
reviewer will ask you to open a tracking issue if they agree your feature can be
added without an RFC.

So once someone comments that this is an acceptable feature to skip an RFC for, I can create the tracking issue.


/// Extensions on [`std::thread::Builder`] for the Nintendo 3DS.
///
/// [`std::thread::Builder`]: crate::thread::Builder
pub trait BuilderExt: Sized {
/// Sets the priority level for the new thread.
///
/// Low values gives the thread higher priority. For userland apps, this has
/// to be within the range of 0x18 to 0x3F inclusive. The main thread
/// usually has a priority of 0x30, but not always.
fn priority(self, priority: i32) -> Self;

/// Sets the ID of the processor the thread should be run on. Threads on the
/// 3DS are only preemptive if they are on the system core. Otherwise they
/// are cooperative (must yield to let other threads run).
///
/// Processor IDs are labeled starting from 0. On Old3DS it must be <2, and
/// on New3DS it must be <4. Pass -1 to execute the thread on all CPUs and
/// -2 to execute the thread on the default CPU (set in the application's
/// Exheader).
///
/// * Processor #0 is the application core. It is always possible to create
/// a thread on this core.
/// * Processor #1 is the system core. If the CPU time limit is set, it is
/// possible to create a single thread on this core.
/// * Processor #2 is New3DS exclusive. Normal applications can create
/// threads on this core only if the built application has proper external setup.
/// * Processor #3 is New3DS exclusive. Normal applications cannot create
/// threads on this core.
fn processor_id(self, processor_id: i32) -> Self;
}

impl BuilderExt for crate::thread::Builder {
fn priority(mut self, priority: i32) -> Self {
self.native_options.priority = Some(priority);
self
}

fn processor_id(mut self, processor_id: i32) -> Self {
self.native_options.processor_id = Some(processor_id);
self
}
}

/// Get the current thread's priority level. Lower values correspond to higher
/// priority levels.
pub fn current_priority() -> i32 {
let thread_id = unsafe { libc::pthread_self() };
let mut policy = 0;
let mut sched_param = libc::sched_param { sched_priority: 0 };

let result = unsafe { libc::pthread_getschedparam(thread_id, &mut policy, &mut sched_param) };
assert_eq!(result, 0);

sched_param.sched_priority
}

/// Get the current thread's processor ID.
///
/// * Processor #0 is the application core.
/// * Processor #1 is the system core.
/// * Processor #2 is New3DS exclusive.
/// * Processor #3 is New3DS exclusive.
pub fn current_processor() -> i32 {
unsafe { libc::pthread_getprocessorid_np() }
}
9 changes: 8 additions & 1 deletion library/std/src/sys/hermit/thread.rs
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,11 @@ impl Thread {
}
}

pub unsafe fn new(stack: usize, p: Box<dyn FnOnce()>) -> io::Result<Thread> {
pub unsafe fn new(
stack: usize,
p: Box<dyn FnOnce()>,
_native_options: BuilderOptions,
) -> io::Result<Thread> {
Thread::new_with_coreid(stack, p, -1 /* = no specific core */)
}

Expand Down Expand Up @@ -97,6 +101,9 @@ impl Thread {
}
}

#[derive(Debug)]
pub struct BuilderOptions;

pub fn available_parallelism() -> io::Result<NonZeroUsize> {
unsupported()
}
Expand Down
9 changes: 8 additions & 1 deletion library/std/src/sys/itron/thread.rs
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,11 @@ impl Thread {
/// # Safety
///
/// See `thread::Builder::spawn_unchecked` for safety requirements.
pub unsafe fn new(stack: usize, p: Box<dyn FnOnce()>) -> io::Result<Thread> {
pub unsafe fn new(
stack: usize,
p: Box<dyn FnOnce()>,
_native_options: BuilderOptions,
) -> io::Result<Thread> {
let inner = Box::new(ThreadInner {
start: UnsafeCell::new(ManuallyDrop::new(p)),
lifecycle: AtomicUsize::new(LIFECYCLE_INIT),
Expand Down Expand Up @@ -288,6 +292,9 @@ impl Drop for Thread {
}
}

#[derive(Debug)]
pub struct BuilderOptions;

pub mod guard {
pub type Guard = !;
pub unsafe fn current() -> Option<Guard> {
Expand Down
9 changes: 8 additions & 1 deletion library/std/src/sys/sgx/thread.rs
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,11 @@ pub mod wait_notify {

impl Thread {
// unsafe: see thread::Builder::spawn_unchecked for safety requirements
pub unsafe fn new(_stack: usize, p: Box<dyn FnOnce()>) -> io::Result<Thread> {
pub unsafe fn new(
_stack: usize,
p: Box<dyn FnOnce()>,
_native_options: BuilderOptions,
) -> io::Result<Thread> {
let mut queue_lock = task_queue::lock();
unsafe { usercalls::launch_thread()? };
let (task, handle) = task_queue::Task::new(p);
Expand Down Expand Up @@ -137,6 +141,9 @@ impl Thread {
}
}

#[derive(Debug)]
pub struct BuilderOptions;

pub fn available_parallelism() -> io::Result<NonZeroUsize> {
unsupported()
}
Expand Down
47 changes: 45 additions & 2 deletions library/std/src/sys/unix/thread.rs
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,11 @@ unsafe impl Sync for Thread {}

impl Thread {
// unsafe: see thread::Builder::spawn_unchecked for safety requirements
pub unsafe fn new(stack: usize, p: Box<dyn FnOnce()>) -> io::Result<Thread> {
pub unsafe fn new(
stack: usize,
p: Box<dyn FnOnce()>,
#[allow(unused)] native_options: BuilderOptions,
) -> io::Result<Thread> {
let p = Box::into_raw(box p);
let mut native: libc::pthread_t = mem::zeroed();
let mut attr: libc::pthread_attr_t = mem::zeroed();
Expand Down Expand Up @@ -84,6 +88,23 @@ impl Thread {
};
}

#[cfg(target_os = "horizon")]
{
// If no priority value is specified, spawn with the same priority
// as the parent thread.
let priority = native_options
.priority
.unwrap_or_else(crate::os::horizon::thread::current_priority);
let sched_param = libc::sched_param { sched_priority: priority };

// If no processor is specified, spawn on the default core.
// (determined by the application's Exheader)
let processor_id = native_options.processor_id.unwrap_or(-2);

assert_eq!(libc::pthread_attr_setschedparam(&mut attr, &sched_param), 0);
assert_eq!(libc::pthread_attr_setprocessorid_np(&mut attr, processor_id), 0);
}

let ret = libc::pthread_create(&mut native, &attr, thread_start, p as *mut _);
// Note: if the thread creation fails and this assert fails, then p will
// be leaked. However, an alternative design could cause double-free
Expand Down Expand Up @@ -200,7 +221,8 @@ impl Thread {
target_os = "l4re",
target_os = "emscripten",
target_os = "redox",
target_os = "vxworks"
target_os = "vxworks",
target_os = "horizon"
))]
pub fn set_name(_name: &CStr) {
// Newlib, Emscripten, and VxWorks have no way to set a thread name.
Expand Down Expand Up @@ -271,6 +293,27 @@ impl Drop for Thread {
}
}

#[derive(Debug)]
pub struct BuilderOptions {
/// The spawned thread's priority value
#[cfg(target_os = "horizon")]
pub(crate) priority: Option<i32>,
/// The processor to spawn the thread on. See [`os::horizon::thread::BuilderExt`].
#[cfg(target_os = "horizon")]
pub(crate) processor_id: Option<i32>,
}

impl Default for BuilderOptions {
fn default() -> Self {
BuilderOptions {
#[cfg(target_os = "horizon")]
priority: None,
#[cfg(target_os = "horizon")]
processor_id: None,
}
}
}

pub fn available_parallelism() -> io::Result<NonZeroUsize> {
cfg_if::cfg_if! {
if #[cfg(any(
Expand Down
9 changes: 8 additions & 1 deletion library/std/src/sys/unsupported/thread.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,11 @@ pub const DEFAULT_MIN_STACK_SIZE: usize = 4096;

impl Thread {
// unsafe: see thread::Builder::spawn_unchecked for safety requirements
pub unsafe fn new(_stack: usize, _p: Box<dyn FnOnce()>) -> io::Result<Thread> {
pub unsafe fn new(
_stack: usize,
_p: Box<dyn FnOnce()>,
_native_options: BuilderOptions,
) -> io::Result<Thread> {
unsupported()
}

Expand All @@ -31,6 +35,9 @@ impl Thread {
}
}

#[derive(Debug)]
pub struct BuilderOptions;

pub fn available_parallelism() -> io::Result<NonZeroUsize> {
unsupported()
}
Expand Down
9 changes: 8 additions & 1 deletion library/std/src/sys/wasi/thread.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,11 @@ pub const DEFAULT_MIN_STACK_SIZE: usize = 4096;

impl Thread {
// unsafe: see thread::Builder::spawn_unchecked for safety requirements
pub unsafe fn new(_stack: usize, _p: Box<dyn FnOnce()>) -> io::Result<Thread> {
pub unsafe fn new(
_stack: usize,
_p: Box<dyn FnOnce()>,
_native_options: BuilderOptions,
) -> io::Result<Thread> {
unsupported()
}

Expand Down Expand Up @@ -66,6 +70,9 @@ impl Thread {
}
}

#[derive(Debug)]
pub struct BuilderOptions;

pub fn available_parallelism() -> io::Result<NonZeroUsize> {
unsupported()
}
Expand Down
9 changes: 8 additions & 1 deletion library/std/src/sys/wasm/atomics/thread.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,11 @@ pub const DEFAULT_MIN_STACK_SIZE: usize = 4096;

impl Thread {
// unsafe: see thread::Builder::spawn_unchecked for safety requirements
pub unsafe fn new(_stack: usize, _p: Box<dyn FnOnce()>) -> io::Result<Thread> {
pub unsafe fn new(
_stack: usize,
_p: Box<dyn FnOnce()>,
_native_options: BuilderOptions,
) -> io::Result<Thread> {
unsupported()
}

Expand Down Expand Up @@ -40,6 +44,9 @@ impl Thread {
pub fn join(self) {}
}

#[derive(Debug)]
pub struct BuilderOptions;

pub fn available_parallelism() -> io::Result<NonZeroUsize> {
unsupported()
}
Expand Down
9 changes: 8 additions & 1 deletion library/std/src/sys/windows/thread.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,11 @@ pub struct Thread {

impl Thread {
// unsafe: see thread::Builder::spawn_unchecked for safety requirements
pub unsafe fn new(stack: usize, p: Box<dyn FnOnce()>) -> io::Result<Thread> {
pub unsafe fn new(
stack: usize,
p: Box<dyn FnOnce()>,
_native_options: BuilderOptions,
) -> io::Result<Thread> {
let p = Box::into_raw(box p);

// FIXME On UNIX, we guard against stack sizes that are too small but
Expand Down Expand Up @@ -98,6 +102,9 @@ impl Thread {
}
}

#[derive(Debug)]
pub struct BuilderOptions;

pub fn available_parallelism() -> io::Result<NonZeroUsize> {
let res = unsafe {
let mut sysinfo: c::SYSTEM_INFO = crate::mem::zeroed();
Expand Down
12 changes: 7 additions & 5 deletions library/std/src/thread/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -267,6 +267,9 @@ pub struct Builder {
name: Option<String>,
// The size of the stack for the spawned thread in bytes
stack_size: Option<usize>,
// Other OS-specific fields. These can be set via extension traits, usually
// found in std::os.
pub(crate) native_options: imp::BuilderOptions,
}

impl Builder {
Expand All @@ -290,7 +293,7 @@ impl Builder {
/// ```
#[stable(feature = "rust1", since = "1.0.0")]
pub fn new() -> Builder {
Builder { name: None, stack_size: None }
Builder { name: None, stack_size: None, native_options: imp::BuilderOptions::default() }
}

/// Names the thread-to-be. Currently the name is used for identification
Expand Down Expand Up @@ -470,11 +473,9 @@ impl Builder {
T: Send + 'a,
'scope: 'a,
{
let Builder { name, stack_size } = self;
let stack_size = self.stack_size.unwrap_or_else(thread::min_stack);

let stack_size = stack_size.unwrap_or_else(thread::min_stack);

let my_thread = Thread::new(name.map(|name| {
let my_thread = Thread::new(self.name.map(|name| {
CString::new(name).expect("thread name may not contain interior null bytes")
}));
let their_thread = my_thread.clone();
Expand Down Expand Up @@ -531,6 +532,7 @@ impl Builder {
mem::transmute::<Box<dyn FnOnce() + 'a>, Box<dyn FnOnce() + 'static>>(
Box::new(main),
),
self.native_options,
)?
},
thread: my_thread,
Expand Down