Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add std::thread::available_concurrency #74480

Merged
merged 1 commit into from
Oct 18, 2020
Merged
Show file tree
Hide file tree
Changes from all 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
157 changes: 157 additions & 0 deletions library/std/src/thread/available_concurrency.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,157 @@
use crate::io;
use crate::num::NonZeroUsize;

/// Returns the number of hardware threads available to the program.
///
/// This value should be considered only a hint.
///
/// # Platform-specific behavior
///
/// If interpreted as the number of actual hardware threads, it may undercount on
/// Windows systems with more than 64 hardware threads. If interpreted as the
/// available concurrency for that process, it may overcount on Windows systems
/// when limited by a process wide affinity mask or job object limitations, and
/// it may overcount on Linux systems when limited by a process wide affinity
/// mask or affected by cgroups limits.
///
/// # Errors
///
/// This function will return an error in the following situations, but is not
/// limited to just these cases:
///
/// - If the number of hardware threads is not known for the target platform.
/// - The process lacks permissions to view the number of hardware threads
/// available.
///
/// # Examples
///
/// ```
/// # #![allow(dead_code)]
/// #![feature(available_concurrency)]
/// use std::thread;
///
/// let count = thread::available_concurrency().map(|n| n.get()).unwrap_or(1);
/// ```
#[unstable(feature = "available_concurrency", issue = "74479")]
pub fn available_concurrency() -> io::Result<NonZeroUsize> {
available_concurrency_internal()
}

cfg_if::cfg_if! {
if #[cfg(windows)] {
#[allow(nonstandard_style)]
fn available_concurrency_internal() -> io::Result<NonZeroUsize> {
#[repr(C)]
struct SYSTEM_INFO {
wProcessorArchitecture: u16,
wReserved: u16,
dwPageSize: u32,
lpMinimumApplicationAddress: *mut u8,
lpMaximumApplicationAddress: *mut u8,
dwActiveProcessorMask: *mut u8,
dwNumberOfProcessors: u32,
dwProcessorType: u32,
dwAllocationGranularity: u32,
wProcessorLevel: u16,
wProcessorRevision: u16,
}
extern "system" {
fn GetSystemInfo(info: *mut SYSTEM_INFO) -> i32;
}
let res = unsafe {
let mut sysinfo = crate::mem::zeroed();
GetSystemInfo(&mut sysinfo);
sysinfo.dwNumberOfProcessors as usize
};
match res {
0 => Err(io::Error::new(io::ErrorKind::NotFound, "The number of hardware threads is not known for the target platform")),
cpus => Ok(unsafe { NonZeroUsize::new_unchecked(cpus) }),
}
}
} else if #[cfg(any(
target_os = "android",
target_os = "cloudabi",
target_os = "emscripten",
target_os = "fuchsia",
target_os = "ios",
target_os = "linux",
target_os = "macos",
target_os = "solaris",
target_os = "illumos",
))] {
fn available_concurrency_internal() -> io::Result<NonZeroUsize> {
match unsafe { libc::sysconf(libc::_SC_NPROCESSORS_ONLN) } {
-1 => Err(io::Error::last_os_error()),
0 => Err(io::Error::new(io::ErrorKind::NotFound, "The number of hardware threads is not known for the target platform")),
cpus => Ok(unsafe { NonZeroUsize::new_unchecked(cpus as usize) }),
}
}
} else if #[cfg(any(target_os = "freebsd", target_os = "dragonfly", target_os = "netbsd"))] {
fn available_concurrency_internal() -> io::Result<NonZeroUsize> {
use crate::ptr;

let mut cpus: libc::c_uint = 0;
let mut cpus_size = crate::mem::size_of_val(&cpus);

unsafe {
cpus = libc::sysconf(libc::_SC_NPROCESSORS_ONLN) as libc::c_uint;
}

// Fallback approach in case of errors or no hardware threads.
if cpus < 1 {
let mut mib = [libc::CTL_HW, libc::HW_NCPU, 0, 0];
let res = unsafe {
libc::sysctl(
mib.as_mut_ptr(),
2,
&mut cpus as *mut _ as *mut _,
&mut cpus_size as *mut _ as *mut _,
ptr::null_mut(),
0,
)
};

// Handle errors if any.
if res == -1 {
return Err(io::Error::last_os_error());
} else if cpus == 0 {
return Err(io::Error::new(io::ErrorKind::NotFound, "The number of hardware threads is not known for the target platform"));
}
}
Ok(unsafe { NonZeroUsize::new_unchecked(cpus as usize) })
}
} else if #[cfg(target_os = "openbsd")] {
fn available_concurrency_internal() -> io::Result<NonZeroUsize> {
use crate::ptr;

let mut cpus: libc::c_uint = 0;
let mut cpus_size = crate::mem::size_of_val(&cpus);
let mut mib = [libc::CTL_HW, libc::HW_NCPU, 0, 0];

let res = unsafe {
libc::sysctl(
mib.as_mut_ptr(),
2,
&mut cpus as *mut _ as *mut _,
&mut cpus_size as *mut _ as *mut _,
ptr::null_mut(),
0,
)
};

// Handle errors if any.
if res == -1 {
return Err(io::Error::last_os_error());
} else if cpus == 0 {
return Err(io::Error::new(io::ErrorKind::NotFound, "The number of hardware threads is not known for the target platform"));
}

Ok(unsafe { NonZeroUsize::new_unchecked(cpus as usize) })
}
} else {
// FIXME: implement on vxWorks, Redox, HermitCore, Haiku, l4re
fn available_concurrency_internal() -> io::Result<NonZeroUsize> {
Err(io::Error::new(io::ErrorKind::NotFound, "The number of hardware threads is not known for the target platform"))
}
}
}
6 changes: 6 additions & 0 deletions library/std/src/thread/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -175,9 +175,15 @@ use crate::time::Duration;
#[macro_use]
mod local;

#[unstable(feature = "available_concurrency", issue = "74479")]
mod available_concurrency;

#[stable(feature = "rust1", since = "1.0.0")]
pub use self::local::{AccessError, LocalKey};

#[unstable(feature = "available_concurrency", issue = "74479")]
pub use available_concurrency::available_concurrency;

// The types used by the thread_local! macro to access TLS keys. Note that there
// are two types, the "OS" type and the "fast" type. The OS thread local key
// type is accessed via platform-specific API calls and is slow, while the fast
Expand Down
103 changes: 2 additions & 101 deletions library/test/src/helpers/concurrency.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
//! Helper module which helps to determine amount of threads to be used
//! during tests execution.
use std::env;
use std::thread;

#[allow(deprecated)]
pub fn get_concurrency() -> usize {
Expand All @@ -12,106 +13,6 @@ pub fn get_concurrency() -> usize {
_ => panic!("RUST_TEST_THREADS is `{}`, should be a positive integer.", s),
}
}
Err(..) => num_cpus(),
}
}

cfg_if::cfg_if! {
if #[cfg(windows)] {
#[allow(nonstandard_style)]
fn num_cpus() -> usize {
#[repr(C)]
struct SYSTEM_INFO {
wProcessorArchitecture: u16,
wReserved: u16,
dwPageSize: u32,
lpMinimumApplicationAddress: *mut u8,
lpMaximumApplicationAddress: *mut u8,
dwActiveProcessorMask: *mut u8,
dwNumberOfProcessors: u32,
dwProcessorType: u32,
dwAllocationGranularity: u32,
wProcessorLevel: u16,
wProcessorRevision: u16,
}
extern "system" {
fn GetSystemInfo(info: *mut SYSTEM_INFO) -> i32;
}
unsafe {
let mut sysinfo = std::mem::zeroed();
GetSystemInfo(&mut sysinfo);
sysinfo.dwNumberOfProcessors as usize
}
}
} else if #[cfg(any(
target_os = "android",
target_os = "cloudabi",
target_os = "emscripten",
target_os = "fuchsia",
target_os = "ios",
target_os = "linux",
target_os = "macos",
target_os = "solaris",
target_os = "illumos",
))] {
fn num_cpus() -> usize {
unsafe { libc::sysconf(libc::_SC_NPROCESSORS_ONLN) as usize }
}
} else if #[cfg(any(target_os = "freebsd", target_os = "dragonfly", target_os = "netbsd"))] {
fn num_cpus() -> usize {
use std::ptr;

let mut cpus: libc::c_uint = 0;
let mut cpus_size = std::mem::size_of_val(&cpus);

unsafe {
cpus = libc::sysconf(libc::_SC_NPROCESSORS_ONLN) as libc::c_uint;
}
if cpus < 1 {
let mut mib = [libc::CTL_HW, libc::HW_NCPU, 0, 0];
unsafe {
libc::sysctl(
mib.as_mut_ptr(),
2,
&mut cpus as *mut _ as *mut _,
&mut cpus_size as *mut _ as *mut _,
ptr::null_mut(),
0,
);
}
if cpus < 1 {
cpus = 1;
}
}
cpus as usize
}
} else if #[cfg(target_os = "openbsd")] {
fn num_cpus() -> usize {
use std::ptr;

let mut cpus: libc::c_uint = 0;
let mut cpus_size = std::mem::size_of_val(&cpus);
let mut mib = [libc::CTL_HW, libc::HW_NCPU, 0, 0];

unsafe {
libc::sysctl(
mib.as_mut_ptr(),
2,
&mut cpus as *mut _ as *mut _,
&mut cpus_size as *mut _ as *mut _,
ptr::null_mut(),
0,
);
}
if cpus < 1 {
cpus = 1;
}
cpus as usize
}
} else {
// FIXME: implement on vxWorks, Redox, HermitCore, Haiku, l4re
fn num_cpus() -> usize {
1
}
Err(..) => thread::available_concurrency().map(|n| n.get()).unwrap_or(1),
}
}
1 change: 1 addition & 0 deletions library/test/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
#![feature(rustc_private)]
#![feature(nll)]
#![feature(bool_to_option)]
#![feature(available_concurrency)]
#![feature(set_stdio)]
#![feature(panic_unwind)]
#![feature(staged_api)]
Expand Down