Skip to content

Commit 3717646

Browse files
committed
Add std::thread::available_concurrency
1 parent a8d6da3 commit 3717646

File tree

4 files changed

+166
-101
lines changed

4 files changed

+166
-101
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,157 @@
1+
use crate::io;
2+
use crate::num::NonZeroUsize;
3+
4+
/// Returns the number of hardware threads available to the program.
5+
///
6+
/// This value should be considered only a hint.
7+
///
8+
/// # Platform-specific behavior
9+
///
10+
/// If interpreted as the number of actual hardware threads, it may undercount on
11+
/// Windows systems with more than 64 hardware threads. If interpreted as the
12+
/// available concurrency for that process, it may overcount on Windows systems
13+
/// when limited by a process wide affinity mask or job object limitations, and
14+
/// it may overcount on Linux systems when limited by a process wide affinity
15+
/// mask or affected by cgroups limits.
16+
///
17+
/// # Errors
18+
///
19+
/// This function will return an error in the following situations, but is not
20+
/// limited to just these cases:
21+
///
22+
/// - If the number of hardware threads is not known for the target platform.
23+
/// - The process lacks permissions to view the number of hardware threads
24+
/// available.
25+
///
26+
/// # Examples
27+
///
28+
/// ```
29+
/// # #![allow(dead_code)]
30+
/// #![feature(available_concurrency)]
31+
/// use std::thread;
32+
///
33+
/// let count = thread::available_concurrency().map(|n| n.get()).unwrap_or(1);
34+
/// ```
35+
#[unstable(feature = "available_concurrency", issue = "74479")]
36+
pub fn available_concurrency() -> io::Result<NonZeroUsize> {
37+
available_concurrency_internal()
38+
}
39+
40+
cfg_if::cfg_if! {
41+
if #[cfg(windows)] {
42+
#[allow(nonstandard_style)]
43+
fn available_concurrency_internal() -> io::Result<NonZeroUsize> {
44+
#[repr(C)]
45+
struct SYSTEM_INFO {
46+
wProcessorArchitecture: u16,
47+
wReserved: u16,
48+
dwPageSize: u32,
49+
lpMinimumApplicationAddress: *mut u8,
50+
lpMaximumApplicationAddress: *mut u8,
51+
dwActiveProcessorMask: *mut u8,
52+
dwNumberOfProcessors: u32,
53+
dwProcessorType: u32,
54+
dwAllocationGranularity: u32,
55+
wProcessorLevel: u16,
56+
wProcessorRevision: u16,
57+
}
58+
extern "system" {
59+
fn GetSystemInfo(info: *mut SYSTEM_INFO) -> i32;
60+
}
61+
let res = unsafe {
62+
let mut sysinfo = crate::mem::zeroed();
63+
GetSystemInfo(&mut sysinfo);
64+
sysinfo.dwNumberOfProcessors as usize
65+
};
66+
match res {
67+
0 => Err(io::Error::new(io::ErrorKind::NotFound, "The number of hardware threads is not known for the target platform")),
68+
cpus => Ok(unsafe { NonZeroUsize::new_unchecked(cpus) }),
69+
}
70+
}
71+
} else if #[cfg(any(
72+
target_os = "android",
73+
target_os = "cloudabi",
74+
target_os = "emscripten",
75+
target_os = "fuchsia",
76+
target_os = "ios",
77+
target_os = "linux",
78+
target_os = "macos",
79+
target_os = "solaris",
80+
target_os = "illumos",
81+
))] {
82+
fn available_concurrency_internal() -> io::Result<NonZeroUsize> {
83+
match unsafe { libc::sysconf(libc::_SC_NPROCESSORS_ONLN) } {
84+
-1 => Err(io::Error::last_os_error()),
85+
0 => Err(io::Error::new(io::ErrorKind::NotFound, "The number of hardware threads is not known for the target platform")),
86+
cpus => Ok(unsafe { NonZeroUsize::new_unchecked(cpus as usize) }),
87+
}
88+
}
89+
} else if #[cfg(any(target_os = "freebsd", target_os = "dragonfly", target_os = "netbsd"))] {
90+
fn available_concurrency_internal() -> io::Result<NonZeroUsize> {
91+
use crate::ptr;
92+
93+
let mut cpus: libc::c_uint = 0;
94+
let mut cpus_size = crate::mem::size_of_val(&cpus);
95+
96+
unsafe {
97+
cpus = libc::sysconf(libc::_SC_NPROCESSORS_ONLN) as libc::c_uint;
98+
}
99+
100+
// Fallback approach in case of errors or no hardware threads.
101+
if cpus < 1 {
102+
let mut mib = [libc::CTL_HW, libc::HW_NCPU, 0, 0];
103+
let res = unsafe {
104+
libc::sysctl(
105+
mib.as_mut_ptr(),
106+
2,
107+
&mut cpus as *mut _ as *mut _,
108+
&mut cpus_size as *mut _ as *mut _,
109+
ptr::null_mut(),
110+
0,
111+
)
112+
};
113+
114+
// Handle errors if any.
115+
if res == -1 {
116+
return Err(io::Error::last_os_error());
117+
} else if cpus == 0 {
118+
return Err(io::Error::new(io::ErrorKind::NotFound, "The number of hardware threads is not known for the target platform"));
119+
}
120+
}
121+
Ok(unsafe { NonZeroUsize::new_unchecked(cpus as usize) })
122+
}
123+
} else if #[cfg(target_os = "openbsd")] {
124+
fn available_concurrency_internal() -> io::Result<NonZeroUsize> {
125+
use crate::ptr;
126+
127+
let mut cpus: libc::c_uint = 0;
128+
let mut cpus_size = crate::mem::size_of_val(&cpus);
129+
let mut mib = [libc::CTL_HW, libc::HW_NCPU, 0, 0];
130+
131+
let res = unsafe {
132+
libc::sysctl(
133+
mib.as_mut_ptr(),
134+
2,
135+
&mut cpus as *mut _ as *mut _,
136+
&mut cpus_size as *mut _ as *mut _,
137+
ptr::null_mut(),
138+
0,
139+
)
140+
};
141+
142+
// Handle errors if any.
143+
if res == -1 {
144+
return Err(io::Error::last_os_error());
145+
} else if cpus == 0 {
146+
return Err(io::Error::new(io::ErrorKind::NotFound, "The number of hardware threads is not known for the target platform"));
147+
}
148+
149+
Ok(unsafe { NonZeroUsize::new_unchecked(cpus as usize) })
150+
}
151+
} else {
152+
// FIXME: implement on vxWorks, Redox, HermitCore, Haiku, l4re
153+
fn available_concurrency_internal() -> io::Result<NonZeroUsize> {
154+
Err(io::Error::new(io::ErrorKind::NotFound, "The number of hardware threads is not known for the target platform"))
155+
}
156+
}
157+
}

library/std/src/thread/mod.rs

+6
Original file line numberDiff line numberDiff line change
@@ -175,9 +175,15 @@ use crate::time::Duration;
175175
#[macro_use]
176176
mod local;
177177

178+
#[unstable(feature = "available_concurrency", issue = "74479")]
179+
mod available_concurrency;
180+
178181
#[stable(feature = "rust1", since = "1.0.0")]
179182
pub use self::local::{AccessError, LocalKey};
180183

184+
#[unstable(feature = "available_concurrency", issue = "74479")]
185+
pub use available_concurrency::available_concurrency;
186+
181187
// The types used by the thread_local! macro to access TLS keys. Note that there
182188
// are two types, the "OS" type and the "fast" type. The OS thread local key
183189
// type is accessed via platform-specific API calls and is slow, while the fast
+2-101
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
//! Helper module which helps to determine amount of threads to be used
22
//! during tests execution.
33
use std::env;
4+
use std::thread;
45

56
#[allow(deprecated)]
67
pub fn get_concurrency() -> usize {
@@ -12,106 +13,6 @@ pub fn get_concurrency() -> usize {
1213
_ => panic!("RUST_TEST_THREADS is `{}`, should be a positive integer.", s),
1314
}
1415
}
15-
Err(..) => num_cpus(),
16-
}
17-
}
18-
19-
cfg_if::cfg_if! {
20-
if #[cfg(windows)] {
21-
#[allow(nonstandard_style)]
22-
fn num_cpus() -> usize {
23-
#[repr(C)]
24-
struct SYSTEM_INFO {
25-
wProcessorArchitecture: u16,
26-
wReserved: u16,
27-
dwPageSize: u32,
28-
lpMinimumApplicationAddress: *mut u8,
29-
lpMaximumApplicationAddress: *mut u8,
30-
dwActiveProcessorMask: *mut u8,
31-
dwNumberOfProcessors: u32,
32-
dwProcessorType: u32,
33-
dwAllocationGranularity: u32,
34-
wProcessorLevel: u16,
35-
wProcessorRevision: u16,
36-
}
37-
extern "system" {
38-
fn GetSystemInfo(info: *mut SYSTEM_INFO) -> i32;
39-
}
40-
unsafe {
41-
let mut sysinfo = std::mem::zeroed();
42-
GetSystemInfo(&mut sysinfo);
43-
sysinfo.dwNumberOfProcessors as usize
44-
}
45-
}
46-
} else if #[cfg(any(
47-
target_os = "android",
48-
target_os = "cloudabi",
49-
target_os = "emscripten",
50-
target_os = "fuchsia",
51-
target_os = "ios",
52-
target_os = "linux",
53-
target_os = "macos",
54-
target_os = "solaris",
55-
target_os = "illumos",
56-
))] {
57-
fn num_cpus() -> usize {
58-
unsafe { libc::sysconf(libc::_SC_NPROCESSORS_ONLN) as usize }
59-
}
60-
} else if #[cfg(any(target_os = "freebsd", target_os = "dragonfly", target_os = "netbsd"))] {
61-
fn num_cpus() -> usize {
62-
use std::ptr;
63-
64-
let mut cpus: libc::c_uint = 0;
65-
let mut cpus_size = std::mem::size_of_val(&cpus);
66-
67-
unsafe {
68-
cpus = libc::sysconf(libc::_SC_NPROCESSORS_ONLN) as libc::c_uint;
69-
}
70-
if cpus < 1 {
71-
let mut mib = [libc::CTL_HW, libc::HW_NCPU, 0, 0];
72-
unsafe {
73-
libc::sysctl(
74-
mib.as_mut_ptr(),
75-
2,
76-
&mut cpus as *mut _ as *mut _,
77-
&mut cpus_size as *mut _ as *mut _,
78-
ptr::null_mut(),
79-
0,
80-
);
81-
}
82-
if cpus < 1 {
83-
cpus = 1;
84-
}
85-
}
86-
cpus as usize
87-
}
88-
} else if #[cfg(target_os = "openbsd")] {
89-
fn num_cpus() -> usize {
90-
use std::ptr;
91-
92-
let mut cpus: libc::c_uint = 0;
93-
let mut cpus_size = std::mem::size_of_val(&cpus);
94-
let mut mib = [libc::CTL_HW, libc::HW_NCPU, 0, 0];
95-
96-
unsafe {
97-
libc::sysctl(
98-
mib.as_mut_ptr(),
99-
2,
100-
&mut cpus as *mut _ as *mut _,
101-
&mut cpus_size as *mut _ as *mut _,
102-
ptr::null_mut(),
103-
0,
104-
);
105-
}
106-
if cpus < 1 {
107-
cpus = 1;
108-
}
109-
cpus as usize
110-
}
111-
} else {
112-
// FIXME: implement on vxWorks, Redox, HermitCore, Haiku, l4re
113-
fn num_cpus() -> usize {
114-
1
115-
}
16+
Err(..) => thread::available_concurrency().map(|n| n.get()).unwrap_or(1),
11617
}
11718
}

library/test/src/lib.rs

+1
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
#![feature(rustc_private)]
2525
#![feature(nll)]
2626
#![feature(bool_to_option)]
27+
#![feature(available_concurrency)]
2728
#![feature(set_stdio)]
2829
#![feature(panic_unwind)]
2930
#![feature(staged_api)]

0 commit comments

Comments
 (0)