libmicrohttpd

HTTP/1.x server C library (MHD 1.x, stable)
Log | Files | Refs | Submodules | README | LICENSE

mhd_tool_get_cpu_count.c (29736B)


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