      1
      2
      3
      4
      5
      6
      7
      8
      9
     10
     11
     12
     13
     14
     15
     16
     17
     18
     19
     20
     21
     22
     23
     24
     25
     26
     27
     28
     29
     30
     31
     32
     33
     34
     35
     36
     37
     38
     39
     40
     41
     42
     43
     44
     45
     46
     47
     48
     49
     50
     51
     52
     53
     54
     55
     56
     57
     58
     59
     60
     61
     62
     63
     64
     65
     66
     67
     68
     69
     70
     71
     72
     73
     74
     75
     76
     77
     78
     79
     80
     81
     82
     83
     84
     85
     86
     87
     88
     89
     90
     91
     92
     93
     94
     95
     96
     97
     98
     99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
    143
    144
    145
    146
    147
    148
    149
    150
    151
    152
    153
    154
    155
    156

    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 = "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"))
            }
        }
    }