Skip to content

Commit a89957a

Browse files
authored
Merge pull request #10 from AzureMarker/feature/std-threads
Implement std threads support
2 parents 80b3df4 + 2b5a7c1 commit a89957a

11 files changed

Lines changed: 201 additions & 14 deletions

File tree

library/std/src/os/horizon/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,3 +4,4 @@
44

55
pub mod fs;
66
pub mod raw;
7+
pub mod thread;
Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
//! Nintendo 3DS-specific extensions to primitives in the [`std::thread`] module.
2+
//!
3+
//! All 3DS models have at least two CPU cores available to spawn threads on:
4+
//! The application core (appcore) and the system core (syscore). The New 3DS
5+
//! has an additional two cores, the first of which can also run user-created
6+
//! threads.
7+
//!
8+
//! Threads spawned on the appcore are cooperative rather than preemptive. This
9+
//! means that threads must explicitly yield control to other threads (whether
10+
//! via synchronization primitives or explicit calls to `yield_now`) when they
11+
//! are not actively performing work. Failure to do so may result in control
12+
//! flow being stuck in an inactive thread while the other threads are powerless
13+
//! to continue their work.
14+
//!
15+
//! However, it is possible to spawn one fully preemptive thread on the syscore
16+
//! by using a service call to reserve a slice of time for a thread to run.
17+
//! Attempting to run more than one thread at a time on the syscore will result
18+
//! in an error.
19+
//!
20+
//! [`std::thread`]: crate::thread
21+
22+
#![unstable(feature = "horizon_thread_ext", issue = "none")]
23+
24+
/// Extensions on [`std::thread::Builder`] for the Nintendo 3DS.
25+
///
26+
/// [`std::thread::Builder`]: crate::thread::Builder
27+
pub trait BuilderExt: Sized {
28+
/// Sets the priority level for the new thread.
29+
///
30+
/// Low values gives the thread higher priority. For userland apps, this has
31+
/// to be within the range of 0x18 to 0x3F inclusive. The main thread
32+
/// usually has a priority of 0x30, but not always.
33+
fn priority(mut self, priority: i32) -> Self;
34+
35+
/// Sets the ID of the processor the thread should be run on. Threads on the
36+
/// 3DS are only preemptive if they are on the system core. Otherwise they
37+
/// are cooperative (must yield to let other threads run).
38+
///
39+
/// Processor IDs are labeled starting from 0. On Old3DS it must be <2, and
40+
/// on New3DS it must be <4. Pass -1 to execute the thread on all CPUs and
41+
/// -2 to execute the thread on the default CPU (set in the application's
42+
/// Exheader).
43+
///
44+
/// * Processor #0 is the application core. It is always possible to create
45+
/// a thread on this core.
46+
/// * Processor #1 is the system core. If the CPU time limit is set, it is
47+
/// possible to create a single thread on this core.
48+
/// * Processor #2 is New3DS exclusive. Normal applications can create
49+
/// threads on this core only if the built application has proper external setup.
50+
/// * Processor #3 is New3DS exclusive. Normal applications cannot create
51+
/// threads on this core.
52+
fn processor_id(mut self, processor_id: i32) -> Self;
53+
}
54+
55+
impl BuilderExt for crate::thread::Builder {
56+
fn priority(mut self, priority: i32) -> Self {
57+
self.native_options.priority = Some(priority);
58+
self
59+
}
60+
61+
fn processor_id(mut self, processor_id: i32) -> Self {
62+
self.native_options.processor_id = Some(processor_id);
63+
self
64+
}
65+
}
66+
67+
/// Get the current thread's priority level. Lower values correspond to higher
68+
/// priority levels.
69+
pub fn current_priority() -> i32 {
70+
let thread_id = unsafe { libc::pthread_self() };
71+
let mut policy = 0;
72+
let mut sched_param = libc::sched_param { sched_priority: 0 };
73+
74+
let result = unsafe { libc::pthread_getschedparam(thread_id, &mut policy, &mut sched_param) };
75+
assert_eq!(result, 0);
76+
77+
sched_param.sched_priority
78+
}
79+
80+
/// Get the current thread's processor ID.
81+
///
82+
/// * Processor #0 is the application core.
83+
/// * Processor #1 is the system core.
84+
/// * Processor #2 is New3DS exclusive.
85+
/// * Processor #3 is New3DS exclusive.
86+
pub fn current_processor() -> i32 {
87+
unsafe { libc::pthread_getprocessorid_np() }
88+
}

library/std/src/sys/hermit/thread.rs

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,11 @@ impl Thread {
5555
}
5656
}
5757

58-
pub unsafe fn new(stack: usize, p: Box<dyn FnOnce()>) -> io::Result<Thread> {
58+
pub unsafe fn new(
59+
stack: usize,
60+
p: Box<dyn FnOnce()>,
61+
_native_options: BuilderOptions,
62+
) -> io::Result<Thread> {
5963
Thread::new_with_coreid(stack, p, -1 /* = no specific core */)
6064
}
6165

@@ -97,6 +101,9 @@ impl Thread {
97101
}
98102
}
99103

104+
#[derive(Debug)]
105+
pub struct BuilderOptions;
106+
100107
pub fn available_parallelism() -> io::Result<NonZeroUsize> {
101108
unsupported()
102109
}

library/std/src/sys/itron/thread.rs

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,15 @@ impl Thread {
8484
/// # Safety
8585
///
8686
/// See `thread::Builder::spawn_unchecked` for safety requirements.
87-
pub unsafe fn new(stack: usize, p: Box<dyn FnOnce()>) -> io::Result<Thread> {
87+
pub unsafe fn new(
88+
stack: usize,
89+
p: Box<dyn FnOnce()>,
90+
_native_options: BuilderOptions,
91+
) -> io::Result<Thread> {
92+
// Inherit the current task's priority
93+
let current_task = task::try_current_task_id().map_err(|e| e.as_io_error())?;
94+
let priority = task::try_task_priority(current_task).map_err(|e| e.as_io_error())?;
95+
8896
let inner = Box::new(ThreadInner {
8997
start: UnsafeCell::new(ManuallyDrop::new(p)),
9098
lifecycle: AtomicUsize::new(LIFECYCLE_INIT),
@@ -289,6 +297,9 @@ impl Drop for Thread {
289297
}
290298
}
291299

300+
#[derive(Debug)]
301+
pub struct BuilderOptions;
302+
292303
pub mod guard {
293304
pub type Guard = !;
294305
pub unsafe fn current() -> Option<Guard> {

library/std/src/sys/sgx/thread.rs

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -104,7 +104,11 @@ pub mod wait_notify {
104104

105105
impl Thread {
106106
// unsafe: see thread::Builder::spawn_unchecked for safety requirements
107-
pub unsafe fn new(_stack: usize, p: Box<dyn FnOnce()>) -> io::Result<Thread> {
107+
pub unsafe fn new(
108+
_stack: usize,
109+
p: Box<dyn FnOnce()>,
110+
_native_options: BuilderOptions,
111+
) -> io::Result<Thread> {
108112
let mut queue_lock = task_queue::lock();
109113
unsafe { usercalls::launch_thread()? };
110114
let (task, handle) = task_queue::Task::new(p);
@@ -137,6 +141,9 @@ impl Thread {
137141
}
138142
}
139143

144+
#[derive(Debug)]
145+
pub struct BuilderOptions;
146+
140147
pub fn available_parallelism() -> io::Result<NonZeroUsize> {
141148
unsupported()
142149
}

library/std/src/sys/unix/thread.rs

Lines changed: 45 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,11 @@ unsafe impl Sync for Thread {}
4848

4949
impl Thread {
5050
// unsafe: see thread::Builder::spawn_unchecked for safety requirements
51-
pub unsafe fn new(stack: usize, p: Box<dyn FnOnce()>) -> io::Result<Thread> {
51+
pub unsafe fn new(
52+
stack: usize,
53+
p: Box<dyn FnOnce()>,
54+
#[allow(unused)] native_options: BuilderOptions,
55+
) -> io::Result<Thread> {
5256
let p = Box::into_raw(box p);
5357
let mut native: libc::pthread_t = mem::zeroed();
5458
let mut attr: libc::pthread_attr_t = mem::zeroed();
@@ -84,6 +88,23 @@ impl Thread {
8488
};
8589
}
8690

91+
#[cfg(target_os = "horizon")]
92+
{
93+
// If no priority value is specified, spawn with the same priority
94+
// as the parent thread.
95+
let priority = native_options
96+
.priority
97+
.unwrap_or_else(crate::os::horizon::thread::current_priority);
98+
let sched_param = libc::sched_param { sched_priority: priority };
99+
100+
// If no processor is specified, spawn on the default core.
101+
// (determined by the application's Exheader)
102+
let processor_id = native_options.processor_id.unwrap_or(-2);
103+
104+
assert_eq!(libc::pthread_attr_setschedparam(&mut attr, &sched_param), 0);
105+
assert_eq!(libc::pthread_attr_setprocessorid_np(&mut attr, processor_id), 0);
106+
}
107+
87108
let ret = libc::pthread_create(&mut native, &attr, thread_start, p as *mut _);
88109
// Note: if the thread creation fails and this assert fails, then p will
89110
// be leaked. However, an alternative design could cause double-free
@@ -194,7 +215,8 @@ impl Thread {
194215
target_os = "l4re",
195216
target_os = "emscripten",
196217
target_os = "redox",
197-
target_os = "vxworks"
218+
target_os = "vxworks",
219+
target_os = "horizon"
198220
))]
199221
pub fn set_name(_name: &CStr) {
200222
// Newlib, Emscripten, and VxWorks have no way to set a thread name.
@@ -265,6 +287,27 @@ impl Drop for Thread {
265287
}
266288
}
267289

290+
#[derive(Debug)]
291+
pub struct BuilderOptions {
292+
/// The spawned thread's priority value
293+
#[cfg(target_os = "horizon")]
294+
pub(crate) priority: Option<i32>,
295+
/// The processor to spawn the thread on. See [`os::horizon::thread::BuilderExt`].
296+
#[cfg(target_os = "horizon")]
297+
pub(crate) processor_id: Option<i32>,
298+
}
299+
300+
impl Default for BuilderOptions {
301+
fn default() -> Self {
302+
BuilderOptions {
303+
#[cfg(target_os = "horizon")]
304+
priority: None,
305+
#[cfg(target_os = "horizon")]
306+
processor_id: None,
307+
}
308+
}
309+
}
310+
268311
pub fn available_parallelism() -> io::Result<NonZeroUsize> {
269312
cfg_if::cfg_if! {
270313
if #[cfg(any(

library/std/src/sys/unsupported/thread.rs

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,11 @@ pub const DEFAULT_MIN_STACK_SIZE: usize = 4096;
1010

1111
impl Thread {
1212
// unsafe: see thread::Builder::spawn_unchecked for safety requirements
13-
pub unsafe fn new(_stack: usize, _p: Box<dyn FnOnce()>) -> io::Result<Thread> {
13+
pub unsafe fn new(
14+
_stack: usize,
15+
_p: Box<dyn FnOnce()>,
16+
_native_options: BuilderOptions,
17+
) -> io::Result<Thread> {
1418
unsupported()
1519
}
1620

@@ -31,6 +35,9 @@ impl Thread {
3135
}
3236
}
3337

38+
#[derive(Debug)]
39+
pub struct BuilderOptions;
40+
3441
pub fn available_parallelism() -> io::Result<NonZeroUsize> {
3542
unsupported()
3643
}

library/std/src/sys/wasi/thread.rs

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,11 @@ pub const DEFAULT_MIN_STACK_SIZE: usize = 4096;
1313

1414
impl Thread {
1515
// unsafe: see thread::Builder::spawn_unchecked for safety requirements
16-
pub unsafe fn new(_stack: usize, _p: Box<dyn FnOnce()>) -> io::Result<Thread> {
16+
pub unsafe fn new(
17+
_stack: usize,
18+
_p: Box<dyn FnOnce()>,
19+
_native_options: BuilderOptions,
20+
) -> io::Result<Thread> {
1721
unsupported()
1822
}
1923

@@ -66,6 +70,9 @@ impl Thread {
6670
}
6771
}
6872

73+
#[derive(Debug)]
74+
pub struct BuilderOptions;
75+
6976
pub fn available_parallelism() -> io::Result<NonZeroUsize> {
7077
unsupported()
7178
}

library/std/src/sys/wasm/atomics/thread.rs

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,11 @@ pub const DEFAULT_MIN_STACK_SIZE: usize = 4096;
1010

1111
impl Thread {
1212
// unsafe: see thread::Builder::spawn_unchecked for safety requirements
13-
pub unsafe fn new(_stack: usize, _p: Box<dyn FnOnce()>) -> io::Result<Thread> {
13+
pub unsafe fn new(
14+
_stack: usize,
15+
_p: Box<dyn FnOnce()>,
16+
_native_options: BuilderOptions,
17+
) -> io::Result<Thread> {
1418
unsupported()
1519
}
1620

@@ -40,6 +44,9 @@ impl Thread {
4044
pub fn join(self) {}
4145
}
4246

47+
#[derive(Debug)]
48+
pub struct BuilderOptions;
49+
4350
pub fn available_parallelism() -> io::Result<NonZeroUsize> {
4451
unsupported()
4552
}

library/std/src/sys/windows/thread.rs

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,11 @@ pub struct Thread {
2020

2121
impl Thread {
2222
// unsafe: see thread::Builder::spawn_unchecked for safety requirements
23-
pub unsafe fn new(stack: usize, p: Box<dyn FnOnce()>) -> io::Result<Thread> {
23+
pub unsafe fn new(
24+
stack: usize,
25+
p: Box<dyn FnOnce()>,
26+
_native_options: BuilderOptions,
27+
) -> io::Result<Thread> {
2428
let p = Box::into_raw(box p);
2529

2630
// FIXME On UNIX, we guard against stack sizes that are too small but
@@ -100,6 +104,9 @@ impl Thread {
100104
}
101105
}
102106

107+
#[derive(Debug)]
108+
pub struct BuilderOptions;
109+
103110
pub fn available_parallelism() -> io::Result<NonZeroUsize> {
104111
let res = unsafe {
105112
let mut sysinfo: c::SYSTEM_INFO = crate::mem::zeroed();

0 commit comments

Comments
 (0)