libmicrohttpd2

HTTP server C library (MHD 2.x, alpha)
Log | Files | Refs | README | LICENSE

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 }