aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--ctru-rs/src/lib.rs4
-rw-r--r--ctru-rs/src/thread.rs1139
2 files changed, 1143 insertions, 0 deletions
diff --git a/ctru-rs/src/lib.rs b/ctru-rs/src/lib.rs
index 57635f6..56ac370 100644
--- a/ctru-rs/src/lib.rs
+++ b/ctru-rs/src/lib.rs
@@ -1,6 +1,9 @@
#![crate_type = "rlib"]
#![crate_name = "ctru"]
+#![feature(fnbox)]
+#![feature(thread_local_state)]
+
#[macro_use]
extern crate bitflags;
extern crate libc;
@@ -14,6 +17,7 @@ pub mod srv;
pub mod gfx;
pub mod services;
pub mod sdmc;
+pub mod thread;
pub use error::{Result, Error};
diff --git a/ctru-rs/src/thread.rs b/ctru-rs/src/thread.rs
new file mode 100644
index 0000000..ee7e819
--- /dev/null
+++ b/ctru-rs/src/thread.rs
@@ -0,0 +1,1139 @@
+// Copyright 2014 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+//! 3DS-specific threading API
+//!
+//! While it is possible to create threads on the 3DS using functions found in
+//! `std::thread`, the standard API does not expose the ability to set a thread's
+//! priority level and to pin a thread to a specific CPU core. This module exists
+//! to address those and other shortcomings.
+//!
+//! 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 `apt::set_app_cpu_time_limit` 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.
+
+use std::any::Any;
+use std::cell::UnsafeCell;
+use std::ffi::{CStr, CString};
+use std::fmt;
+use std::io;
+use std::panic;
+use std::str;
+use std::sync::{Arc, Condvar, Mutex};
+use std::sync::atomic::AtomicUsize;
+use std::sync::atomic::Ordering::SeqCst;
+use std::thread as std_thread;
+use std::time::Duration;
+
+use libctru::{svcGetProcessorID, svcGetThreadId, svcGetThreadPriority};
+
+////////////////////////////////////////////////////////////////////////////////
+// Builder
+////////////////////////////////////////////////////////////////////////////////
+
+/// Thread factory, which can be used in order to configure the properties of
+/// a new thread.
+#[derive(Debug)]
+pub struct Builder {
+ // A name for the thread-to-be, for identification in panic messages
+ name: Option<String>,
+ // The size of the stack for the spawned thread in bytes
+ stack_size: Option<usize>,
+ // The spawned thread's priority value
+ priority: Option<i32>,
+ // The spawned thread's CPU affinity value
+ affinity: Option<i32>,
+}
+
+impl Builder {
+ /// Generates the base configuration for spawning a thread, from which
+ /// configuration methods can be chained.
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// use ctru::thread;
+ ///
+ /// let builder = thread::Builder::new()
+ /// .name("foo".into())
+ /// .stack_size(10);
+ ///
+ /// let handler = builder.spawn(|| {
+ /// // thread code
+ /// }).unwrap();
+ ///
+ /// handler.join().unwrap();
+ /// ```
+ pub fn new() -> Builder {
+ Builder {
+ name: None,
+ stack_size: None,
+ priority: None,
+ affinity: None,
+ }
+ }
+
+ /// Names the thread-to-be. Currently the name is used for identification
+ /// only in panic messages.
+ ///
+ /// The name must not contain null bytes (`\0`).
+ ///
+ /// For more information about named threads, see
+ /// [this module-level documentation][naming-threads].
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// use ctru::thread;
+ ///
+ /// let builder = thread::Builder::new()
+ /// .name("foo".into());
+ ///
+ /// let handler = builder.spawn(|| {
+ /// assert_eq!(thread::current().name(), Some("foo"))
+ /// }).unwrap();
+ ///
+ /// handler.join().unwrap();
+ /// ```
+ ///
+ /// [naming-threads]: ./index.html#naming-threads
+ pub fn name(mut self, name: String) -> Builder {
+ self.name = Some(name);
+ self
+ }
+
+ /// Sets the size of the stack (in bytes) for the new thread.
+ ///
+ /// The actual stack size may be greater than this value if
+ /// the platform specifies minimal stack size.
+ ///
+ /// For more information about the stack size for threads, see
+ /// [this module-level documentation][stack-size].
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// use ctru::thread;
+ ///
+ /// let builder = thread::Builder::new().stack_size(32 * 1024);
+ /// ```
+ ///
+ /// [stack-size]: ./index.html#stack-size
+ pub fn stack_size(mut self, size: usize) -> Builder {
+ self.stack_size = Some(size);
+ self
+ }
+
+ /// 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.
+ pub fn priority(mut self, priority: i32) -> Builder {
+ self.priority = Some(priority);
+ self
+ }
+
+ /// Sets the ID of the processor the thread should be ran on.
+ ///
+ /// 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 APT_SetAppCpuTimeLimit is used, it is possible
+ /// to create a single thread on this core.
+ /// *Processor #2 is New3DS exclusive. Normal applications can create threads on
+ /// this core if the exheader kernel flags bitmask has 0x2000 set.
+ /// *Processor #3 is New3DS exclusive. Normal applications cannot create threads
+ /// on this core.
+ ///
+ /// Processes in the BASE memory region can always create threads on
+ /// processors #2 and #3.
+ pub fn affinity(mut self, affinity: i32) -> Builder {
+ self.affinity = Some(affinity);
+ self
+ }
+
+ /// Spawns a new thread by taking ownership of the `Builder`, and returns an
+ /// [`io::Result`] to its [`JoinHandle`].
+ ///
+ /// The spawned thread may outlive the caller (unless the caller thread
+ /// is the main thread; the whole process is terminated when the main
+ /// thread finishes). The join handle can be used to block on
+ /// termination of the child thread, including recovering its panics.
+ ///
+ /// For a more complete documentation see [`thread::spawn`][`spawn`].
+ ///
+ /// # Errors
+ ///
+ /// Unlike the [`spawn`] free function, this method yields an
+ /// [`io::Result`] to capture any failure to create the thread at
+ /// the OS level.
+ ///
+ /// [`spawn`]: ../../std/thread/fn.spawn.html
+ /// [`io::Result`]: ../../std/io/type.Result.html
+ /// [`JoinHandle`]: ../../std/thread/struct.JoinHandle.html
+ ///
+ /// # Panics
+ ///
+ /// Panics if a thread name was set and it contained null bytes.
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// use ctru::thread;
+ ///
+ /// let builder = thread::Builder::new();
+ ///
+ /// let handler = builder.spawn(|| {
+ /// // thread code
+ /// }).unwrap();
+ ///
+ /// handler.join().unwrap();
+ /// ```
+ pub fn spawn<F, T>(self, f: F) -> io::Result<JoinHandle<T>>
+ where
+ F: FnOnce() -> T,
+ F: Send + 'static,
+ T: Send + 'static,
+ {
+ let Builder {
+ name,
+ stack_size,
+ priority,
+ affinity,
+ } = self;
+
+ let stack_size = stack_size.unwrap_or(imp::DEFAULT_MIN_STACK_SIZE);
+
+ // If no priority value is specified, spawn with the same
+ // priority as the parent thread
+ let priority = priority.unwrap_or_else(|| unsafe {
+ let mut default_priority = 0;
+ svcGetThreadPriority(&mut default_priority, 0xFFFF8000);
+ default_priority
+ });
+
+ // If no affinity is specified, spawn on the default core (determined by
+ // the application's Exheader)
+ let affinity = affinity.unwrap_or(-2);
+
+ let my_thread = Thread::new(name);
+ let their_thread = my_thread.clone();
+
+ let my_packet: Arc<UnsafeCell<Option<Result<T>>>> = Arc::new(UnsafeCell::new(None));
+ let their_packet = my_packet.clone();
+
+ let main = move || {
+ if let Some(name) = their_thread.cname() {
+ imp::Thread::set_name(name);
+ }
+ unsafe {
+ thread_info::set(their_thread);
+ let try_result = panic::catch_unwind(panic::AssertUnwindSafe(f));
+ *their_packet.get() = Some(try_result);
+ }
+ };
+
+ Ok(JoinHandle(JoinInner {
+ native: unsafe {
+ Some(imp::Thread::new(
+ stack_size,
+ priority,
+ affinity,
+ Box::new(main),
+ )?)
+ },
+ thread: my_thread,
+ packet: Packet(my_packet),
+ }))
+ }
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// Free functions
+////////////////////////////////////////////////////////////////////////////////
+
+/// Spawns a new thread, returning a [`JoinHandle`] for it.
+///
+/// The join handle will implicitly *detach* the child thread upon being
+/// dropped. In this case, the child thread may outlive the parent (unless
+/// the parent thread is the main thread; the whole process is terminated when
+/// the main thread finishes). Additionally, the join handle provides a [`join`]
+/// method that can be used to join the child thread. If the child thread
+/// panics, [`join`] will return an [`Err`] containing the argument given to
+/// [`panic`].
+///
+/// This will create a thread using default parameters of [`Builder`], if you
+/// want to specify the stack size or the name of the thread, use this API
+/// instead.
+///
+/// As you can see in the signature of `spawn` there are two constraints on
+/// both the closure given to `spawn` and its return value, let's explain them:
+///
+/// - The `'static` constraint means that the closure and its return value
+/// must have a lifetime of the whole program execution. The reason for this
+/// is that threads can `detach` and outlive the lifetime they have been
+/// created in.
+/// Indeed if the thread, and by extension its return value, can outlive their
+/// caller, we need to make sure that they will be valid afterwards, and since
+/// we *can't* know when it will return we need to have them valid as long as
+/// possible, that is until the end of the program, hence the `'static`
+/// lifetime.
+/// - The [`Send`] constraint is because the closure will need to be passed
+/// *by value* from the thread where it is spawned to the new thread. Its
+/// return value will need to be passed from the new thread to the thread
+/// where it is `join`ed.
+/// As a reminder, the [`Send`] marker trait expresses that it is safe to be
+/// passed from thread to thread. [`Sync`] expresses that it is safe to have a
+/// reference be passed from thread to thread.
+///
+/// # Panics
+///
+/// Panics if the OS fails to create a thread; use [`Builder::spawn`]
+/// to recover from such errors.
+pub fn spawn<F, T>(f: F) -> JoinHandle<T>
+where
+ F: FnOnce() -> T,
+ F: Send + 'static,
+ T: Send + 'static,
+{
+ Builder::new().spawn(f).unwrap()
+}
+
+/// Gets a handle to the thread that invokes it.
+///
+/// # Examples
+///
+/// Getting a handle to the current thread with `thread::current()`:
+///
+/// ```
+/// use ctru::thread;
+///
+/// let handler = thread::Builder::new()
+/// .name("named thread".into())
+/// .spawn(|| {
+/// let handle = thread::current();
+/// assert_eq!(handle.name(), Some("named thread"));
+/// })
+/// .unwrap();
+///
+/// handler.join().unwrap();
+/// ```
+pub fn current() -> Thread {
+ thread_info::current_thread().expect(
+ "use of ctru::thread::current() is not \
+ possible after the thread's local \
+ data has been destroyed",
+ )
+}
+
+/// Cooperatively gives up a timeslice to the OS scheduler.
+pub fn yield_now() {
+ imp::Thread::yield_now()
+}
+
+/// Determines whether the current thread is unwinding because of panic.
+///
+/// A common use of this feature is to poison shared resources when writing
+/// unsafe code, by checking `panicking` when the `drop` is called.
+///
+/// This is usually not needed when writing safe code, as [`Mutex`es][Mutex]
+/// already poison themselves when a thread panics while holding the lock.
+///
+/// This can also be used in multithreaded applications, in order to send a
+/// message to other threads warning that a thread has panicked (e.g. for
+/// monitoring purposes).
+#[inline]
+pub fn panicking() -> bool {
+ std_thread::panicking()
+}
+
+/// Puts the current thread to sleep for the specified amount of time.
+///
+/// The thread may sleep longer than the duration specified due to scheduling
+/// specifics or platform-dependent functionality.
+pub fn sleep(dur: Duration) {
+ imp::Thread::sleep(dur)
+}
+
+// constants for park/unpark
+const EMPTY: usize = 0;
+const PARKED: usize = 1;
+const NOTIFIED: usize = 2;
+
+/// Blocks unless or until the current thread's token is made available.
+///
+/// A call to `park` does not guarantee that the thread will remain parked
+/// forever, and callers should be prepared for this possibility.
+///
+/// # park and unpark
+///
+/// Every thread is equipped with some basic low-level blocking support, via the
+/// [`thread::park`][`park`] function and [`thread::Thread::unpark`][`unpark`]
+/// method. [`park`] blocks the current thread, which can then be resumed from
+/// another thread by calling the [`unpark`] method on the blocked thread's
+/// handle.
+///
+/// Conceptually, each [`Thread`] handle has an associated token, which is
+/// initially not present:
+///
+/// * The [`thread::park`][`park`] function blocks the current thread unless or
+/// until the token is available for its thread handle, at which point it
+/// atomically consumes the token. It may also return *spuriously*, without
+/// consuming the token. [`thread::park_timeout`] does the same, but allows
+/// specifying a maximum time to block the thread for.
+///
+/// * The [`unpark`] method on a [`Thread`] atomically makes the token available
+/// if it wasn't already.
+///
+/// In other words, each [`Thread`] acts a bit like a spinlock that can be
+/// locked and unlocked using `park` and `unpark`.
+///
+/// The API is typically used by acquiring a handle to the current thread,
+/// placing that handle in a shared data structure so that other threads can
+/// find it, and then `park`ing. When some desired condition is met, another
+/// thread calls [`unpark`] on the handle.
+///
+/// The motivation for this design is twofold:
+///
+/// * It avoids the need to allocate mutexes and condvars when building new
+/// synchronization primitives; the threads already provide basic
+/// blocking/signaling.
+///
+/// * It can be implemented very efficiently on many platforms.
+///
+/// # Examples
+///
+/// ```
+/// use ctru::thread;
+/// use std::time::Duration;
+///
+/// let parked_thread = thread::Builder::new()
+/// .spawn(|| {
+/// println!("Parking thread");
+/// thread::park();
+/// println!("Thread unparked");
+/// })
+/// .unwrap();
+///
+/// // Let some time pass for the thread to be spawned.
+/// thread::sleep(Duration::from_millis(10));
+///
+/// println!("Unpark the thread");
+/// parked_thread.thread().unpark();
+///
+/// parked_thread.join().unwrap();
+/// ```
+///
+/// [`Thread`]: ../../std/thread/struct.Thread.html
+/// [`park`]: ../../std/thread/fn.park.html
+/// [`unpark`]: ../../std/thread/struct.Thread.html#method.unpark
+/// [`thread::park_timeout`]: ../../std/thread/fn.park_timeout.html
+//
+// The implementation currently uses the trivial strategy of a Mutex+Condvar
+// with wakeup flag, which does not actually allow spurious wakeups. In the
+// future, this will be implemented in a more efficient way, perhaps along the lines of
+// http://cr.openjdk.java.net/~stefank/6989984.1/raw_files/new/src/os/linux/vm/os_linux.cpp
+// or futuxes, and in either case may allow spurious wakeups.
+pub fn park() {
+ let thread = current();
+
+ // If we were previously notified then we consume this notification and
+ // return quickly.
+ if thread
+ .inner
+ .state
+ .compare_exchange(NOTIFIED, EMPTY, SeqCst, SeqCst)
+ .is_ok()
+ {
+ return;
+ }
+
+ // Otherwise we need to coordinate going to sleep
+ let mut m = thread.inner.lock.lock().unwrap();
+ match thread
+ .inner
+ .state
+ .compare_exchange(EMPTY, PARKED, SeqCst, SeqCst)
+ {
+ Ok(_) => {}
+ Err(NOTIFIED) => return, // notified after we locked
+ Err(_) => panic!("inconsistent park state"),
+ }
+ loop {
+ m = thread.inner.cvar.wait(m).unwrap();
+ match thread
+ .inner
+ .state
+ .compare_exchange(NOTIFIED, EMPTY, SeqCst, SeqCst)
+ {
+ Ok(_) => return, // got a notification
+ Err(_) => {} // spurious wakeup, go back to sleep
+ }
+ }
+}
+
+/// Blocks unless or until the current thread's token is made available or
+/// the specified duration has been reached (may wake spuriously).
+///
+/// The semantics of this function are equivalent to [`park`][park] except
+/// that the thread will be blocked for roughly no longer than `dur`. This
+/// method should not be used for precise timing due to anomalies such as
+/// preemption or platform differences that may not cause the maximum
+/// amount of time waited to be precisely `dur` long.
+///
+/// See the [park documentation][park] for more details.
+///
+/// # Platform-specific behavior
+///
+/// Platforms which do not support nanosecond precision for sleeping will have
+/// `dur` rounded up to the nearest granularity of time they can sleep for.
+///
+/// # Examples
+///
+/// Waiting for the complete expiration of the timeout:
+///
+/// ```rust,no_run
+/// use ctru::thread::park_timeout;
+/// use std::time::{Instant, Duration};
+///
+/// let timeout = Duration::from_secs(2);
+/// let beginning_park = Instant::now();
+///
+/// let mut timeout_remaining = timeout;
+/// loop {
+/// park_timeout(timeout_remaining);
+/// let elapsed = beginning_park.elapsed();
+/// if elapsed >= timeout {
+/// break;
+/// }
+/// println!("restarting park_timeout after {:?}", elapsed);
+/// timeout_remaining = timeout - elapsed;
+/// }
+/// ```
+///
+/// [park]: fn.park.html
+pub fn park_timeout(dur: Duration) {
+ let thread = current();
+
+ // Like `park` above we have a fast path for an already-notified thread, and
+ // afterwards we start coordinating for a sleep.
+ // return quickly.
+ if thread
+ .inner
+ .state
+ .compare_exchange(NOTIFIED, EMPTY, SeqCst, SeqCst)
+ .is_ok()
+ {
+ return;
+ }
+ let m = thread.inner.lock.lock().unwrap();
+ match thread
+ .inner
+ .state
+ .compare_exchange(EMPTY, PARKED, SeqCst, SeqCst)
+ {
+ Ok(_) => {}
+ Err(NOTIFIED) => return, // notified after we locked
+ Err(_) => panic!("inconsistent park_timeout state"),
+ }
+
+ // Wait with a timeout, and if we spuriously wake up or otherwise wake up
+ // from a notification we just want to unconditionally set the state back to
+ // empty, either consuming a notification or un-flagging ourselves as
+ // parked.
+ let (_m, _result) = thread.inner.cvar.wait_timeout(m, dur).unwrap();
+ match thread.inner.state.swap(EMPTY, SeqCst) {
+ NOTIFIED => {} // got a notification, hurray!
+ PARKED => {} // no notification, alas
+ n => panic!("inconsistent park_timeout state: {}", n),
+ }
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// ThreadId
+////////////////////////////////////////////////////////////////////////////////
+
+/// A unique identifier for a running thread.
+///
+/// # Examples
+///
+/// ```
+/// use ctru::thread;
+///
+/// let other_thread = thread::spawn(|| {
+/// thread::current().id()
+/// });
+///
+/// let other_thread_id = other_thread.join().unwrap();
+/// assert!(thread::current().id() != other_thread_id);
+/// ```
+///
+/// [`id`]: ../../std/thread/struct.Thread.html#method.id
+/// [`Thread`]: ../../std/thread/struct.Thread.html
+#[derive(Eq, PartialEq, Clone, Copy, Hash, Debug)]
+pub struct ThreadId(u32);
+
+////////////////////////////////////////////////////////////////////////////////
+// Thread
+////////////////////////////////////////////////////////////////////////////////
+
+/// The internal representation of a `Thread` handle
+struct Inner {
+ name: Option<CString>, // Guaranteed to be UTF-8
+
+ // state for thread park/unpark
+ state: AtomicUsize,
+ lock: Mutex<()>,
+ cvar: Condvar,
+}
+
+#[derive(Clone)]
+/// A handle to a thread.
+///
+/// Threads are represented via the `Thread` type, which you can get in one of
+/// two ways:
+///
+/// * By spawning a new thread, e.g. using the [`thread::spawn`][`spawn`]
+/// function, and calling [`thread`][`JoinHandle::thread`] on the
+/// [`JoinHandle`].
+/// * By requesting the current thread, using the [`thread::current`] function.
+///
+/// The [`thread::current`] function is available even for threads not spawned
+/// by the APIs of this module.
+///
+/// There is usually no need to create a `Thread` struct yourself, one
+/// should instead use a function like `spawn` to create new threads, see the
+/// docs of [`Builder`] and [`spawn`] for more details.
+///
+/// [`Builder`]: ../../std/thread/struct.Builder.html
+/// [`JoinHandle::thread`]: ../../std/thread/struct.JoinHandle.html#method.thread
+/// [`JoinHandle`]: ../../std/thread/struct.JoinHandle.html
+/// [`thread::current`]: ../../std/thread/fn.current.html
+/// [`spawn`]: ../../std/thread/fn.spawn.html
+
+pub struct Thread {
+ inner: Arc<Inner>,
+}
+
+impl Thread {
+ // Used only internally to construct a thread object without spawning
+ // Panics if the name contains nuls.
+ pub(crate) fn new(name: Option<String>) -> Thread {
+ let cname = name
+ .map(|n| CString::new(n)
+ .expect("thread name may not contain interior null bytes"));
+ Thread {
+ inner: Arc::new(Inner {
+ name: cname,
+ state: AtomicUsize::new(EMPTY),
+ lock: Mutex::new(()),
+ cvar: Condvar::new(),
+ }),
+ }
+ }
+
+ /// Atomically makes the handle's token available if it is not already.
+ ///
+ /// Every thread is equipped with some basic low-level blocking support, via
+ /// the [`park`][park] function and the `unpark()` method. These can be
+ /// used as a more CPU-efficient implementation of a spinlock.
+ ///
+ /// See the [park documentation][park] for more details.
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// use ctru::thread;
+ /// use std::time::Duration;
+ ///
+ /// let parked_thread = thread::Builder::new()
+ /// .spawn(|| {
+ /// println!("Parking thread");
+ /// thread::park();
+ /// println!("Thread unparked");
+ /// })
+ /// .unwrap();
+ ///
+ /// // Let some time pass for the thread to be spawned.
+ /// thread::sleep(Duration::from_millis(10));
+ ///
+ /// println!("Unpark the thread");
+ /// parked_thread.thread().unpark();
+ ///
+ /// parked_thread.join().unwrap();
+ /// ```
+ ///
+ /// [park]: fn.park.html
+ pub fn unpark(&self) {
+ loop {
+ match self.inner
+ .state
+ .compare_exchange(EMPTY, NOTIFIED, SeqCst, SeqCst)
+ {
+ Ok(_) => return, // no one was waiting
+ Err(NOTIFIED) => return, // already unparked
+ Err(PARKED) => {} // gotta go wake someone up
+ _ => panic!("inconsistent state in unpark"),
+ }
+
+ // Coordinate wakeup through the mutex and a condvar notification
+ let _lock = self.inner.lock.lock().unwrap();
+ match self.inner
+ .state
+ .compare_exchange(PARKED, NOTIFIED, SeqCst, SeqCst)
+ {
+ Ok(_) => return self.inner.cvar.notify_one(),
+ Err(NOTIFIED) => return, // a different thread unparked
+ Err(EMPTY) => {} // parked thread went away, try again
+ _ => panic!("inconsistent state in unpark"),
+ }
+ }
+ }
+
+ /// Gets the thread's unique identifier.
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// use ctru::thread;
+ ///
+ /// let other_thread = thread::spawn(|| {
+ /// thread::current().id()
+ /// });
+ ///
+ /// let other_thread_id = other_thread.join().unwrap();
+ /// assert!(thread::current().id() != other_thread_id);
+ /// ```
+ pub fn id(&self) -> ThreadId {
+ unsafe {
+ let mut id = 0;
+ svcGetThreadId(&mut id, 0xFFFF8000);
+ ThreadId(id)
+ }
+ }
+
+ /// Gets the thread's name.
+ ///
+ /// For more information about named threads, see
+ /// [this module-level documentation][naming-threads].
+ ///
+ /// # Examples
+ ///
+ /// Threads by default have no name specified:
+ ///
+ /// ```
+ /// use ctru::thread;
+ ///
+ /// let builder = thread::Builder::new();
+ ///
+ /// let handler = builder.spawn(|| {
+ /// assert!(thread::current().name().is_none());
+ /// }).unwrap();
+ ///
+ /// handler.join().unwrap();
+ /// ```
+ ///
+ /// Thread with a specified name:
+ ///
+ /// ```
+ /// use ctru::thread;
+ ///
+ /// let builder = thread::Builder::new()
+ /// .name("foo".into());
+ ///
+ /// let handler = builder.spawn(|| {
+ /// assert_eq!(thread::current().name(), Some("foo"))
+ /// }).unwrap();
+ ///
+ /// handler.join().unwrap();
+ /// ```
+ ///
+ /// [naming-threads]: ./index.html#naming-threads
+ pub fn name(&self) -> Option<&str> {
+ self.cname()
+ .map(|s| unsafe { str::from_utf8_unchecked(s.to_bytes()) })
+ }
+
+ /// Get the current thread's priority level. Lower values correspond to higher
+ /// priority levels. The main thread's priority is typically 0x30, but not always.
+ pub fn priority(&self) -> i32 {
+ unsafe {
+ let mut priority = 0;
+ svcGetThreadPriority(&mut priority, 0xFFFF8000);
+ priority
+ }
+ }
+
+ /// Returns the ID of the processor the current thread is running on.
+ pub fn affinity(&self) -> i32 {
+ unsafe {
+ svcGetProcessorID()
+ }
+ }
+
+ fn cname(&self) -> Option<&CStr> {
+ self.inner.name.as_ref().map(|s| &**s)
+ }
+}
+
+impl fmt::Debug for Thread {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ fmt::Debug::fmt(&self.name(), f)
+ }
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// JoinHandle
+////////////////////////////////////////////////////////////////////////////////
+
+/// A specialized [`Result`] type for threads.
+///
+/// Indicates the manner in which a thread exited.
+///
+/// A thread that completes without panicking is considered to exit successfully.
+///
+/// # Examples
+///
+/// ```no_run
+/// use ctru::thread;
+/// use std::fs;
+///
+/// fn copy_in_thread() -> thread::Result<()> {
+/// thread::spawn(move || { fs::copy("foo.txt", "bar.txt").unwrap(); }).join()
+/// }
+///
+/// fn main() {
+/// match copy_in_thread() {
+/// Ok(_) => println!("this is fine"),
+/// Err(_) => println!("thread panicked"),
+/// }
+/// }
+/// ```
+///
+/// [`Result`]: ../../std/result/enum.Result.html
+pub type Result<T> = ::std::result::Result<T, Box<Any + Send + 'static>>;
+
+// This packet is used to communicate the return value between the child thread
+// and the parent thread. Memory is shared through the `Arc` within and there's
+// no need for a mutex here because synchronization happens with `join()` (the
+// parent thread never reads this packet until the child has exited).
+//
+// This packet itself is then stored into a `JoinInner` which in turns is placed
+// in `JoinHandle` and `JoinGuard`. Due to the usage of `UnsafeCell` we need to
+// manually worry about impls like Send and Sync. The type `T` should
+// already always be Send (otherwise the thread could not have been created) and
+// this type is inherently Sync because no methods take &self. Regardless,
+// however, we add inheriting impls for Send/Sync to this type to ensure it's
+// Send/Sync and that future modifications will still appropriately classify it.
+struct Packet<T>(Arc<UnsafeCell<Option<Result<T>>>>);
+
+unsafe impl<T: Send> Send for Packet<T> {}
+unsafe impl<T: Sync> Sync for Packet<T> {}
+
+/// Inner representation for JoinHandle
+struct JoinInner<T> {
+ native: Option<imp::Thread>,
+ thread: Thread,
+ packet: Packet<T>,
+}
+
+impl<T> JoinInner<T> {
+ fn join(&mut self) -> Result<T> {
+ self.native.take().unwrap().join();
+ unsafe { (*self.packet.0.get()).take().unwrap() }
+ }
+}
+
+/// An owned permission to join on a thread (block on its termination).
+///
+/// A `JoinHandle` *detaches* the associated thread when it is dropped, which
+/// means that there is no longer any handle to thread and no way to `join`
+/// on it.
+///
+/// Due to platform restrictions, it is not possible to [`Clone`] this
+/// handle: the ability to join a thread is a uniquely-owned permission.
+///
+/// This `struct` is created by the [`thread::spawn`] function and the
+/// [`thread::Builder::spawn`] method.
+///
+/// # Examples
+///
+/// Creation from [`thread::spawn`]:
+///
+/// ```
+/// use ctru::thread;
+///
+/// let join_handle: thread::JoinHandle<_> = thread::spawn(|| {
+/// // some work here
+/// });
+/// ```
+///
+/// Creation from [`thread::Builder::spawn`]:
+///
+/// ```
+/// use ctru::thread;
+///
+/// let builder = thread::Builder::new();
+///
+/// let join_handle: thread::JoinHandle<_> = builder.spawn(|| {
+/// // some work here
+/// }).unwrap();
+/// ```
+///
+/// Child being detached and outliving its parent:
+///
+/// ```no_run
+/// use ctru::thread;
+/// use std::time::Duration;
+///
+/// let original_thread = thread::spawn(|| {
+/// let _detached_thread = thread::spawn(|| {
+/// // Here we sleep to make sure that the first thread returns before.
+/// thread::sleep(Duration::from_millis(10));
+/// // This will be called, even though the JoinHandle is dropped.
+/// println!("♫ Still alive ♫");
+/// });
+/// });
+///
+/// original_thread.join().expect("The thread being joined has panicked");
+/// println!("Original thread is joined.");
+///
+/// // We make sure that the new thread has time to run, before the main
+/// // thread returns.
+///
+/// thread::sleep(Duration::from_millis(1000));
+/// ```
+///
+/// [`Clone`]: ../../std/clone/trait.Clone.html
+/// [`thread::spawn`]: fn.spawn.html
+/// [`thread::Builder::spawn`]: struct.Builder.html#method.spawn
+pub struct JoinHandle<T>(JoinInner<T>);
+
+impl<T> JoinHandle<T> {
+ /// Extracts a handle to the underlying thread.
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// use ctru::thread;
+ ///
+ /// let builder = thread::Builder::new();
+ ///
+ /// let join_handle: thread::JoinHandle<_> = builder.spawn(|| {
+ /// // some work here
+ /// }).unwrap();
+ ///
+ /// let thread = join_handle.thread();
+ /// println!("thread id: {:?}", thread.id());
+ /// ```
+ pub fn thread(&self) -> &Thread {
+ &self.0.thread
+ }
+
+ /// Waits for the associated thread to finish.
+ ///
+ /// If the child thread panics, [`Err`] is returned with the parameter given
+ /// to [`panic`].
+ ///
+ /// [`Err`]: ../../std/result/enum.Result.html#variant.Err
+ /// [`panic`]: ../../std/macro.panic.html
+ ///
+ /// # Panics
+ ///
+ /// This function may panic on some platforms if a thread attempts to join
+ /// itself or otherwise may create a deadlock with joining threads.
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// use ctru::thread;
+ ///
+ /// let builder = thread::Builder::new();
+ ///
+ /// let join_handle: thread::JoinHandle<_> = builder.spawn(|| {
+ /// // some work here
+ /// }).unwrap();
+ /// join_handle.join().expect("Couldn't join on the associated thread");
+ /// ```
+ pub fn join(mut self) -> Result<T> {
+ self.0.join()
+ }
+}
+
+impl<T> fmt::Debug for JoinHandle<T> {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ f.pad("JoinHandle { .. }")
+ }
+}
+
+fn _assert_sync_and_send() {
+ fn _assert_both<T: Send + Sync>() {}
+ _assert_both::<JoinHandle<()>>();
+ _assert_both::<Thread>();
+}
+
+mod imp {
+ use std::boxed::FnBox;
+ use std::cmp;
+ use std::ffi::CStr;
+ use std::io;
+ use std::mem;
+ use std::ptr;
+ use std::time::Duration;
+
+ use libc;
+
+ use libctru::{Thread as ThreadHandle, svcSleepThread, threadCreate, threadDetach,
+ threadFree, threadJoin};
+
+ pub struct Thread {
+ handle: ThreadHandle,
+ }
+
+ unsafe impl Send for Thread {}
+ unsafe impl Sync for Thread {}
+
+ pub const DEFAULT_MIN_STACK_SIZE: usize = 4096;
+
+ impl Thread {
+ pub unsafe fn new<'a>(
+ stack: usize,
+ priority: i32,
+ affinity: i32,
+ p: Box<FnBox() + 'a>,
+ ) -> io::Result<Thread> {
+ let p = Box::new(p);
+ let stack_size = cmp::max(stack, DEFAULT_MIN_STACK_SIZE);
+
+ let handle = threadCreate(
+ Some(thread_func),
+ &*p as *const _ as *mut _,
+ stack_size,
+ priority,
+ affinity,
+ false,
+ );
+
+ return if handle == ptr::null_mut() {
+ Err(io::Error::from_raw_os_error(libc::EAGAIN))
+ } else {
+ mem::forget(p); // ownership passed to the new thread
+ Ok(Thread { handle: handle })
+ };
+
+ extern "C" fn thread_func(start: *mut libc::c_void) {
+ unsafe { Thread::_start_thread(start as *mut u8) }
+ }
+ }
+
+ unsafe fn _start_thread(main: *mut u8) {
+ Box::from_raw(main as *mut Box<FnBox()>)()
+ }
+
+ pub fn yield_now() {
+ unsafe { svcSleepThread(0) }
+ }
+
+ pub fn set_name(_name: &CStr) {
+ // threads aren't named in libctru
+ }
+
+ pub fn sleep(dur: Duration) {
+ unsafe {
+ let nanos = dur.as_secs()
+ .saturating_mul(1_000_000_000)
+ .saturating_add(dur.subsec_nanos() as u64);
+ svcSleepThread(nanos as i64)
+ }
+ }
+
+ pub fn join(self) {
+ unsafe {
+ let ret = threadJoin(self.handle, u64::max_value());
+ threadFree(self.handle);
+ mem::forget(self);
+ debug_assert_eq!(ret, 0);
+ }
+ }
+
+ #[allow(dead_code)]
+ pub fn id(&self) -> ThreadHandle {
+ self.handle
+ }
+
+ #[allow(dead_code)]
+ pub fn into_id(self) -> ThreadHandle {
+ let handle = self.handle;
+ mem::forget(self);
+ handle
+ }
+ }
+
+ impl Drop for Thread {
+ fn drop(&mut self) {
+ unsafe { threadDetach(self.handle) }
+ }
+ }
+}
+
+mod thread_info {
+ use std::cell::RefCell;
+ use thread::Thread;
+
+ struct ThreadInfo {
+ thread: Thread,
+ }
+
+ thread_local! { static THREAD_INFO: RefCell<Option<ThreadInfo>> = RefCell::new(None) }
+
+ impl ThreadInfo {
+ fn with<R, F>(f: F) -> Option<R>
+ where
+ F: FnOnce(&mut ThreadInfo) -> R,
+ {
+ THREAD_INFO
+ .try_with(move |c| {
+ if c.borrow().is_none() {
+ *c.borrow_mut() = Some(ThreadInfo {
+ thread: Thread::new(None),
+ })
+ }
+ f(c.borrow_mut().as_mut().unwrap())
+ })
+ .ok()
+ }
+ }
+
+ pub fn current_thread() -> Option<Thread> {
+ ThreadInfo::with(|info| info.thread.clone())
+ }
+
+ pub fn set(thread: Thread) {
+ THREAD_INFO.with(|c| assert!(c.borrow().is_none()));
+ THREAD_INFO.with(move |c| *c.borrow_mut() = Some(ThreadInfo { thread }));
+ }
+}