1#![cfg_attr(test, deny(warnings))]
30#![deny(missing_docs)]
31#![allow(non_snake_case)]
32
33#[cfg(not(windows))]
34extern crate libc;
35
36#[cfg(target_os = "hermit")]
37extern crate hermit_abi;
38
39#[cfg(target_os = "linux")]
40mod linux;
41#[cfg(target_os = "linux")]
42use linux::{get_num_cpus, get_num_physical_cpus};
43
44#[inline]
73pub fn get() -> usize {
74 get_num_cpus()
75}
76
77#[inline]
108pub fn get_physical() -> usize {
109 get_num_physical_cpus()
110}
111
112#[cfg(not(any(
113 target_os = "linux",
114 target_os = "windows",
115 target_os = "macos",
116 target_os = "openbsd",
117 target_os = "aix"
118)))]
119#[inline]
120fn get_num_physical_cpus() -> usize {
121 get_num_cpus()
123}
124
125#[cfg(target_os = "windows")]
126fn get_num_physical_cpus() -> usize {
127 match get_num_physical_cpus_windows() {
128 Some(num) => num,
129 None => get_num_cpus(),
130 }
131}
132
133#[cfg(target_os = "windows")]
134fn get_num_physical_cpus_windows() -> Option<usize> {
135 use std::{mem, ptr};
138
139 #[allow(non_upper_case_globals)]
140 const RelationProcessorCore: u32 = 0;
141
142 #[repr(C)]
143 #[allow(non_camel_case_types)]
144 struct SYSTEM_LOGICAL_PROCESSOR_INFORMATION {
145 mask: usize,
146 relationship: u32,
147 _unused: [u64; 2],
148 }
149
150 extern "system" {
151 fn GetLogicalProcessorInformation(info: *mut SYSTEM_LOGICAL_PROCESSOR_INFORMATION, length: &mut u32) -> u32;
152 }
153
154 let mut needed_size = 0;
158
159 unsafe {
160 GetLogicalProcessorInformation(ptr::null_mut(), &mut needed_size);
161 }
162
163 let struct_size = mem::size_of::<SYSTEM_LOGICAL_PROCESSOR_INFORMATION>() as u32;
164
165 if needed_size == 0 || needed_size < struct_size || needed_size % struct_size != 0 {
167 return None;
168 }
169
170 let count = needed_size / struct_size;
171
172 let mut buf = Vec::with_capacity(count as usize);
174
175 let result;
176
177 unsafe {
178 result = GetLogicalProcessorInformation(buf.as_mut_ptr(), &mut needed_size);
179 }
180
181 if result == 0 {
183 return None;
184 }
185
186 let count = needed_size / struct_size;
187
188 unsafe {
189 buf.set_len(count as usize);
190 }
191
192 let phys_proc_count = buf
193 .iter()
194 .filter(|proc_info| proc_info.relationship == RelationProcessorCore)
196 .count();
197
198 if phys_proc_count == 0 {
199 None
200 } else {
201 Some(phys_proc_count)
202 }
203}
204
205#[cfg(windows)]
206fn get_num_cpus() -> usize {
207 #[repr(C)]
208 struct SYSTEM_INFO {
209 wProcessorArchitecture: u16,
210 wReserved: u16,
211 dwPageSize: u32,
212 lpMinimumApplicationAddress: *mut u8,
213 lpMaximumApplicationAddress: *mut u8,
214 dwActiveProcessorMask: *mut u8,
215 dwNumberOfProcessors: u32,
216 dwProcessorType: u32,
217 dwAllocationGranularity: u32,
218 wProcessorLevel: u16,
219 wProcessorRevision: u16,
220 }
221
222 extern "system" {
223 fn GetSystemInfo(lpSystemInfo: *mut SYSTEM_INFO);
224 }
225
226 unsafe {
227 let mut sysinfo: SYSTEM_INFO = std::mem::zeroed();
228 GetSystemInfo(&mut sysinfo);
229 sysinfo.dwNumberOfProcessors as usize
230 }
231}
232
233#[cfg(any(target_os = "freebsd", target_os = "dragonfly", target_os = "netbsd"))]
234fn get_num_cpus() -> usize {
235 use std::ptr;
236
237 let mut cpus: libc::c_uint = 0;
238 let mut cpus_size = std::mem::size_of_val(&cpus);
239
240 unsafe {
241 cpus = libc::sysconf(libc::_SC_NPROCESSORS_ONLN) as libc::c_uint;
242 }
243 if cpus < 1 {
244 let mut mib = [libc::CTL_HW, libc::HW_NCPU, 0, 0];
245 unsafe {
246 libc::sysctl(
247 mib.as_mut_ptr(),
248 2,
249 &mut cpus as *mut _ as *mut _,
250 &mut cpus_size as *mut _ as *mut _,
251 ptr::null_mut(),
252 0,
253 );
254 }
255 if cpus < 1 {
256 cpus = 1;
257 }
258 }
259 cpus as usize
260}
261
262#[cfg(target_os = "openbsd")]
263fn get_num_cpus() -> usize {
264 use std::ptr;
265
266 let mut cpus: libc::c_uint = 0;
267 let mut cpus_size = std::mem::size_of_val(&cpus);
268 let mut mib = [libc::CTL_HW, libc::HW_NCPUONLINE, 0, 0];
269 let rc: libc::c_int;
270
271 unsafe {
272 rc = libc::sysctl(
273 mib.as_mut_ptr(),
274 2,
275 &mut cpus as *mut _ as *mut _,
276 &mut cpus_size as *mut _ as *mut _,
277 ptr::null_mut(),
278 0,
279 );
280 }
281 if rc < 0 {
282 cpus = 1;
283 }
284 cpus as usize
285}
286
287#[cfg(target_os = "openbsd")]
288fn get_num_physical_cpus() -> usize {
289 use std::ptr;
290
291 let mut cpus: libc::c_uint = 0;
292 let mut cpus_size = std::mem::size_of_val(&cpus);
293 let mut mib = [libc::CTL_HW, libc::HW_NCPU, 0, 0];
294 let rc: libc::c_int;
295
296 unsafe {
297 rc = libc::sysctl(
298 mib.as_mut_ptr(),
299 2,
300 &mut cpus as *mut _ as *mut _,
301 &mut cpus_size as *mut _ as *mut _,
302 ptr::null_mut(),
303 0,
304 );
305 }
306 if rc < 0 {
307 cpus = 1;
308 }
309 cpus as usize
310}
311
312#[cfg(target_os = "macos")]
313fn get_num_physical_cpus() -> usize {
314 use std::{ffi::CStr, ptr};
315
316 let mut cpus: i32 = 0;
317 let mut cpus_size = std::mem::size_of_val(&cpus);
318
319 let sysctl_name = CStr::from_bytes_with_nul(b"hw.physicalcpu\0").expect("byte literal is missing NUL");
320
321 unsafe {
322 if 0 != libc::sysctlbyname(
323 sysctl_name.as_ptr(),
324 &mut cpus as *mut _ as *mut _,
325 &mut cpus_size as *mut _ as *mut _,
326 ptr::null_mut(),
327 0,
328 ) {
329 return get_num_cpus();
330 }
331 }
332 cpus as usize
333}
334
335#[cfg(target_os = "aix")]
336fn get_num_physical_cpus() -> usize {
337 match get_smt_threads_aix() {
338 Some(num) => get_num_cpus() / num,
339 None => get_num_cpus(),
340 }
341}
342
343#[cfg(target_os = "aix")]
344fn get_smt_threads_aix() -> Option<usize> {
345 let smt = unsafe { libc::getsystemcfg(libc::SC_SMT_TC) };
346 if smt == u64::MAX {
347 return None;
348 }
349 Some(smt as usize)
350}
351
352#[cfg(any(
353 target_os = "macos",
354 target_os = "ios",
355 target_os = "android",
356 target_os = "aix",
357 target_os = "solaris",
358 target_os = "illumos",
359 target_os = "fuchsia"
360))]
361fn get_num_cpus() -> usize {
362 #[cfg(any(target_arch = "arm", target_arch = "aarch64"))]
365 const CONF_NAME: libc::c_int = libc::_SC_NPROCESSORS_CONF;
366 #[cfg(not(any(target_arch = "arm", target_arch = "aarch64")))]
367 const CONF_NAME: libc::c_int = libc::_SC_NPROCESSORS_ONLN;
368
369 let cpus = unsafe { libc::sysconf(CONF_NAME) };
370 if cpus < 1 { 1 } else { cpus as usize }
371}
372
373#[cfg(target_os = "haiku")]
374fn get_num_cpus() -> usize {
375 use std::mem;
376
377 #[allow(non_camel_case_types)]
378 type bigtime_t = i64;
379 #[allow(non_camel_case_types)]
380 type status_t = i32;
381
382 #[repr(C)]
383 pub struct system_info {
384 pub boot_time: bigtime_t,
385 pub cpu_count: u32,
386 pub max_pages: u64,
387 pub used_pages: u64,
388 pub cached_pages: u64,
389 pub block_cache_pages: u64,
390 pub ignored_pages: u64,
391 pub needed_memory: u64,
392 pub free_memory: u64,
393 pub max_swap_pages: u64,
394 pub free_swap_pages: u64,
395 pub page_faults: u32,
396 pub max_sems: u32,
397 pub used_sems: u32,
398 pub max_ports: u32,
399 pub used_ports: u32,
400 pub max_threads: u32,
401 pub used_threads: u32,
402 pub max_teams: u32,
403 pub used_teams: u32,
404 pub kernel_name: [::std::os::raw::c_char; 256usize],
405 pub kernel_build_date: [::std::os::raw::c_char; 32usize],
406 pub kernel_build_time: [::std::os::raw::c_char; 32usize],
407 pub kernel_version: i64,
408 pub abi: u32,
409 }
410
411 extern "C" {
412 fn get_system_info(info: *mut system_info) -> status_t;
413 }
414
415 let mut info: system_info = unsafe { mem::zeroed() };
416 let status = unsafe { get_system_info(&mut info as *mut _) };
417 if status == 0 { info.cpu_count as usize } else { 1 }
418}
419
420#[cfg(target_os = "hermit")]
421fn get_num_cpus() -> usize {
422 unsafe { hermit_abi::get_processor_count() }
423}
424
425#[cfg(not(any(
426 target_os = "macos",
427 target_os = "ios",
428 target_os = "android",
429 target_os = "aix",
430 target_os = "solaris",
431 target_os = "illumos",
432 target_os = "fuchsia",
433 target_os = "linux",
434 target_os = "openbsd",
435 target_os = "freebsd",
436 target_os = "dragonfly",
437 target_os = "netbsd",
438 target_os = "haiku",
439 target_os = "hermit",
440 windows,
441)))]
442fn get_num_cpus() -> usize {
443 1
444}
445
446#[cfg(test)]
447mod tests {
448 fn env_var(name: &'static str) -> Option<usize> {
449 ::std::env::var(name).ok().map(|val| val.parse().unwrap())
450 }
451
452 #[test]
453 fn test_get() {
454 let num = super::get();
455 if let Some(n) = env_var("NUM_CPUS_TEST_GET") {
456 assert_eq!(num, n);
457 } else {
458 assert!(num > 0);
459 assert!(num < 236_451);
460 }
461 }
462
463 #[test]
464 fn test_get_physical() {
465 let num = super::get_physical();
466 if let Some(n) = env_var("NUM_CPUS_TEST_GET_PHYSICAL") {
467 assert_eq!(num, n);
468 } else {
469 assert!(num > 0);
470 assert!(num < 236_451);
471 }
472 }
473}