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