diff --git a/library/std/src/sys/hermit/thread.rs b/library/std/src/sys/hermit/thread.rs index f35a3a8a80f..3d00e626e1a 100644 --- a/library/std/src/sys/hermit/thread.rs +++ b/library/std/src/sys/hermit/thread.rs @@ -3,6 +3,7 @@ use crate::ffi::CStr; use crate::io; use crate::mem; +use crate::num::NonZeroUsize; use crate::sys::hermit::abi; use crate::sys::hermit::thread_local_dtor::run_dtors; use crate::time::Duration; @@ -95,6 +96,13 @@ impl Thread { } } +pub fn available_concurrency() -> io::Result { + Err(io::Error::new_const( + io::ErrorKind::NotFound, + &"The number of hardware threads is not known for the target platform", + )) +} + pub mod guard { pub type Guard = !; pub unsafe fn current() -> Option { diff --git a/library/std/src/sys/sgx/thread.rs b/library/std/src/sys/sgx/thread.rs index 67e2e8b59d3..b82ae86521a 100644 --- a/library/std/src/sys/sgx/thread.rs +++ b/library/std/src/sys/sgx/thread.rs @@ -1,6 +1,7 @@ #![cfg_attr(test, allow(dead_code))] // why is this necessary? use crate::ffi::CStr; use crate::io; +use crate::num::NonZeroUsize; use crate::time::Duration; use super::abi::usercalls; @@ -135,6 +136,13 @@ impl Thread { } } +pub fn available_concurrency() -> io::Result { + Err(io::Error::new_const( + io::ErrorKind::NotFound, + &"The number of hardware threads is not known for the target platform", + )) +} + pub mod guard { pub type Guard = !; pub unsafe fn current() -> Option { diff --git a/library/std/src/sys/unix/thread.rs b/library/std/src/sys/unix/thread.rs index b8f43caec32..e49971de940 100644 --- a/library/std/src/sys/unix/thread.rs +++ b/library/std/src/sys/unix/thread.rs @@ -2,6 +2,7 @@ use crate::cmp; use crate::ffi::CStr; use crate::io; use crate::mem; +use crate::num::NonZeroUsize; use crate::ptr; use crate::sys::{os, stack_overflow}; use crate::time::Duration; @@ -198,6 +199,88 @@ impl Drop for Thread { } } +pub fn available_concurrency() -> io::Result { + cfg_if::cfg_if! { + if #[cfg(any( + target_os = "android", + target_os = "emscripten", + target_os = "fuchsia", + target_os = "ios", + target_os = "linux", + target_os = "macos", + target_os = "solaris", + target_os = "illumos", + ))] { + match unsafe { libc::sysconf(libc::_SC_NPROCESSORS_ONLN) } { + -1 => Err(io::Error::last_os_error()), + 0 => Err(io::Error::new_const(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"))] { + 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_const(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")] { + 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_const(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, Haiku, l4re + Err(io::Error::new_const(io::ErrorKind::NotFound, &"The number of hardware threads is not known for the target platform")) + } + } +} + #[cfg(all( not(target_os = "linux"), not(target_os = "freebsd"), diff --git a/library/std/src/sys/unsupported/thread.rs b/library/std/src/sys/unsupported/thread.rs index cda8510e1ba..47005ead196 100644 --- a/library/std/src/sys/unsupported/thread.rs +++ b/library/std/src/sys/unsupported/thread.rs @@ -1,6 +1,7 @@ use super::unsupported; use crate::ffi::CStr; use crate::io; +use crate::num::NonZeroUsize; use crate::time::Duration; pub struct Thread(!); @@ -30,6 +31,13 @@ impl Thread { } } +pub fn available_concurrency() -> io::Result { + Err(io::Error::new_const( + io::ErrorKind::NotFound, + &"The number of hardware threads is not known for the target platform", + )) +} + pub mod guard { pub type Guard = !; pub unsafe fn current() -> Option { diff --git a/library/std/src/sys/wasi/thread.rs b/library/std/src/sys/wasi/thread.rs index 74515553a82..959077f7b54 100644 --- a/library/std/src/sys/wasi/thread.rs +++ b/library/std/src/sys/wasi/thread.rs @@ -3,6 +3,7 @@ use crate::ffi::CStr; use crate::io; use crate::mem; +use crate::num::NonZeroUsize; use crate::sys::unsupported; use crate::time::Duration; @@ -63,6 +64,13 @@ impl Thread { } } +pub fn available_concurrency() -> io::Result { + Err(io::Error::new_const( + io::ErrorKind::NotFound, + &"The number of hardware threads is not known for the target platform", + )) +} + pub mod guard { pub type Guard = !; pub unsafe fn current() -> Option { diff --git a/library/std/src/sys/wasm/atomics/thread.rs b/library/std/src/sys/wasm/atomics/thread.rs index 54bc877aa7d..8207a41035d 100644 --- a/library/std/src/sys/wasm/atomics/thread.rs +++ b/library/std/src/sys/wasm/atomics/thread.rs @@ -1,5 +1,6 @@ use crate::ffi::CStr; use crate::io; +use crate::num::NonZeroUsize; use crate::sys::unsupported; use crate::time::Duration; @@ -39,6 +40,13 @@ impl Thread { pub fn join(self) {} } +pub fn available_concurrency() -> io::Result { + Err(io::Error::new_const( + io::ErrorKind::NotFound, + &"The number of hardware threads is not known for the target platform", + )) +} + pub mod guard { pub type Guard = !; pub unsafe fn current() -> Option { diff --git a/library/std/src/sys/windows/c.rs b/library/std/src/sys/windows/c.rs index b64870401f1..193c28c7673 100644 --- a/library/std/src/sys/windows/c.rs +++ b/library/std/src/sys/windows/c.rs @@ -13,6 +13,7 @@ use libc::{c_void, size_t, wchar_t}; pub use self::EXCEPTION_DISPOSITION::*; pub use self::FILE_INFO_BY_HANDLE_CLASS::*; +pub type DWORD_PTR = ULONG_PTR; pub type DWORD = c_ulong; pub type NonZeroDWORD = NonZero_c_ulong; pub type HANDLE = LPVOID; @@ -53,6 +54,7 @@ pub type LPWSADATA = *mut WSADATA; pub type LPWSAPROTOCOL_INFO = *mut WSAPROTOCOL_INFO; pub type LPWSTR = *mut WCHAR; pub type LPFILETIME = *mut FILETIME; +pub type LPSYSTEM_INFO = *mut SYSTEM_INFO; pub type LPWSABUF = *mut WSABUF; pub type LPWSAOVERLAPPED = *mut c_void; pub type LPWSAOVERLAPPED_COMPLETION_ROUTINE = *mut c_void; @@ -533,6 +535,21 @@ pub struct FILETIME { pub dwHighDateTime: DWORD, } +#[repr(C)] +pub struct SYSTEM_INFO { + pub wProcessorArchitecture: WORD, + pub wReserved: WORD, + pub dwPageSize: DWORD, + pub lpMinimumApplicationAddress: LPVOID, + pub lpMaximumApplicationAddress: LPVOID, + pub dwActiveProcessorMask: DWORD_PTR, + pub dwNumberOfProcessors: DWORD, + pub dwProcessorType: DWORD, + pub dwAllocationGranularity: DWORD, + pub wProcessorLevel: WORD, + pub wProcessorRevision: WORD, +} + #[repr(C)] pub struct OVERLAPPED { pub Internal: *mut c_ulong, @@ -934,6 +951,7 @@ extern "system" { pub fn GetModuleHandleW(lpModuleName: LPCWSTR) -> HMODULE; pub fn GetSystemTimeAsFileTime(lpSystemTimeAsFileTime: LPFILETIME); + pub fn GetSystemInfo(lpSystemInfo: LPSYSTEM_INFO); pub fn CreateEventW( lpEventAttributes: LPSECURITY_ATTRIBUTES, diff --git a/library/std/src/sys/windows/thread.rs b/library/std/src/sys/windows/thread.rs index 38839ea5e90..ef7a9733fd8 100644 --- a/library/std/src/sys/windows/thread.rs +++ b/library/std/src/sys/windows/thread.rs @@ -1,5 +1,6 @@ use crate::ffi::CStr; use crate::io; +use crate::num::NonZeroUsize; use crate::ptr; use crate::sys::c; use crate::sys::handle::Handle; @@ -98,6 +99,21 @@ impl Thread { } } +pub fn available_concurrency() -> io::Result { + let res = unsafe { + let mut sysinfo: c::SYSTEM_INFO = crate::mem::zeroed(); + c::GetSystemInfo(&mut sysinfo); + sysinfo.dwNumberOfProcessors as usize + }; + match res { + 0 => Err(io::Error::new_const( + io::ErrorKind::NotFound, + &"The number of hardware threads is not known for the target platform", + )), + cpus => Ok(unsafe { NonZeroUsize::new_unchecked(cpus) }), + } +} + #[cfg_attr(test, allow(dead_code))] pub mod guard { pub type Guard = !; diff --git a/library/std/src/thread/available_concurrency.rs b/library/std/src/thread/available_concurrency.rs deleted file mode 100644 index e8cdde88014..00000000000 --- a/library/std/src/thread/available_concurrency.rs +++ /dev/null @@ -1,156 +0,0 @@ -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 { - available_concurrency_internal() -} - -cfg_if::cfg_if! { - if #[cfg(windows)] { - #[allow(nonstandard_style)] - fn available_concurrency_internal() -> io::Result { - #[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_const(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 = "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 { - match unsafe { libc::sysconf(libc::_SC_NPROCESSORS_ONLN) } { - -1 => Err(io::Error::last_os_error()), - 0 => Err(io::Error::new_const(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 { - 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_const(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 { - 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_const(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 { - Err(io::Error::new_const(io::ErrorKind::NotFound, &"The number of hardware threads is not known for the target platform")) - } - } -} diff --git a/library/std/src/thread/mod.rs b/library/std/src/thread/mod.rs index 30d8c2a1b6f..f7e79141903 100644 --- a/library/std/src/thread/mod.rs +++ b/library/std/src/thread/mod.rs @@ -155,6 +155,7 @@ use crate::fmt; use crate::io; use crate::mem; use crate::num::NonZeroU64; +use crate::num::NonZeroUsize; use crate::panic; use crate::panicking; use crate::str; @@ -174,15 +175,9 @@ 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 @@ -1422,3 +1417,39 @@ fn _assert_sync_and_send() { _assert_both::>(); _assert_both::(); } + +/// 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 { + imp::available_concurrency() +}