mhdtl_get_cpu_count.c (30655B)
1 /* SPDX-License-Identifier: LGPL-2.1-or-later OR (GPL-2.0-or-later WITH eCos-exception-2.0) */ 2 /* 3 This file is part of GNU libmicrohttpd. 4 Copyright (C) 2023-2025 Evgeny Grin (Karlson2k) 5 6 GNU libmicrohttpd is free software; you can redistribute it and/or 7 modify it under the terms of the GNU Lesser General Public 8 License as published by the Free Software Foundation; either 9 version 2.1 of the License, or (at your option) any later version. 10 11 GNU libmicrohttpd is distributed in the hope that it will be useful, 12 but WITHOUT ANY WARRANTY; without even the implied warranty of 13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14 Lesser General Public License for more details. 15 16 Alternatively, you can redistribute GNU libmicrohttpd and/or 17 modify it under the terms of the GNU General Public License as 18 published by the Free Software Foundation; either version 2 of 19 the License, or (at your option) any later version, together 20 with the eCos exception, as follows: 21 22 As a special exception, if other files instantiate templates or 23 use macros or inline functions from this file, or you compile this 24 file and link it with other works to produce a work based on this 25 file, this file does not by itself cause the resulting work to be 26 covered by the GNU General Public License. However the source code 27 for this file must still be made available in accordance with 28 section (3) of the GNU General Public License v2. 29 30 This exception does not invalidate any other reasons why a work 31 based on this file might be covered by the GNU General Public 32 License. 33 34 You should have received copies of the GNU Lesser General Public 35 License and the GNU General Public License along with this library; 36 if not, see <https://www.gnu.org/licenses/>. 37 */ 38 39 /** 40 * @file src/tools/mhdtl_get_cpu_count.c 41 * @brief Implementation of functions to detect the number of available 42 * CPU cores. 43 * @author Karlson2k (Evgeny Grin) 44 */ 45 46 #include "mhd_sys_options.h" 47 #include "mhdtl_get_cpu_count.h" 48 #ifdef HAVE_SYS_TYPES_H 49 # include <sys/types.h> 50 #endif /* HAVE_SYS_TYPES_H */ 51 #ifdef HAVE_SYS_PARAM_H 52 # include <sys/param.h> 53 #endif /* HAVE_SYS_PARAM_H */ 54 #ifdef HAVE_SYS__CPUSET_H 55 # include <sys/_cpuset.h> 56 #endif /* HAVE_SYS_PARAM_H */ 57 #ifdef HAVE_STDDEF_H 58 # include <stddef.h> 59 #endif /* HAVE_STDDEF_H */ 60 #ifdef HAVE_STRING_H 61 # include <string.h> 62 #endif /* HAVE_STRING_H */ 63 #ifdef HAVE_SYS_SYSCTL_H 64 # include <sys/sysctl.h> 65 #endif /* HAVE_SYS_SYSCTL_H */ 66 #ifdef HAVE_FEATURES_H 67 # include <features.h> 68 #endif /* HAVE_FEATURES_H */ 69 #ifdef HAVE_UNISTD_H 70 # include <unistd.h> 71 #endif /* HAVE_UNISTD_H */ 72 #ifdef HAVE_VXCPULIB_H 73 # include <vxCpuLib.h> 74 #endif 75 #ifdef HAVE_WINDOWS_H 76 # ifndef WIN32_LEAN_AND_MEAN 77 # define WIN32_LEAN_AND_MEAN 1 78 # endif /* ! WIN32_LEAN_AND_MEAN */ 79 # include <windows.h> 80 # ifndef ALL_PROCESSOR_GROUPS 81 # define ALL_PROCESSOR_GROUPS 0xFFFFu 82 # endif /* ALL_PROCESSOR_GROUPS */ 83 #elif defined(_WIN32) && ! defined (__CYGWIN__) 84 # error Windows headers are required for Windows build 85 #endif /* HAVE_WINDOWS_H */ 86 #ifdef HAVE_SCHED_H 87 # include <sched.h> 88 #endif /* HAVE_SCHED_H */ 89 #ifdef HAVE_SYS_CPUSET_H 90 # include <sys/cpuset.h> 91 #endif /* HAVE_SYS_CPUSET_H */ 92 #ifdef HAVE_STDBOOL_H 93 # include <stdbool.h> 94 #endif /* HAVE_STDBOOL_H */ 95 96 #if ! defined(HAS_DECL_CPU_SETSIZE) && ! defined(CPU_SETSIZE) 97 # define CPU_SETSIZE (1024) 98 # define CPU_SETSIZE_SAFE (64) 99 #else /* HAS_DECL_CPU_SETSIZE || CPU_SETSIZE */ 100 # define CPU_SETSIZE_SAFE CPU_SETSIZE 101 #endif /* HAS_DECL_CPU_SETSIZE || CPU_SETSIZE */ 102 103 /* Check and fix possible missing macros */ 104 #if ! defined(HAS_DECL_CTL_HW) && defined(CTL_HW) 105 # define HAS_DECL_CTL_HW 1 106 #endif /* ! HAS_DECL_CTL_HW && CTL_HW */ 107 108 #if ! defined(HAS_DECL_HW_NCPUONLINE) && defined(HW_NCPUONLINE) 109 # define HAS_DECL_HW_NCPUONLINE 1 110 #endif /* ! HAS_DECL_HW_NCPUONLINE && HW_NCPUONLINE */ 111 112 #if ! defined(HAS_DECL_HW_AVAILCPU) && defined(HW_AVAILCPU) 113 # define HAS_DECL_HW_AVAILCPU 1 114 #endif /* ! HAS_DECL_HW_AVAILCPU && HW_AVAILCPU */ 115 116 #if ! defined(HAS_DECL_HW_NCPU) && defined(HW_NCPU) 117 # define HAS_DECL_HW_NCPU 1 118 #endif /* ! HAS_DECL_HW_NCPU && HW_NCPU */ 119 120 #if ! defined(HAS_DECL__SC_NPROCESSORS_ONLN) && defined(_SC_NPROCESSORS_ONLN) 121 # define HAS_DECL__SC_NPROCESSORS_ONLN 1 122 #endif /* ! HAS_DECL__SC_NPROCESSORS_ONLN && _SC_NPROCESSORS_ONLN */ 123 124 #if ! defined(HAS_DECL__SC_NPROC_ONLN) && defined(_SC_NPROC_ONLN) 125 # define HAS_DECL__SC_NPROC_ONLN 1 126 #endif /* ! HAS_DECL__SC_NPROC_ONLN && _SC_NPROC_ONLN */ 127 128 #if ! defined(HAS_DECL__SC_CRAY_NCPU) && defined(_SC_CRAY_NCPU) 129 # define HAS_DECL__SC_CRAY_NCPU 1 130 #endif /* ! HAS_DECL__SC_CRAY_NCPU && _SC_CRAY_NCPU */ 131 132 #if ! defined(HAS_DECL__SC_NPROCESSORS_CONF) && defined(_SC_NPROCESSORS_CONF) 133 # define HAS_DECL__SC_NPROCESSORS_CONF 1 134 #endif /* ! HAVE_DECL__SC_NPROCESSORS_CONF && _SC_NPROCESSORS_CONF */ 135 136 /* Forward declarations */ 137 138 static int 139 mhdtl_get_sys_cpu_count_sysctl_ (void); 140 141 /** 142 * Detect the number of logical CPU cores available for the process by 143 * sched_getaffinity() (and related) function. 144 * @return the number of detected logical CPU cores or 145 * -1 if failed to detect (or this function unavailable). 146 */ 147 static int 148 mhdtl_get_proc_cpu_count_sched_getaffinity_ (void) 149 { 150 int ret = -1; 151 #if defined(HAVE_SCHED_GETAFFINITY) && defined(HAVE_GETPID) 152 /* Glibc style */ 153 if (0 >= ret) 154 { 155 cpu_set_t cur_set; 156 if (0 == sched_getaffinity (getpid (), sizeof (cur_set), &cur_set)) 157 { 158 #ifdef HAVE_CPU_COUNT 159 ret = CPU_COUNT (&cur_set); 160 #else /* ! HAVE_CPU_COUNT */ 161 unsigned int i; 162 ret = 0; 163 for (i = 0; i < CPU_SETSIZE_SAFE; ++i) 164 { 165 if (CPU_ISSET (i, &cur_set)) 166 ++ret; 167 } 168 if (0 == ret) 169 ret = -1; 170 #endif /* ! HAVE_CPU_COUNT */ 171 } 172 } 173 #ifdef HAVE_CPU_COUNT_S 174 if (0 >= ret) 175 { 176 /* Use 256 times larger size than size for default maximum CPU number. 177 Hopefully it would be enough even for exotic situations. */ 178 static const unsigned int set_size_cpus = 256 * CPU_SETSIZE; 179 const size_t set_size_bytes = CPU_ALLOC_SIZE (set_size_cpus); 180 cpu_set_t *p_set; 181 182 p_set = CPU_ALLOC (set_size_cpus); 183 if (NULL != p_set) 184 { 185 if (0 == sched_getaffinity (getpid (), set_size_bytes, p_set)) 186 { 187 #ifndef MHD_FUNC_CPU_COUNT_S_GETS_CPUS 188 ret = CPU_COUNT_S (set_size_bytes, p_set); 189 #else /* MHD_FUNC_CPU_COUNT_S_GETS_CPUS */ 190 ret = CPU_COUNT_S (set_size_cpus, p_set); 191 #endif /* MHD_FUNC_CPU_COUNT_S_GETS_CPUS */ 192 } 193 CPU_FREE (p_set); 194 } 195 } 196 #endif /* HAVE_CPU_COUNT_S */ 197 #endif /* HAVE_SCHED_GETAFFINITY && HAVE_GETPID */ 198 if (0 >= ret) 199 return -1; 200 return ret; 201 } 202 203 204 /** 205 * Detect the number of logical CPU cores available for the process by 206 * cpuset_getaffinity() function. 207 * @return the number of detected logical CPU cores or 208 * -1 if failed to detect (or this function unavailable). 209 */ 210 static int 211 mhdtl_get_proc_cpu_count_cpuset_getaffinity_ (void) 212 { 213 int ret = -1; 214 #if defined(HAVE_CPUSET_GETAFFINITY) 215 /* FreeBSD style */ 216 if (0 >= ret) 217 { 218 cpuset_t cur_mask; 219 /* The should get "anonymous" mask/set. The anonymous mask is always 220 a subset of the assigned set (which is a subset of the root set). */ 221 if (0 == cpuset_getaffinity (CPU_LEVEL_WHICH, CPU_WHICH_PID, (id_t) -1, 222 sizeof (cur_mask), &cur_mask)) 223 { 224 #ifdef HAVE_CPU_COUNT 225 ret = CPU_COUNT (&cur_mask); 226 #else /* ! HAVE_CPU_COUNT */ 227 unsigned int i; 228 ret = 0; 229 for (i = 0; i < CPU_SETSIZE_SAFE; ++i) 230 { 231 if (CPU_ISSET (i, &cur_mask)) 232 ++ret; 233 } 234 if (0 == ret) 235 ret = -1; 236 #endif /* ! HAVE_CPU_COUNT */ 237 } 238 } 239 #ifdef HAVE_CPU_COUNT_S 240 if (0 >= ret) 241 { 242 /* Use 256 times larger size than size for default maximum CPU number. 243 Hopefully it would be enough even for exotic situations. */ 244 static const unsigned int mask_size_cpus = 256 * CPU_SETSIZE; 245 const size_t mask_size_bytes = CPU_ALLOC_SIZE (mask_size_cpus); 246 cpuset_t *p_mask; 247 248 p_mask = CPU_ALLOC (mask_size_cpus); 249 if (NULL != p_mask) 250 { 251 if (0 == cpuset_getaffinity (CPU_LEVEL_WHICH, CPU_WHICH_PID, (id_t) -1, 252 mask_size_bytes, p_mask)) 253 { 254 #ifndef MHD_FUNC_CPU_COUNT_S_GETS_CPUS 255 ret = CPU_COUNT_S (mask_size_bytes, p_mask); 256 #else /* MHD_FUNC_CPU_COUNT_S_GETS_CPUS */ 257 ret = CPU_COUNT_S (mask_size_cpus, p_mask); 258 #endif /* MHD_FUNC_CPU_COUNT_S_GETS_CPUS */ 259 } 260 CPU_FREE (p_mask); 261 } 262 } 263 #endif /* HAVE_CPU_COUNT_S */ 264 #endif /* HAVE_CPUSET_GETAFFINITY */ 265 if (0 >= ret) 266 return -1; 267 return ret; 268 } 269 270 271 /** 272 * Detect the number of logical CPU cores available for the process by 273 * sched_getaffinity_np() (and related) function. 274 * @return the number of detected logical CPU cores or 275 * -1 if failed to detect (or this function unavailable). 276 */ 277 static int 278 mhdtl_get_proc_cpu_count_sched_getaffinity_np_ (void) 279 { 280 int ret = -1; 281 #if defined(HAVE_SCHED_GETAFFINITY_NP) && defined(HAVE_GETPID) 282 /* NetBSD style */ 283 cpuset_t *cpuset_ptr; 284 cpuset_ptr = cpuset_create (); 285 if (NULL != cpuset_ptr) 286 { 287 if (0 == sched_getaffinity_np (getpid (), cpuset_size (cpuset_ptr), 288 cpuset_ptr)) 289 { 290 cpuid_t cpu_num; 291 #if defined(HAVE_SYSCONF) && defined(HAVE_DECL__SC_NPROCESSORS_CONF) 292 unsigned int max_num = 0; 293 long sc_value; 294 sc_value = sysconf (_SC_NPROCESSORS_ONLN); 295 if (0 < sc_value) 296 max_num = (unsigned int) sc_value; 297 if (0 < max_num) 298 { 299 ret = 0; 300 for (cpu_num = 0; cpu_num < max_num; ++cpu_num) 301 if (0 < cpuset_isset (cpu_num, cpuset_ptr)) 302 ++ret; 303 } 304 else /* Combined with the next 'if' */ 305 #endif /* HAVE_SYSCONF && HAVE_DECL__SC_NPROCESSORS_CONF */ 306 if (1) 307 { 308 int res; 309 cpu_num = 0; 310 ret = 0; 311 do 312 { 313 res = cpuset_isset (cpu_num++, cpuset_ptr); 314 if (0 < res) 315 ++ret; 316 } while (0 <= res); 317 } 318 #ifdef __NetBSD__ 319 if (0 == ret) 320 { 321 /* On NetBSD "unset" affinity (exactly zero CPUs) means 322 "all CPUs are available". */ 323 ret = mhdtl_get_sys_cpu_count_sysctl_ (); 324 } 325 #endif /* __NetBSD__ */ 326 } 327 cpuset_destroy (cpuset_ptr); 328 } 329 #endif /* HAVE_SCHED_GETAFFINITY_NP && HAVE_GETPID */ 330 if (0 >= ret) 331 return -1; 332 return ret; 333 } 334 335 336 /** 337 * Detect the number of logical CPU cores available for the process by 338 * W32 API functions. 339 * @return the number of detected logical CPU cores or 340 * -1 if failed to detect (or this function unavailable). 341 */ 342 static int 343 mhdtl_get_proc_cpu_count_w32_ (void) 344 { 345 int ret = -1; 346 #if defined(_WIN32) && ! defined(__CYGWIN__) 347 /* W32 Native */ 348 /** 349 * Maximum used number of CPU groups. 350 * Improvement: Implement dynamic allocation when it would be reasonable 351 */ 352 #define MHDT_MAX_GROUP_COUNT 128 353 /** 354 * The count of logical CPUs as returned by GetProcessAffinityMask() 355 */ 356 int count_by_proc_aff_mask; 357 count_by_proc_aff_mask = -1; 358 if (1) 359 { 360 DWORD_PTR proc_aff; 361 DWORD_PTR sys_aff; 362 363 if (GetProcessAffinityMask (GetCurrentProcess (), &proc_aff, &sys_aff)) 364 { 365 /* Count all set bits */ 366 for (count_by_proc_aff_mask = 0; 0 != proc_aff; proc_aff &= proc_aff - 1) 367 ++count_by_proc_aff_mask; 368 } 369 } 370 if (0 < count_by_proc_aff_mask) 371 { 372 HMODULE k32hndl; 373 k32hndl = LoadLibraryA ("kernel32.dll"); 374 if (NULL != k32hndl) 375 { 376 typedef BOOL (WINAPI *GPGA_PTR)(HANDLE hProcess, 377 PUSHORT GroupCount, 378 PUSHORT GroupArray); 379 GPGA_PTR ptrGetProcessGroupAffinity; 380 ptrGetProcessGroupAffinity = 381 (GPGA_PTR) (void *) GetProcAddress (k32hndl, 382 "GetProcessGroupAffinity"); 383 if (NULL == ptrGetProcessGroupAffinity) 384 { 385 /* Windows version before Win7 */ 386 /* No processor groups supported, the process affinity mask gives full picture */ 387 ret = count_by_proc_aff_mask; 388 } 389 else 390 { 391 /* Windows version Win7 or later */ 392 /* Processor groups are supported */ 393 USHORT arr_elements = MHDT_MAX_GROUP_COUNT; 394 USHORT groups_arr[MHDT_MAX_GROUP_COUNT]; /* Hopefully should be enough */ 395 /* Improvement: Implement dynamic allocation when it would be reasonable */ 396 /** 397 * Exactly one processor group is assigned to the process 398 */ 399 bool single_cpu_group_assigned; /**< Exactly one processor group is assigned to the process */ 400 struct mhdt_GR_AFFINITY 401 { 402 KAFFINITY Mask; 403 WORD Group; 404 WORD Reserved[3]; 405 }; 406 typedef BOOL (WINAPI *GPDCSM_PTR)(HANDLE Process, 407 struct mhdt_GR_AFFINITY *CpuSetMasks, 408 USHORT CpuSetMaskCount, 409 USHORT *RequiredMaskCount); 410 GPDCSM_PTR ptrGetProcessDefaultCpuSetMasks; 411 bool win_fe_or_later; 412 bool cpu_set_mask_assigned; 413 414 single_cpu_group_assigned = false; 415 if (ptrGetProcessGroupAffinity (GetCurrentProcess (), &arr_elements, 416 groups_arr)) 417 { 418 if (1 == arr_elements) 419 { 420 /* Exactly one processor group assigned to the process */ 421 single_cpu_group_assigned = true; 422 #if 0 /* Disabled code */ 423 /* The value returned by GetThreadGroupAffinity() is not relevant as 424 for the new threads the process affinity mask is used. */ 425 ULONG_PTR proc_aff2; 426 typedef BOOL (WINAPI *GTGA_PTR)(HANDLE hThread, 427 struct mhdt_GR_AFFINITY * 428 GroupAffinity); 429 GTGA_PTR ptrGetThreadGroupAffinity; 430 ptrGetThreadGroupAffinity = 431 (GTGA_PTR) (void *) GetProcAddress (k32hndl, 432 "GetThreadGroupAffinity"); 433 if (NULL != ptrGetThreadGroupAffinity) 434 { 435 struct mhdt_GR_AFFINITY thr_gr_aff; 436 if (ptrGetThreadGroupAffinity (GetCurrentThread (), &thr_gr_aff)) 437 proc_aff2 = (ULONG_PTR) thr_gr_aff.Mask; 438 } 439 #endif /* Disabled code */ 440 } 441 } 442 ptrGetProcessDefaultCpuSetMasks = 443 (GPDCSM_PTR) (void *) GetProcAddress (k32hndl, 444 "GetProcessDefaultCpuSetMasks"); 445 if (NULL != ptrGetProcessDefaultCpuSetMasks) 446 { 447 /* This is Iron Release / Codename Fe 448 (also know as Windows 11 and Windows Server 2022) 449 or later version */ 450 struct mhdt_GR_AFFINITY gr_affs[MHDT_MAX_GROUP_COUNT]; /* Hopefully should be enough */ 451 /* Improvement: Implement dynamic allocation when it would be reasonable */ 452 USHORT num_elm; 453 454 win_fe_or_later = true; 455 456 if (ptrGetProcessDefaultCpuSetMasks (GetCurrentProcess (), gr_affs, 457 sizeof (gr_affs) 458 / sizeof (gr_affs[0]), &num_elm)) 459 { 460 if (0 == num_elm) 461 { 462 /* No group mask set */ 463 cpu_set_mask_assigned = false; 464 } 465 else 466 cpu_set_mask_assigned = true; 467 } 468 else 469 cpu_set_mask_assigned = true; /* Assume the worst case */ 470 } 471 else 472 { 473 win_fe_or_later = false; 474 cpu_set_mask_assigned = false; 475 } 476 if (! win_fe_or_later) 477 { 478 /* The OS is not capable of distributing threads across different 479 processor groups. Results reported by GetProcessAffinityMask() 480 are relevant for the main processor group for the process. */ 481 ret = count_by_proc_aff_mask; 482 } 483 else 484 { 485 /* The of is capable of automatic threads distribution across 486 processor groups. */ 487 if (cpu_set_mask_assigned) 488 { 489 /* Assigned Default CpuSet Masks combines with "classic" 490 affinity in the not fully clear way. The combination 491 is not documented and this functionality could be changed 492 any moment. */ 493 ret = -1; 494 } 495 else 496 { 497 if (! single_cpu_group_assigned) 498 { 499 /* This is a multi processor group process on Win11 (or later). 500 Each processor group may have different affinity and 501 the OS has not API to get it. 502 For example, affinity to the main processor group could be 503 assigned by SetProcessAffinityMask() function, which converts 504 the process to the single-processor-group type, but if 505 SetThreadGroupAffinity() is called later and bind the thread 506 to another processor group, the process becomes multi-processor- 507 group again, however the initial affinity mask is still used 508 for the initial (main) processor group. There is no API to read 509 it. 510 It is also possible that processor groups have different number 511 of processors. */ 512 ret = -1; 513 } 514 else 515 { 516 /* Single-processor-group process on Win11 (or later) without 517 assigned Default CpuSet Masks. */ 518 ret = count_by_proc_aff_mask; 519 } 520 } 521 } 522 } 523 FreeLibrary (k32hndl); 524 } 525 } 526 #endif /* _WIN32 && ! __CYGWIN__ */ 527 if (0 >= ret) 528 return -1; 529 return ret; 530 } 531 532 533 /** 534 * Detect the number of logical CPU cores available for the process. 535 * The number of cores available for this process could be different from 536 * value of cores available on the system. The OS may have limit on number 537 * assigned/allowed cores for single process and process may have limited 538 * CPU affinity. 539 * @return the number of logical CPU cores available for the process or 540 * -1 if failed to detect 541 */ 542 int 543 mhdtl_get_proc_cpu_count (void) 544 { 545 int res; 546 547 #if defined(__linux__) || defined(__GLIBC__) 548 /* On Linux kernel try first 'sched_getaffinity()' as it should be 549 the native API. 550 Also try it first on other kernels if Glibc is used. */ 551 res = mhdtl_get_proc_cpu_count_sched_getaffinity_ (); 552 if (0 < res) 553 return res; 554 555 res = mhdtl_get_proc_cpu_count_cpuset_getaffinity_ (); 556 if (0 < res) 557 return res; 558 #else /* ! __linux__ && ! __GLIBC__ */ 559 /* On non-Linux kernels 'cpuset_getaffinity()' could be the native API, 560 while 'sched_getaffinity()' could be implemented in compatibility layer. */ 561 res = mhdtl_get_proc_cpu_count_cpuset_getaffinity_ (); 562 if (0 < res) 563 return res; 564 565 res = mhdtl_get_proc_cpu_count_sched_getaffinity_ (); 566 if (0 < res) 567 return res; 568 #endif /* ! __linux__ && ! __GLIBC__ */ 569 570 res = mhdtl_get_proc_cpu_count_sched_getaffinity_np_ (); 571 if (0 < res) 572 return res; 573 574 res = mhdtl_get_proc_cpu_count_w32_ (); 575 if (0 < res) 576 return res; 577 578 return -1; 579 } 580 581 582 /** 583 * Detect the number of processors by special API functions 584 * @return number of processors as returned by special API functions or 585 * -1 in case of error or special API functions unavailable 586 */ 587 static int 588 mhdtl_get_sys_cpu_count_special_api_ (void) 589 { 590 int ret = -1; 591 #ifdef HAVE_PSTAT_GETDYNAMIC 592 if (0 >= ret) 593 { 594 /* HP-UX things */ 595 struct pst_dynamic psd_data; 596 memset ((void *) &psd_data, 0, sizeof (psd_data)); 597 if (1 == pstat_getdynamic (&psd_data, sizeof (psd_data), (size_t) 1, 0)) 598 { 599 if (0 < psd_data.psd_proc_cnt) 600 ret = (int) psd_data.psd_proc_cnt; 601 } 602 } 603 #endif /* HAVE_PSTAT_GETDYNAMIC */ 604 #ifdef HAVE_VXCPUENABLEDGET 605 if (0 >= ret) 606 { 607 /* VxWorks */ 608 cpuset_t enb_set; 609 enb_set = vxCpuEnabledGet (); 610 /* Count set bits */ 611 for (ret = 0; 0 != enb_set; enb_set &= enb_set - 1) 612 ++ret; 613 } 614 #endif /* HAVE_VXCPUENABLEDGET */ 615 #if defined(_WIN32) && ! defined (__CYGWIN__) 616 if (0 >= ret) 617 { 618 /* Native W32 */ 619 HMODULE k32hndl; 620 k32hndl = LoadLibraryA ("kernel32.dll"); 621 if (NULL != k32hndl) 622 { 623 typedef DWORD (WINAPI *GAPC_PTR)(WORD GroupNumber); 624 GAPC_PTR ptrGetActiveProcessorCount; 625 /* Available on W7 or later */ 626 ptrGetActiveProcessorCount = 627 (GAPC_PTR) (void *) GetProcAddress (k32hndl, "GetActiveProcessorCount"); 628 if (NULL != ptrGetActiveProcessorCount) 629 { 630 DWORD res; 631 res = ptrGetActiveProcessorCount (ALL_PROCESSOR_GROUPS); 632 ret = (int) res; 633 if (res != (DWORD) ret) 634 ret = -1; /* Overflow */ 635 } 636 } 637 if ((0 >= ret) && (NULL != k32hndl)) 638 { 639 typedef void (WINAPI *GNSI_PTR)(SYSTEM_INFO *pSysInfo); 640 GNSI_PTR ptrGetNativeSystemInfo; 641 /* May give incorrect (low) result on versions from W7 to W11 642 when more then 64 CPUs are available */ 643 ptrGetNativeSystemInfo = 644 (GNSI_PTR) (void *) GetProcAddress (k32hndl, "GetNativeSystemInfo"); 645 if (NULL != ptrGetNativeSystemInfo) 646 { 647 SYSTEM_INFO sysInfo; 648 649 memset ((void *) &sysInfo, 0, sizeof (sysInfo)); 650 ptrGetNativeSystemInfo (&sysInfo); 651 ret = (int) sysInfo.dwNumberOfProcessors; 652 if (sysInfo.dwNumberOfProcessors != (DWORD) ret) 653 ret = -1; /* Overflow */ 654 } 655 } 656 if (NULL != k32hndl) 657 FreeLibrary (k32hndl); 658 } 659 if (0 >= ret) 660 { 661 /* May give incorrect (low) result on versions from W7 to W11 662 when more then 64 CPUs are available */ 663 SYSTEM_INFO sysInfo; 664 memset ((void *) &sysInfo, 0, sizeof (sysInfo)); 665 GetSystemInfo (&sysInfo); 666 ret = (int) sysInfo.dwNumberOfProcessors; 667 if (sysInfo.dwNumberOfProcessors != (DWORD) ret) 668 ret = -1; /* Overflow */ 669 } 670 #endif /* _WIN32 && ! __CYGWIN__ */ 671 if (0 >= ret) 672 return -1; 673 return ret; 674 } 675 676 677 /** 678 * Detect the number of processors by sysctl*() functions in reliable way. 679 * 680 * This function uses reliable identificators that coreponds to actual 681 * number of CPU cores online currently. 682 * @return number of processors as returned by 'sysctl*' functions or 683 * -1 in case of error or the number cannot be detected 684 * by these functions 685 */ 686 static int 687 mhdtl_get_sys_cpu_count_sysctl_ (void) 688 { 689 int ret = -1; 690 /* Do not use sysctl() function on GNU/Linux even if 691 sysctl() is available */ 692 #ifndef __linux__ 693 #ifdef HAVE_SYSCTLBYNAME 694 if (0 >= ret) 695 { 696 size_t value_size = sizeof (ret); 697 /* Darwin: The number of available logical CPUs */ 698 if ((0 != sysctlbyname ("hw.logicalcpu", &ret, &value_size, 699 NULL, 0)) 700 || (sizeof (ret) != value_size)) 701 ret = -1; 702 } 703 if (0 >= ret) 704 { 705 size_t value_size = sizeof (ret); 706 /* FreeBSD: The number of online CPUs */ 707 if ((0 != sysctlbyname ("kern.smp.cpus", &ret, &value_size, 708 NULL, 0)) 709 || (sizeof (ret) != value_size)) 710 ret = -1; 711 } 712 if (0 >= ret) 713 { 714 size_t value_size = sizeof (ret); 715 /* Darwin: The current number of CPUs available to run threads */ 716 if ((0 != sysctlbyname ("hw.activecpu", &ret, &value_size, 717 NULL, 0)) 718 || (sizeof (ret) != value_size)) 719 ret = -1; 720 } 721 if (0 >= ret) 722 { 723 size_t value_size = sizeof (ret); 724 /* OpenBSD, NetBSD: The number of online CPUs */ 725 if ((0 != sysctlbyname ("hw.ncpuonline", &ret, &value_size, 726 NULL, 0)) 727 || (sizeof (ret) != value_size)) 728 ret = -1; 729 } 730 if (0 >= ret) 731 { 732 size_t value_size = sizeof (ret); 733 /* Darwin: The old/alternative name for "hw.activecpu" */ 734 if ((0 != sysctlbyname ("hw.availcpu", &ret, &value_size, 735 NULL, 0)) 736 || (sizeof (ret) != value_size)) 737 ret = -1; 738 } 739 #endif /* HAVE_SYSCTLBYNAME */ 740 #if defined(HAVE_SYSCTL) && \ 741 defined(HAS_DECL_CTL_HW) && \ 742 defined(HAS_DECL_HW_NCPUONLINE) 743 if (0 >= ret) 744 { 745 /* OpenBSD, NetBSD: The number of online CPUs */ 746 int mib[2] = {CTL_HW, HW_NCPUONLINE}; 747 size_t value_size = sizeof (ret); 748 if ((0 != sysctl (mib, 2, &ret, &value_size, NULL, 0)) 749 || (sizeof (ret) != value_size)) 750 ret = -1; 751 } 752 #endif /* HAVE_SYSCTL && HAS_DECL_CTL_HW && HAS_DECL_HW_NCPUONLINE */ 753 #if defined(HAVE_SYSCTL) && \ 754 defined(HAS_DECL_CTL_HW) && \ 755 defined(HAS_DECL_HW_AVAILCPU) 756 if (0 >= ret) 757 { 758 /* Darwin: The MIB name for "hw.activecpu" */ 759 int mib[2] = {CTL_HW, HW_AVAILCPU}; 760 size_t value_size = sizeof (ret); 761 if ((0 != sysctl (mib, 2, &ret, &value_size, NULL, 0)) 762 || (sizeof (ret) != value_size)) 763 ret = -1; 764 } 765 #endif /* HAVE_SYSCTL && HAS_DECL_CTL_HW && HAS_DECL_HW_AVAILCPU */ 766 #endif /* ! __linux__ */ 767 if (0 >= ret) 768 return -1; 769 return ret; 770 } 771 772 773 /** 774 * Detect the number of processors by sysctl*() functions, using fallback way. 775 * 776 * This function uses less reliable (compared to 777 * #mhd_tool_get_sys_cpu_count_sysctl_()) ways to detect the number of 778 * available CPU cores and may return values corresponding to the number of 779 * physically available (but possibly not used by the kernel) CPU logical cores. 780 * @return number of processors as returned by 'sysctl*' functions or 781 * -1 in case of error or the number cannot be detected 782 * by these functions 783 */ 784 static int 785 mhdtl_get_sys_cpu_count_sysctl_fallback_ (void) 786 { 787 int ret = -1; 788 /* Do not use sysctl() function on GNU/Linux even if 789 sysctl() is available */ 790 #ifndef __linux__ 791 #ifdef HAVE_SYSCTLBYNAME 792 if (0 >= ret) 793 { 794 size_t value_size = sizeof (ret); 795 /* FreeBSD, OpenBSD, NetBSD, Darwin (and others?): The number of CPUs */ 796 if ((0 != sysctlbyname ("hw.ncpu", &ret, &value_size, 797 NULL, 0)) 798 || (sizeof (ret) != value_size)) 799 ret = -1; 800 } 801 #endif /* HAVE_SYSCTLBYNAME */ 802 #if defined(HAVE_SYSCTL) && \ 803 defined(HAS_DECL_CTL_HW) && \ 804 defined(HAS_DECL_HW_NCPU) 805 if (0 >= ret) 806 { 807 /* FreeBSD, OpenBSD, NetBSD, Darwin (and others?): The number of CPUs */ 808 int mib[2] = {CTL_HW, HW_NCPU}; 809 size_t value_size = sizeof (ret); 810 if ((0 != sysctl (mib, 2, &ret, &value_size, NULL, 0)) 811 || (sizeof (ret) != value_size)) 812 ret = -1; 813 } 814 #endif /* HAVE_SYSCTL && HAS_DECL_CTL_HW && HAS_DECL_HW_NCPU */ 815 #endif /* ! __linux__ */ 816 if (0 >= ret) 817 return -1; 818 return ret; 819 } 820 821 822 /** 823 * Detect the number of processors by sysconf() function in reliable way. 824 * 825 * This function uses reliable identificators that coreponds to actual 826 * number of CPU cores online currently. 827 * @return number of processors as returned by 'sysconf' function or 828 * -1 in case of error or 'sysconf' unavailable 829 */ 830 static int 831 mhdtl_get_sys_cpu_count_sysconf_ (void) 832 { 833 int ret = -1; 834 #if defined(HAVE_SYSCONF) && \ 835 (defined(HAS_DECL__SC_NPROCESSORS_ONLN) || defined(HAS_DECL__SC_NPROC_ONLN)) 836 long value = -1; 837 #ifdef HAS_DECL__SC_NPROCESSORS_ONLN 838 if (0 >= value) 839 value = sysconf (_SC_NPROCESSORS_ONLN); 840 #endif /* HAS_DECL__SC_NPROCESSORS_ONLN */ 841 #ifdef HAS_DECL__SC_NPROC_ONLN 842 if (0 >= value) 843 value = sysconf (_SC_NPROC_ONLN); 844 #endif /* HAS_DECL__SC_NPROC_ONLN */ 845 if (0 >= value) 846 return -1; 847 ret = (int) value; 848 if ((long) ret != value) 849 return -1; /* Overflow */ 850 #endif /* HAVE_SYSCONF && 851 (HAS_DECL__SC_NPROCESSORS_ONLN || HAS_DECL__SC_NPROC_ONLN) */ 852 return ret; 853 } 854 855 856 /** 857 * Detect the number of processors by sysconf() function, using fallback way. 858 * 859 * This function uses less reliable (compared to 860 * #mhd_tool_get_sys_cpu_count_sysconf_()) ways to detect the number of 861 * available CPU cores and may return values corresponding to the number of 862 * physically available (but possibly not used by the kernel) CPU logical cores. 863 * @return number of processors as returned by 'sysconf' function or 864 * -1 in case of error or 'sysconf' unavailable 865 */ 866 static int 867 mhdtl_get_sys_cpu_count_sysconf_fallback_ (void) 868 { 869 int ret = -1; 870 #if defined(HAVE_SYSCONF) && \ 871 (defined(HAS_DECL__SC_CRAY_NCPU) || defined(HAS_DECL__SC_NPROCESSORS_CONF)) 872 long value = -1; 873 #ifdef HAS_DECL__SC_CRAY_NCPU 874 if (0 >= value) 875 value = sysconf (_SC_CRAY_NCPU); 876 #endif /* HAS_DECL__SC_CRAY_NCPU */ 877 #ifdef HAS_DECL__SC_NPROCESSORS_CONF 878 if (0 >= value) 879 value = sysconf (_SC_NPROCESSORS_CONF); 880 #endif /* HAS_DECL__SC_NPROCESSORS_CONF */ 881 if (0 >= value) 882 return -1; 883 ret = (int) value; 884 if ((long) ret != value) 885 return -1; /* Overflow */ 886 #endif /* HAVE_SYSCONF && 887 (HAS_DECL__SC_CRAY_NCPU || HAS_DECL__SC_NPROCESSORS_CONF) */ 888 return ret; 889 } 890 891 892 /** 893 * Try to detect the number of logical CPU cores available for the system. 894 * The number of available logical CPU cores could be changed any time due to 895 * CPU hotplug. 896 * @return the number of logical CPU cores available, 897 * -1 if failed to detect. 898 */ 899 int 900 mhdtl_get_system_cpu_count (void) 901 { 902 int res; 903 904 /* Try specialised APIs first */ 905 res = mhdtl_get_sys_cpu_count_special_api_ (); 906 if (0 < res) 907 return res; 908 909 /* Try sysctl*(). This is typically a direct interface to 910 kernel values. */ 911 res = mhdtl_get_sys_cpu_count_sysctl_ (); 912 if (0 < res) 913 return res; 914 915 /* Try sysconf() as the last resort as this is a generic interface 916 which can be implemented by parsing system files. */ 917 res = mhdtl_get_sys_cpu_count_sysconf_ (); 918 #if ! defined(__linux__) && ! defined(__GLIBC__) 919 if (0 < res) 920 return res; 921 #else /* __linux__ || __GLIBC__ */ 922 if (2 < res) 923 return res; 924 if (0 < res) 925 { 926 /* '1' or '2' could a be fallback number. 927 * See get_nprocs_fallback() in glibc 928 sysdeps/unix/sysv/linux/getsysstats.c */ 929 930 int proc_cpu_count; 931 932 proc_cpu_count = mhdtl_get_proc_cpu_count (); 933 if (proc_cpu_count == res) 934 { 935 /* The detected number of CPUs available for the process 936 is equal to the detected number of system CPUs. 937 Assume detected number is correct. */ 938 return res; 939 } 940 } 941 #endif /* __linux__ || __GLIBC__ */ 942 943 /* Try available fallbacks */ 944 945 res = mhdtl_get_sys_cpu_count_sysctl_fallback_ (); 946 if (0 < res) 947 return res; 948 949 res = mhdtl_get_sys_cpu_count_sysconf_fallback_ (); 950 #if ! defined(__linux__) && ! defined(__GLIBC__) 951 if (0 < res) 952 return res; 953 #else /* __linux__ || __GLIBC__ */ 954 if (2 < res) 955 return res; 956 #endif /* __linux__ || __GLIBC__ */ 957 958 return -1; /* Cannot detect */ 959 }