aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorEvgeny Grin (Karlson2k) <k2k@narod.ru>2023-07-12 21:29:45 +0300
committerEvgeny Grin (Karlson2k) <k2k@narod.ru>2023-07-29 23:38:13 +0300
commite87d26c42902db03560ef2e71e7e656bfd32b5a7 (patch)
treec62cc4340e3eedc3c52c4904fb65bb47a2e7768b
parent8a6307e08c45672e10736ceed95cf0ee377123e0 (diff)
downloadlibmicrohttpd-e87d26c42902db03560ef2e71e7e656bfd32b5a7.tar.gz
libmicrohttpd-e87d26c42902db03560ef2e71e7e656bfd32b5a7.zip
Implemented proper detection of number of available CPU cores
-rw-r--r--configure.ac337
-rw-r--r--src/include/mhd_options.h76
-rw-r--r--src/tools/Makefile.am3
-rw-r--r--src/tools/mhd_tool_get_cpu_count.c658
-rw-r--r--src/tools/mhd_tool_get_cpu_count.h55
-rw-r--r--src/tools/perf_replies.c132
6 files changed, 1232 insertions, 29 deletions
diff --git a/configure.ac b/configure.ac
index 65a700fb..b9160692 100644
--- a/configure.ac
+++ b/configure.ac
@@ -3917,6 +3917,343 @@ AS_VAR_IF([enable_tools],["yes"],
3917 ) 3917 )
3918 ] 3918 ]
3919 ) 3919 )
3920 AC_CHECK_HEADERS([features.h sys/pstat.h vxCpuLib.h],[],[],[AC_INCLUDES_DEFAULT])
3921 AC_CHECK_DECLS(
3922 [_SC_NPROCESSORS_ONLN,_SC_NPROC_ONLN,_SC_CRAY_NCPU,_SC_NPROCESSORS_CONF,CTL_HW,HW_NCPUONLINE,HW_NCPU,HW_AVAILCPU],
3923 [],[],
3924 [[
3925#ifdef HAVE_SYS_TYPES_H
3926#include <sys/types.h>
3927#endif /* HAVE_SYS_TYPES_H */
3928#ifdef HAVE_SYS_PARAM_H
3929#include <sys/param.h>
3930#endif /* HAVE_SYS_PARAM_H */
3931#ifdef HAVE_SYS_SYSCTL_H
3932#include <sys/sysctl.h>
3933#endif /* HAVE_SYS_SYSCTL_H */
3934#ifdef HAVE_UNISTD_H
3935#include <unistd.h>
3936#endif /* HAVE_UNISTD_H */
3937 ]]
3938 )
3939 MHD_CHECK_FUNC([pstat_getdynamic],[[
3940#include <sys/param.h>
3941#include <sys/pstat.h>
3942 ]],
3943 [[
3944 struct pst_dynamic psd_data;
3945 i][f (1 != pstat_getdynamic(&psd_data, sizeof(psd_data), (size_t)1, 0))
3946 return 2;
3947 i][f (0 >= psd_data.psd_proc_cnt)
3948 return 3;
3949 ]]
3950 )
3951 MHD_CHECK_FUNC([vxCpuEnabledGet],[[#include <vxCpuLib.h>]],
3952 [[
3953 cpuset_t enb_set;
3954 enb_set = vxCpuEnabledGet();
3955 (void) enb_set;
3956 ]]
3957 )
3958 AC_CHECK_HEADERS([sched.h sys/_cpuset.h sys/cpuset.h],[],[],[AC_INCLUDES_DEFAULT])
3959 # glibc / Linux kernel
3960 MHD_CHECK_FUNC([getpid],[[
3961#ifdef HAVE_SYS_TYPES_H
3962#include <sys/types.h>
3963#endif /* HAVE_SYS_TYPES_H */
3964#include <unistd.h>
3965 ]],
3966 [[
3967 pid_t cur_pid;
3968 cur_pid = getpid();
3969 (void) &cur_pid; /* Mute possible warning */
3970 ]],
3971 [
3972 MHD_CHECK_FUNC([sched_getaffinity],[[
3973#ifdef HAVE_SYS_TYPES_H
3974#include <sys/types.h>
3975#endif /* HAVE_SYS_TYPES_H */
3976#include <unistd.h>
3977#include <sched.h>
3978 ]],
3979 [[
3980 cpu_set_t cur_set;
3981 i][f (0 != sched_getaffinity(getpid(), sizeof(cur_set), &cur_set))
3982 return 2;
3983 i][f (0 == CPU_SET (0, &cur_set))
3984 return 3; /* Actually this could be a valid result */
3985 ]],
3986 [
3987 MHD_CHECK_FUNC([CPU_COUNT],[[
3988#include <stddef.h>
3989#include <sched.h>
3990 ]],
3991 [[
3992 cpu_set_t cur_set;
3993 CPU_ZERO(&cur_set);
3994 i][f (0 != CPU_COUNT(&cur_set))
3995 return 2;
3996 ]],
3997 [
3998 MHD_CHECK_FUNC([CPU_COUNT_S],[[
3999#include <stddef.h>
4000#include <sched.h>
4001 ]],
4002 [[
4003 static const unsigned int set_size_cpus = 2048u;
4004 const size_t set_size_bytes = (size_t) CPU_ALLOC_SIZE(set_size_cpus);
4005 cpu_set_t *p_set;
4006 p_set = CPU_ALLOC(set_size_cpus);
4007 i][f (!p_set)
4008 return 2;
4009 CPU_ZERO_S(set_size_bytes, p_set);
4010 i][f (0 != CPU_COUNT_S(set_size_bytes, p_set))
4011 {
4012 CPU_FREE(p_set);
4013 return 3;
4014 }
4015 CPU_FREE(p_set);
4016 ]],
4017 [AC_CHECK_DECLS([CPU_SETSIZE],[],[],[#include <sched.h>])]
4018 )
4019 ]
4020 )
4021 ]
4022 )
4023 # NetBSD
4024 # Should work only with -lrt, but actually works without it.
4025 MHD_CHECK_FUNC([sched_getaffinity_np],[[
4026#ifdef HAVE_SYS_TYPES_H
4027#include <sys/types.h>
4028#endif /* HAVE_SYS_TYPES_H */
4029#include <unistd.h>
4030#include <sched.h>
4031 ]],
4032 [[
4033 cpuset_t *cpuset_ptr;
4034 cpuid_t cpu_num = 0;
4035 cpuset_ptr = cpuset_create();
4036 i][f (!cpuset_ptr)
4037 return 2;
4038 i][f (0 != sched_getaffinity_np(getpid(), cpuset_size(cpuset_ptr), cpuset_ptr))
4039 {
4040 cpuset_destroy(cpuset_ptr);
4041 return 3;
4042 }
4043 i][f (0 >= cpuset_isset(cpu_num, cpuset_ptr))
4044 {
4045 cpuset_destroy(cpuset_ptr);
4046 return 4; /* Actually this could be a valid result */
4047 }
4048 cpuset_destroy(cpuset_ptr);
4049 ]]
4050 )
4051 ]
4052 )
4053 # FreeBSD
4054 MHD_CHECK_FUNC([cpuset_getaffinity],[[
4055#ifdef HAVE_SYS_PARAM_H
4056#include <sys/param.h>
4057#endif /* HAVE_SYS_PARAM_H */
4058#include <sys/cpuset.h>
4059 ]],
4060 [[
4061 cpuset_t cur_mask;
4062 i][f (0 != cpuset_getaffinity(CPU_LEVEL_WHICH, CPU_WHICH_PID, (id_t) -1, sizeof(cur_mask), &cur_mask))
4063 return 2;
4064 ]],
4065 [
4066 MHD_CHECK_FUNC([CPU_COUNT],[[
4067#ifdef HAVE_SYS_PARAM_H
4068#include <sys/param.h>
4069#endif /* HAVE_SYS_PARAM_H */
4070#ifdef HAVE_SYS__CPUSET_H
4071#include <sys/_cpuset.h>
4072#endif /* HAVE_SYS_PARAM_H */
4073#include <sys/cpuset.h>
4074 ]],
4075 [[
4076 cpuset_t test_mask;
4077 CPU_ZERO(&test_mask);
4078 i][f (0 != CPU_COUNT(&test_mask))
4079 return 2;
4080 ]],
4081 [
4082 MHD_CHECK_FUNC([CPU_COUNT_S],[[
4083#ifdef HAVE_SYS_PARAM_H
4084#include <sys/param.h>
4085#endif /* HAVE_SYS_PARAM_H */
4086#ifdef HAVE_SYS__CPUSET_H
4087#include <sys/_cpuset.h>
4088#endif /* HAVE_SYS_PARAM_H */
4089#include <sys/cpuset.h>
4090 ]],
4091 [[
4092 static const unsigned int max_cpu_num = 2048u;
4093 cpuset_t *mask_ptr;
4094 mask_ptr = CPU_ALLOC(max_cpu_num);
4095 i][f (! mask_ptr)
4096 return 2;
4097 i][f (0 != cpuset_getaffinity(CPU_LEVEL_WHICH, CPU_WHICH_PID, (id_t) -1,
4098 CPU_ALLOC_SIZE(max_cpu_num), mask_ptr))
4099 {
4100 CPU_FREE(mask_ptr);
4101 return 3;
4102 }
4103 i][f (0 == CPU_COUNT_S(CPU_ALLOC_SIZE(max_cpu_num), &test_mask))
4104 {
4105 CPU_FREE(mask_ptr);
4106 return 4;
4107 }
4108 CPU_FREE(mask_ptr);
4109 ]]
4110 )
4111 ]
4112 )
4113 ]
4114 )
4115 AS_VAR_IF([mhd_cv_func_CPU_COUNT_S],["yes"],
4116 [
4117 AC_CACHE_CHECK([whether CPU_COUNT_S() expects max CPU number as 'size' parameter],[mhd_cv_func_cpu_count_s_cpus],
4118 [
4119 AS_VAR_IF([cross-compiling],["yes"],[mhd_cv_func_cpu_count_s_cpus="assuming no"],
4120 [
4121 AC_LINK_IFELSE(
4122 [
4123 AC_LANG_PROGRAM([[
4124#ifdef HAVE_SYS_TYPES_H
4125#include <sys/types.h>
4126#endif /* HAVE_SYS_TYPES_H */
4127#ifdef HAVE_SYS_PARAM_H
4128#include <sys/param.h>
4129#endif /* HAVE_SYS_PARAM_H */
4130#ifdef HAVE_SYS__CPUSET_H
4131#include <sys/_cpuset.h>
4132#endif /* HAVE_SYS_PARAM_H */
4133#ifdef HAVE_SYS_CPUSET_H
4134#include <sys/cpuset.h>
4135#endif /* HAVE_SYS_CPUSET_H */
4136#ifdef HAVE_STDDEF_H
4137#include <stddef.h>
4138#endif /* HAVE_STDDEF_H */
4139#ifdef HAVE_UNISTD_H
4140#include <unistd.h>
4141#endif /* HAVE_UNISTD_H */
4142#ifdef HAVE_SCHED_H
4143#include <sched.h>
4144#endif /* HAVE_SCHED_H */
4145#include <stdio.h>
4146
4147#ifdef HAVE_SCHED_GETAFFINITY
4148#define TEST_CPUSET_TYPE cpu_set_t
4149#else /* ! HAVE_SCHED_GETAFFINITY */
4150#define TEST_CPUSET_TYPE cpuset_t
4151#endif /* ! HAVE_SCHED_GETAFFINITY */
4152 ]],
4153 [[
4154 TEST_CPUSET_TYPE *p_testset;
4155 /* The size of the cpuset that is larger then the test cpuset (in CPUs) */
4156 static const unsigned int testset_size_oversized_cpus = (8 * sizeof(long)) * 1024;
4157 const size_t testset_size_oversized_bytes = CPU_ALLOC_SIZE(testset_size_oversized_cpus);
4158 /* The size of the test cpuset in number of CPUs */
4159 const unsigned int testset_size_cpus = (unsigned int) testset_size_oversized_bytes;
4160 /* The size of the test cpuset in bytes */
4161 const size_t testset_size_bytes = CPU_ALLOC_SIZE(testset_size_cpus);
4162 /* The last one CPU in the test set */
4163 const unsigned int test_cpu_num1 = testset_size_cpus - 1;
4164 /* The penultimate CPU in the test set */
4165 const unsigned int test_cpu_num2 = testset_size_cpus - 2;
4166 /* The CPU numbers that should be cleared */
4167 const unsigned int test_cpu_clear1 = testset_size_cpus - 3;
4168 const unsigned int test_cpu_clear2 = testset_size_cpus - 4;
4169 unsigned int count_res;
4170 int ret = 0;
4171
4172 /* Allocate oversize area to ensure that memory outside the buffer is not used */
4173 p_testset = CPU_ALLOC(testset_size_oversized_cpus);
4174 if (! p_testset)
4175 {
4176 fprintf (stderr, "Error allocating memory.\n");
4177 return 99;
4178 }
4179 /* Set the some CPU numbers and then clear them */
4180 CPU_SET_S(test_cpu_clear1, testset_size_bytes, p_testset);
4181 CPU_SET_S(test_cpu_clear2, testset_size_bytes, p_testset);
4182 CPU_ZERO_S(testset_size_bytes, p_testset);
4183 /* Set two CPUs on */
4184 CPU_SET_S(test_cpu_num1, testset_size_bytes, p_testset);
4185 CPU_SET_S(test_cpu_num2, testset_size_bytes, p_testset);
4186 count_res = (unsigned int) CPU_COUNT_S(testset_size_bytes, p_testset);
4187 if (0 == count_res)
4188 {
4189 fprintf (stderr, "Full cpuset cannot be read by CPU_COUNT_S() when using the number of bytes as the size parameter.\n");
4190 /* Set the some CPU numbers and then clear them */
4191 CPU_SET_S(test_cpu_clear1, testset_size_cpus, p_testset);
4192 CPU_SET_S(test_cpu_clear2, testset_size_cpus, p_testset);
4193 CPU_ZERO_S(testset_size_cpus, p_testset);
4194 /* Set two CPUs on */
4195 CPU_SET_S(test_cpu_num1, testset_size_cpus, p_testset);
4196 CPU_SET_S(test_cpu_num2, testset_size_cpus, p_testset);
4197 count_res = (unsigned int) CPU_COUNT_S(testset_size_cpus, p_testset);
4198 if (2 == count_res)
4199 {
4200 fprintf (stderr, "Full cpuset is read by CPU_COUNT_S() only when using the maximum CPU number as the size parameter.\n");
4201 ret = 3;
4202 }
4203 else
4204 {
4205 fprintf (stderr, "Wrong result returned by CPU_COUNT_S() when using the maximum CPU number as the size parameter.\n");
4206 fprintf (stderr, "Number of 'enabled' CPUs: 2\n");
4207 fprintf (stderr, "Number of counted CPUs: %u\n", count_res);
4208 fprintf (stderr, "CPU_COUNT_S() could be unreliable.\n");
4209 ret = 70;
4210 }
4211 }
4212 else if (2 == count_res)
4213 {
4214 fprintf (stderr, "Full cpuset is read by CPU_COUNT_S() when using the number of bytes as the size parameter.\n");
4215 }
4216 else
4217 {
4218 fprintf (stderr, "Wrong result returned by CPU_COUNT_S() when using the number of bytes as the size parameter.\n");
4219 fprintf (stderr, "Number of 'enabled' CPUs: 2\n");
4220 fprintf (stderr, "Number of counted CPUs: %u\n", count_res);
4221 fprintf (stderr, "CPU_COUNT_S() could be unreliable.\n");
4222 ret = 71;
4223 }
4224 CPU_FREE(p_testset);
4225 if (0 != ret)
4226 return ret;
4227 ]]
4228 )
4229 ],
4230 [
4231 AM_RUN_LOG([./conftest$EXEEXT])
4232 count_test_res=$?
4233 AS_IF([test $count_test_res -eq 0],[mhd_cv_func_cpu_count_s_cpus="no"],
4234 [test $count_test_res -eq 3],[mhd_cv_func_cpu_count_s_cpus="yes"],
4235 [
4236 AC_MSG_WARN([Unexpected value returned by CPU_COUNT_S() test program. Please report to ${PACKAGE_BUGREPORT}])
4237 mhd_cv_func_cpu_count_s_cpus="assuming no"
4238 ]
4239 )
4240 ],
4241 [
4242 AC_MSG_WARN([Cannot build CPU_COUNT_S() test program. Please report to ${PACKAGE_BUGREPORT}])
4243 mhd_cv_func_cpu_count_s_cpus="assuming no"
4244 ]
4245 )
4246 ]
4247 )
4248 ]
4249 )
4250 AS_VAR_IF([mhd_cv_func_cpu_count_s_cpus],["yes"],
4251 [AC_DEFINE([MHD_FUNC_CPU_COUNT_S_GETS_CPUS],[1],
4252 [Define to '1' if CPU_COUNT_S() function expects max CPU number as 'size' parameter])
4253 ]
4254 )
4255 ]
4256 )
3920 ] 4257 ]
3921) 4258)
3922 4259
diff --git a/src/include/mhd_options.h b/src/include/mhd_options.h
index a7fadfae..faefd328 100644
--- a/src/include/mhd_options.h
+++ b/src/include/mhd_options.h
@@ -192,5 +192,81 @@
192#endif /* MHD_HAVE_MHD_FUNC_ */ 192#endif /* MHD_HAVE_MHD_FUNC_ */
193#endif 193#endif
194 194
195/* Un-define some HAVE_DECL_* macro if they equal zero.
196 This should allow safely use #ifdef in the code.
197 Define HAS_DECL_* macros only if matching HAVE_DECL_* macro
198 has non-zero value. Unlike HAVE_DECL_*, macros HAS_DECL_*
199 cannot have zero value. */
200#ifdef HAVE_DECL__SC_NPROCESSORS_ONLN
201# if 0 == HAVE_DECL__SC_NPROCESSORS_ONLN
202# undef HAVE_DECL__SC_NPROCESSORS_ONLN
203# else /* 0 != HAVE_DECL__SC_NPROCESSORS_ONLN */
204# define HAS_DECL__SC_NPROCESSORS_ONLN 1
205# endif /* 0 != HAVE_DECL__SC_NPROCESSORS_ONLN */
206#endif /* HAVE_DECL__SC_NPROCESSORS_ONLN */
207
208#ifdef HAVE_DECL__SC_NPROCESSORS_CONF
209# if 0 == HAVE_DECL__SC_NPROCESSORS_CONF
210# undef HAVE_DECL__SC_NPROCESSORS_CONF
211# else /* 0 != HAVE_DECL__SC_NPROCESSORS_CONF */
212# define HAS_DECL__SC_NPROCESSORS_CONF 1
213# endif /* 0 != HAVE_DECL__SC_NPROCESSORS_CONF */
214#endif /* HAVE_DECL__SC_NPROCESSORS_CONF */
215
216#ifdef HAVE_DECL__SC_NPROC_ONLN
217# if 0 == HAVE_DECL__SC_NPROC_ONLN
218# undef HAVE_DECL__SC_NPROC_ONLN
219# else /* 0 != HAVE_DECL__SC_NPROC_ONLN */
220# define HAS_DECL__SC_NPROC_ONLN 1
221# endif /* 0 != HAVE_DECL__SC_NPROC_ONLN */
222#endif /* HAVE_DECL__SC_NPROC_ONLN */
223
224#ifdef HAVE_DECL__SC_CRAY_NCPU
225# if 0 == HAVE_DECL__SC_CRAY_NCPU
226# undef HAVE_DECL__SC_CRAY_NCPU
227# else /* 0 != HAVE_DECL__SC_CRAY_NCPU */
228# define HAS_DECL__SC_CRAY_NCPU 1
229# endif /* 0 != HAVE_DECL__SC_CRAY_NCPU */
230#endif /* HAVE_DECL__SC_CRAY_NCPU */
231
232#ifdef HAVE_DECL_CTL_HW
233# if 0 == HAVE_DECL_CTL_HW
234# undef HAVE_DECL_CTL_HW
235# else /* 0 != HAVE_DECL_CTL_HW */
236# define HAS_DECL_CTL_HW 1
237# endif /* 0 != HAVE_DECL_CTL_HW */
238#endif /* HAVE_DECL_CTL_HW */
239
240#ifdef HAVE_DECL_HW_NCPUONLINE
241# if 0 == HAVE_DECL_HW_NCPUONLINE
242# undef HAVE_DECL_HW_NCPUONLINE
243# else /* 0 != HAVE_DECL_HW_NCPUONLINE */
244# define HAS_DECL_HW_NCPUONLINE 1
245# endif /* 0 != HAVE_DECL_HW_NCPUONLINE */
246#endif /* HAVE_DECL_HW_NCPUONLINE */
247
248#ifdef HAVE_DECL_HW_AVAILCPU
249# if 0 == HAVE_DECL_HW_AVAILCPU
250# undef HAVE_DECL_HW_AVAILCPU
251# else /* 0 != HAVE_DECL_HW_AVAILCPU */
252# define HAS_DECL_HW_AVAILCPU 1
253# endif /* 0 != HAVE_DECL_HW_AVAILCPU */
254#endif /* HAVE_DECL_HW_AVAILCPU */
255
256#ifdef HAVE_DECL_HW_NCPU
257# if 0 == HAVE_DECL_HW_NCPU
258# undef HAVE_DECL_HW_NCPU
259# else /* 0 != HAVE_DECL_HW_NCPU */
260# define HAS_DECL_HW_NCPU 1
261# endif /* 0 != HAVE_DECL_HW_NCPU */
262#endif /* HAVE_DECL_HW_NCPU */
263
264#ifdef HAVE_DECL_CPU_SETSIZE
265# if 0 == HAVE_DECL_CPU_SETSIZE
266# undef HAVE_DECL_CPU_SETSIZE
267# else /* 0 != HAVE_DECL_CPU_SETSIZE */
268# define HAS_DECL_CPU_SETSIZE 1
269# endif /* 0 != HAVE_DECL_CPU_SETSIZE */
270#endif /* HAVE_DECL_CPU_SETSIZE */
195 271
196#endif /* MHD_OPTIONS_H */ 272#endif /* MHD_OPTIONS_H */
diff --git a/src/tools/Makefile.am b/src/tools/Makefile.am
index d9064969..da776afe 100644
--- a/src/tools/Makefile.am
+++ b/src/tools/Makefile.am
@@ -36,4 +36,5 @@ endif
36 36
37 37
38perf_replies_SOURCES = \ 38perf_replies_SOURCES = \
39 perf_replies.c mhd_tool_str_to_uint.h 39 perf_replies.c mhd_tool_str_to_uint.h \
40 mhd_tool_get_cpu_count.h mhd_tool_get_cpu_count.c
diff --git a/src/tools/mhd_tool_get_cpu_count.c b/src/tools/mhd_tool_get_cpu_count.c
new file mode 100644
index 00000000..81badf28
--- /dev/null
+++ b/src/tools/mhd_tool_get_cpu_count.c
@@ -0,0 +1,658 @@
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
74#if ! defined(HAS_DECL_CPU_SETSIZE) && ! defined(CPU_SETSIZE)
75# define CPU_SETSIZE (1024)
76# define CPU_SETSIZE_SAFE (64)
77#else /* HAS_DECL_CPU_SETSIZE || CPU_SETSIZE */
78# define CPU_SETSIZE_SAFE CPU_SETSIZE
79#endif /* HAS_DECL_CPU_SETSIZE || CPU_SETSIZE */
80
81/* Check and fix possible missing macros */
82#if ! defined(HAS_DECL_CTL_HW) && defined(CTL_HW)
83# define HAS_DECL_CTL_HW 1
84#endif /* ! HAS_DECL_CTL_HW && CTL_HW */
85
86#if ! defined(HAS_DECL_HW_NCPUONLINE) && defined(HW_NCPUONLINE)
87# define HAS_DECL_HW_NCPUONLINE 1
88#endif /* ! HAS_DECL_HW_NCPUONLINE && HW_NCPUONLINE */
89
90#if ! defined(HAS_DECL_HW_AVAILCPU) && defined(HW_AVAILCPU)
91# define HAS_DECL_HW_AVAILCPU 1
92#endif /* ! HAS_DECL_HW_AVAILCPU && HW_AVAILCPU */
93
94#if ! defined(HAS_DECL_HW_NCPU) && defined(HW_NCPU)
95# define HAS_DECL_HW_NCPU 1
96#endif /* ! HAS_DECL_HW_NCPU && HW_NCPU */
97
98#if ! defined(HAS_DECL__SC_NPROCESSORS_ONLN) && defined(_SC_NPROCESSORS_ONLN)
99# define HAS_DECL__SC_NPROCESSORS_ONLN 1
100#endif /* ! HAS_DECL__SC_NPROCESSORS_ONLN && _SC_NPROCESSORS_ONLN */
101
102#if ! defined(HAS_DECL__SC_NPROC_ONLN) && defined(_SC_NPROC_ONLN)
103# define HAS_DECL__SC_NPROC_ONLN 1
104#endif /* ! HAS_DECL__SC_NPROC_ONLN && _SC_NPROC_ONLN */
105
106#if ! defined(HAS_DECL__SC_CRAY_NCPU) && defined(_SC_CRAY_NCPU)
107# define HAS_DECL__SC_CRAY_NCPU 1
108#endif /* ! HAS_DECL__SC_CRAY_NCPU && _SC_CRAY_NCPU */
109
110#if ! defined(HAS_DECL__SC_NPROCESSORS_CONF) && defined(_SC_NPROCESSORS_CONF)
111# define HAS_DECL__SC_NPROCESSORS_CONF 1
112#endif /* ! HAVE_DECL__SC_NPROCESSORS_CONF && _SC_NPROCESSORS_CONF */
113
114/* Forward declarations */
115
116static int
117mhd_tool_get_sys_cpu_count_sysctl_ (void);
118
119/**
120 * Detect the number of logical CPU cores available for the process by
121 * sched_getaffinity() (and related) function.
122 * @return the number of detected logical CPU cores or
123 * -1 if failed to detect (or this function unavailable).
124 */
125static int
126mhd_tool_get_proc_cpu_count_sched_getaffinity_ (void)
127{
128 int ret = -1;
129#if defined(HAVE_SCHED_GETAFFINITY) && defined(HAVE_GETPID)
130 /* Glibc style */
131 if (0 >= ret)
132 {
133 cpu_set_t cur_set;
134 if (0 == sched_getaffinity (getpid (), sizeof(cur_set), &cur_set))
135 {
136#ifdef HAVE_CPU_COUNT
137 ret = CPU_COUNT (&cur_set);
138#else /* ! HAVE_CPU_COUNT */
139 unsigned int i;
140 ret = 0;
141 for (i = 0; i < CPU_SETSIZE_SAFE; ++i)
142 {
143 if (CPU_ISSET (i, &cur_set))
144 ++ret;
145 }
146 if (0 == ret)
147 ret = -1;
148#endif /* ! HAVE_CPU_COUNT */
149 }
150 }
151#ifdef HAVE_CPU_COUNT_S
152 if (0 >= ret)
153 {
154 /* Use 256 times larger size than size for default maximum CPU number.
155 Hopefully it would be enough even for exotic situations. */
156 static const unsigned int set_size_cpus = 256 * CPU_SETSIZE;
157 const size_t set_size_bytes = CPU_ALLOC_SIZE (set_size_cpus);
158 cpu_set_t *p_set;
159
160 p_set = CPU_ALLOC (set_size_cpus);
161 if (NULL != p_set)
162 {
163 if (0 == sched_getaffinity (getpid (), set_size_bytes, p_set))
164 {
165#ifndef MHD_FUNC_CPU_COUNT_S_GETS_CPUS
166 ret = CPU_COUNT_S (set_size_bytes, p_set);
167#else /* MHD_FUNC_CPU_COUNT_S_GETS_CPUS */
168 ret = CPU_COUNT_S (set_size_cpus, p_set);
169#endif /* MHD_FUNC_CPU_COUNT_S_GETS_CPUS */
170 }
171 CPU_FREE (p_set);
172 }
173 }
174#endif /* HAVE_CPU_COUNT_S */
175#endif /* HAVE_SCHED_GETAFFINITY && HAVE_GETPID */
176 if (0 >= ret)
177 return -1;
178 return ret;
179}
180
181
182/**
183 * Detect the number of logical CPU cores available for the process by
184 * cpuset_getaffinity() function.
185 * @return the number of detected logical CPU cores or
186 * -1 if failed to detect (or this function unavailable).
187 */
188static int
189mhd_tool_get_proc_cpu_count_cpuset_getaffinity_ (void)
190{
191 int ret = -1;
192#if defined(HAVE_CPUSET_GETAFFINITY)
193 /* FreeBSD style */
194 if (0 >= ret)
195 {
196 cpuset_t cur_mask;
197 /* The should get "anonymous" mask/set. The anonymous mask is always
198 a subset of the assigned set (which is a subset of the root set). */
199 if (0 == cpuset_getaffinity (CPU_LEVEL_WHICH, CPU_WHICH_PID, (id_t) -1,
200 sizeof(cur_mask), &cur_mask))
201 {
202#ifdef HAVE_CPU_COUNT
203 ret = CPU_COUNT (&cur_mask);
204#else /* ! HAVE_CPU_COUNT */
205 unsigned int i;
206 ret = 0;
207 for (i = 0; i < CPU_SETSIZE_SAFE; ++i)
208 {
209 if (CPU_ISSET (i, &cur_mask))
210 ++ret;
211 }
212 if (0 == ret)
213 ret = -1;
214#endif /* ! HAVE_CPU_COUNT */
215 }
216 }
217#ifdef HAVE_CPU_COUNT_S
218 if (0 >= ret)
219 {
220 /* Use 256 times larger size than size for default maximum CPU number.
221 Hopefully it would be enough even for exotic situations. */
222 static const unsigned int mask_size_cpus = 256 * CPU_SETSIZE;
223 const size_t mask_size_bytes = CPU_ALLOC_SIZE (mask_size_cpus);
224 cpuset_t *p_mask;
225
226 p_mask = CPU_ALLOC (mask_size_cpus);
227 if (NULL != p_mask)
228 {
229 if (0 == cpuset_getaffinity (CPU_LEVEL_WHICH, CPU_WHICH_PID, (id_t) -1,
230 mask_size_bytes, p_mask))
231 {
232#ifndef MHD_FUNC_CPU_COUNT_S_GETS_CPUS
233 ret = CPU_COUNT_S (mask_size_bytes, p_mask);
234#else /* MHD_FUNC_CPU_COUNT_S_GETS_CPUS */
235 ret = CPU_COUNT_S (mask_size_cpus, p_mask);
236#endif /* MHD_FUNC_CPU_COUNT_S_GETS_CPUS */
237 }
238 CPU_FREE (p_mask);
239 }
240 }
241#endif /* HAVE_CPU_COUNT_S */
242#endif /* HAVE_CPUSET_GETAFFINITY */
243 if (0 >= ret)
244 return -1;
245 return ret;
246}
247
248
249/**
250 * Detect the number of logical CPU cores available for the process by
251 * sched_getaffinity_np() (and related) function.
252 * @return the number of detected logical CPU cores or
253 * -1 if failed to detect (or this function unavailable).
254 */
255static int
256mhd_tool_get_proc_cpu_count_sched_getaffinity_np_ (void)
257{
258 int ret = -1;
259#if defined(HAVE_SCHED_GETAFFINITY_NP) && defined(HAVE_GETPID)
260 /* NetBSD style */
261 cpuset_t *cpuset_ptr;
262 cpuset_ptr = cpuset_create ();
263 if (NULL != cpuset_ptr)
264 {
265 if (0 == sched_getaffinity_np (getpid (), cpuset_size (cpuset_ptr),
266 cpuset_ptr))
267 {
268 cpuid_t cpu_num;
269#if defined(HAVE_SYSCONF) && defined(HAVE_DECL__SC_NPROCESSORS_CONF)
270 unsigned int max_num = 0;
271 long sc_value = -1;
272 sc_value = sysconf (_SC_NPROCESSORS_ONLN);
273 if (0 < sc_value)
274 max_num = (unsigned int) sc_value;
275 if (0 < max_num)
276 {
277 ret = 0;
278 for (cpu_num = 0; cpu_num < max_num; ++cpu_num)
279 if (0 < cpuset_isset (cpu_num, cpuset_ptr))
280 ++ret;
281 }
282 else /* Combined with the next 'if' */
283#endif /* HAVE_SYSCONF && HAVE_DECL__SC_NPROCESSORS_CONF */
284 if (1)
285 {
286 int res;
287 cpu_num = 0;
288 ret = 0;
289 do
290 {
291 res = cpuset_isset (cpu_num++, cpuset_ptr);
292 if (0 < res)
293 ++ret;
294 } while (0 <= res);
295 }
296#ifdef __NetBSD__
297 if (0 == ret)
298 {
299 /* On NetBSD "unset" affinity (exactly zero CPUs) means
300 "all CPUs are available". */
301 ret = mhd_tool_get_sys_cpu_count_sysctl_ ();
302 }
303#endif /* __NetBSD__ */
304 }
305 cpuset_destroy (cpuset_ptr);
306 }
307#endif /* HAVE_SCHED_GETAFFINITY_NP && HAVE_GETPID */
308 if (0 >= ret)
309 return -1;
310 return ret;
311}
312
313
314/**
315 * Detect the number of logical CPU cores available for the process.
316 * The number of cores available for this process could be different from
317 * value of cores available on the system. The OS may have limit on number
318 * assigned/allowed cores for single process and process may have limited
319 * CPU affinity.
320 * @return the number of logical CPU cores available for the process or
321 * -1 if failed to detect
322 */
323int
324mhd_tool_get_proc_cpu_count (void)
325{
326 int res;
327
328#if defined(__linux__) || defined(__GLIBC__)
329 /* On Linux kernel try first 'sched_getaffinity()' as it should be
330 the native API.
331 Also try it first on other kernels if Glibc is used. */
332 res = mhd_tool_get_proc_cpu_count_sched_getaffinity_ ();
333 if (0 < res)
334 return res;
335
336 res = mhd_tool_get_proc_cpu_count_cpuset_getaffinity_ ();
337 if (0 < res)
338 return res;
339#else /* ! __linux__ && ! __GLIBC__ */
340 /* On non-Linux kernels 'cpuset_getaffinity()' could be the native API,
341 while 'sched_getaffinity()' could be implemented in compatibility layer. */
342 res = mhd_tool_get_proc_cpu_count_cpuset_getaffinity_ ();
343 if (0 < res)
344 return res;
345
346 res = mhd_tool_get_proc_cpu_count_sched_getaffinity_ ();
347 if (0 < res)
348 return res;
349#endif /* ! __linux__ && ! __GLIBC__ */
350
351 res = mhd_tool_get_proc_cpu_count_sched_getaffinity_np_ ();
352 if (0 < res)
353 return res;
354
355 return -1;
356}
357
358
359/**
360 * Detect the number of processors by special API functions
361 * @return number of processors as returned by special API functions or
362 * -1 in case of error or special API functions unavailable
363 */
364static int
365mhd_tool_get_sys_cpu_count_special_api_ (void)
366{
367 int ret = -1;
368#ifdef HAVE_PSTAT_GETDYNAMIC
369 if (0 >= ret)
370 {
371 /* HP-UX things */
372 struct pst_dynamic psd_data;
373 memset ((void *) &psd_data, 0, sizeof(psd_data));
374 if (1 == pstat_getdynamic (&psd_data, sizeof(psd_data), (size_t) 1, 0))
375 {
376 if (0 < psd_data.psd_proc_cnt)
377 ret = (int) psd_data.psd_proc_cnt;
378 }
379 }
380#endif /* HAVE_PSTAT_GETDYNAMIC */
381#ifdef HAVE_VXCPUENABLEDGET
382 if (0 >= ret)
383 {
384 /* VxWorks */
385 cpuset_t enb_set;
386 enb_set = vxCpuEnabledGet ();
387 /* Count set bits */
388 for (ret = 0; 0 != enb_set; enb_set &= enb_set - 1)
389 ++ret;
390 }
391#endif /* HAVE_VXCPUENABLEDGET */
392#if defined(_WIN32) && ! defined (__CYGWIN__)
393 if (0 >= ret)
394 {
395 /* Native W32 */
396 HMODULE k32hndl;
397 k32hndl = GetModuleHandleA ("kernel32.dll");
398 if (NULL != k32hndl)
399 {
400 typedef DWORD (WINAPI *GAPC_PTR)(WORD GroupNumber);
401 GAPC_PTR ptrGetActiveProcessorCount;
402 /* Available on W7 or later */
403 ptrGetActiveProcessorCount =
404 (GAPC_PTR) (void *) GetProcAddress (k32hndl, "GetActiveProcessorCount");
405 if (NULL != ptrGetActiveProcessorCount)
406 {
407 DWORD res;
408 res = ptrGetActiveProcessorCount (ALL_PROCESSOR_GROUPS);
409 ret = (int) res;
410 if (res != (DWORD) ret)
411 ret = -1; /* Overflow */
412 }
413 }
414 if ((0 >= ret) && (NULL != k32hndl))
415 {
416 typedef void (WINAPI *GNSI_PTR)(SYSTEM_INFO *pSysInfo);
417 GNSI_PTR ptrGetNativeSystemInfo;
418 /* May give incorrect (low) result on versions from W7 to W11
419 when more then 64 CPUs are available */
420 ptrGetNativeSystemInfo =
421 (GNSI_PTR) (void *) GetProcAddress (k32hndl, "GetNativeSystemInfo");
422 if (NULL != ptrGetNativeSystemInfo)
423 {
424 SYSTEM_INFO sysInfo;
425
426 memset ((void *) &sysInfo, 0, sizeof(sysInfo));
427 ptrGetNativeSystemInfo (&sysInfo);
428 ret = (int) sysInfo.dwNumberOfProcessors;
429 if (sysInfo.dwNumberOfProcessors != (DWORD) ret)
430 ret = -1; /* Overflow */
431 }
432 }
433 }
434 if (0 >= ret)
435 {
436 /* May give incorrect (low) result on versions from W7 to W11
437 when more then 64 CPUs are available */
438 SYSTEM_INFO sysInfo;
439 memset ((void *) &sysInfo, 0, sizeof(sysInfo));
440 GetSystemInfo (&sysInfo);
441 ret = (int) sysInfo.dwNumberOfProcessors;
442 if (sysInfo.dwNumberOfProcessors != (DWORD) ret)
443 ret = -1; /* Overflow */
444 }
445#endif /* _WIN32 && ! __CYGWIN__ */
446 if (0 >= ret)
447 return -1;
448 return ret;
449}
450
451
452/**
453 * Detect the number of processors by sysctl*() functions
454 * @return number of processors as returned by 'sysctl*' functions or
455 * -1 in case of error or the number cannot be detected
456 * by these functions
457 */
458static int
459mhd_tool_get_sys_cpu_count_sysctl_ (void)
460{
461 int ret = -1;
462 /* Do not use sysctl() function on GNU/Linux even if
463 sysctl() is available */
464#ifndef __linux__
465#ifdef HAVE_SYSCTLBYNAME
466 if (0 >= ret)
467 {
468 size_t value_size = sizeof(ret);
469 /* Darwin: The number of available logical CPUs */
470 if ((0 != sysctlbyname ("hw.logicalcpu", &ret, &value_size,
471 NULL, 0))
472 || (sizeof(ret) != value_size))
473 ret = -1;
474 }
475 if (0 >= ret)
476 {
477 size_t value_size = sizeof(ret);
478 /* FreeBSD: The number of online CPUs */
479 if ((0 != sysctlbyname ("kern.smp.cpus", &ret, &value_size,
480 NULL, 0))
481 || (sizeof(ret) != value_size))
482 ret = -1;
483 }
484 if (0 >= ret)
485 {
486 size_t value_size = sizeof(ret);
487 /* Darwin: The current number of CPUs available to run threads */
488 if ((0 != sysctlbyname ("hw.activecpu", &ret, &value_size,
489 NULL, 0))
490 || (sizeof(ret) != value_size))
491 ret = -1;
492 }
493 if (0 >= ret)
494 {
495 size_t value_size = sizeof(ret);
496 /* OpenBSD, NetBSD: The number of online CPUs */
497 if ((0 != sysctlbyname ("hw.ncpuonline", &ret, &value_size,
498 NULL, 0))
499 || (sizeof(ret) != value_size))
500 ret = -1;
501 }
502 if (0 >= ret)
503 {
504 size_t value_size = sizeof(ret);
505 /* Darwin: The old/alternative name for "hw.activecpu" */
506 if ((0 != sysctlbyname ("hw.availcpu", &ret, &value_size,
507 NULL, 0))
508 || (sizeof(ret) != value_size))
509 ret = -1;
510 }
511#endif /* HAVE_SYSCTLBYNAME */
512#if defined(HAVE_SYSCTL) && \
513 defined(HAS_DECL_CTL_HW) && \
514 defined(HAS_DECL_HW_NCPUONLINE)
515 if (0 >= ret)
516 {
517 /* OpenBSD, NetBSD: The number of online CPUs */
518 int mib[2] = { CTL_HW, HW_NCPUONLINE };
519 size_t value_size = sizeof(ret);
520 if ((0 != sysctl (mib, 2, &ret, &value_size, NULL, 0))
521 || (sizeof(ret) != value_size))
522 ret = -1;
523 }
524#endif /* HAVE_SYSCTL && HAS_DECL_CTL_HW && HAS_DECL_HW_NCPUONLINE */
525#if defined(HAVE_SYSCTL) && \
526 defined(HAS_DECL_CTL_HW) && \
527 defined(HAS_DECL_HW_AVAILCPU)
528 if (0 >= ret)
529 {
530 /* Darwin: The MIB name for "hw.activecpu" */
531 int mib[2] = { CTL_HW, HW_AVAILCPU };
532 size_t value_size = sizeof(ret);
533 if ((0 != sysctl (mib, 2, &ret, &value_size, NULL, 0))
534 || (sizeof(ret) != value_size))
535 ret = -1;
536 }
537#endif /* HAVE_SYSCTL && HAS_DECL_CTL_HW && HAS_DECL_HW_AVAILCPU */
538#ifdef HAVE_SYSCTLBYNAME
539 if (0 >= ret)
540 {
541 size_t value_size = sizeof(ret);
542 /* FreeBSD, OpenBSD, NetBSD, Darwin (and others?): The number of CPUs */
543 if ((0 != sysctlbyname ("hw.ncpu", &ret, &value_size,
544 NULL, 0))
545 || (sizeof(ret) != value_size))
546 ret = -1;
547 }
548#endif /* HAVE_SYSCTLBYNAME */
549#if defined(HAVE_SYSCTL) && \
550 defined(HAS_DECL_CTL_HW) && \
551 defined(HAS_DECL_HW_NCPU)
552 if (0 >= ret)
553 {
554 /* FreeBSD, OpenBSD, NetBSD, Darwin (and others?): The number of CPUs */
555 int mib[2] = { CTL_HW, HW_NCPU };
556 size_t value_size = sizeof(ret);
557 if ((0 != sysctl (mib, 2, &ret, &value_size, NULL, 0))
558 || (sizeof(ret) != value_size))
559 ret = -1;
560 }
561#endif /* HAVE_SYSCTL && HAS_DECL_CTL_HW && HAS_DECL_HW_NCPU */
562#endif /* ! __linux__ */
563 if (0 >= ret)
564 return -1;
565 return ret;
566}
567
568
569/**
570 * Detect the number of processors by sysconf() function
571 * @return number of processors as returned by 'sysconf' function or
572 * -1 in case of error or 'sysconf' unavailable
573 */
574static int
575mhd_tool_get_sys_cpu_count_sysconf_ (void)
576{
577 int ret = -1;
578#if defined(HAVE_SYSCONF) && \
579 (defined(HAS_DECL__SC_NPROCESSORS_ONLN) || \
580 defined(HAS_DECL__SC_NPROC_ONLN) || \
581 defined(HAS_DECL__SC_CRAY_NCPU))
582 long value = -1;
583#ifdef HAS_DECL__SC_NPROCESSORS_ONLN
584 if (0 >= value)
585 value = sysconf (_SC_NPROCESSORS_ONLN);
586#endif /* HAS_DECL__SC_NPROCESSORS_ONLN */
587#ifdef HAS_DECL__SC_NPROC_ONLN
588 if (0 >= value)
589 value = sysconf (_SC_NPROC_ONLN);
590#endif /* HAS_DECL__SC_NPROC_ONLN */
591#ifdef HAS_DECL__SC_CRAY_NCPU
592 if (0 >= value)
593 value = sysconf (_SC_CRAY_NCPU);
594#endif /* HAS_DECL__SC_CRAY_NCPU */
595 if (0 >= value)
596 return -1;
597 ret = (int) value;
598 if ((long) ret != value)
599 return -1; /* Overflow */
600#endif /* HAVE_SYSCONF &&
601 (HAS_DECL__SC_NPROCESSORS_ONLN || HAS_DECL__SC_NPROC_ONLN ||
602 HAS_DECL__SC_CRAY_NCPU) */
603 return ret;
604}
605
606
607/**
608 * Try to detect the number of logical CPU cores available for the system.
609 * The number of available logical CPU cores could be changed any time due to
610 * CPU hotplug.
611 * @return the number of logical CPU cores available,
612 * -1 if failed to detect.
613 */
614int
615mhd_tool_get_system_cpu_count (void)
616{
617 int res;
618
619 /* Try specialised APIs first */
620 res = mhd_tool_get_sys_cpu_count_special_api_ ();
621 if (0 < res)
622 return res;
623
624 /* Try sysctl*(). This is typically a direct interface to
625 kernel values. */
626 res = mhd_tool_get_sys_cpu_count_sysctl_ ();
627 if (0 < res)
628 return res;
629
630 /* Try sysconf() as the last resort as this is a generic interface
631 which can be implemented by parsing system files. */
632 res = mhd_tool_get_sys_cpu_count_sysconf_ ();
633#if ! defined(__linux__) && ! defined(__GLIBC__)
634 if (0 < res)
635 return res;
636#else /* __linux__ || __GLIBC__ */
637 if (2 < res)
638 return res;
639 if (0 < res)
640 {
641 /* '1' or '2' could a be fallback number.
642 * See get_nprocs_fallback() in glibc
643 sysdeps/unix/sysv/linux/getsysstats.c */
644
645 int proc_cpu_count;
646
647 proc_cpu_count = mhd_tool_get_proc_cpu_count ();
648 if ((0 < proc_cpu_count) && (proc_cpu_count <= res))
649 {
650 /* The detected number of CPUs available for the process
651 is 1 or 2 and fits detected number of system CPUS.
652 Assume detected number is correct. */
653 return res;
654 }
655 }
656#endif /* __linux__ || __GLIBC__ */
657 return -1; /* Cannot detect */
658}
diff --git a/src/tools/mhd_tool_get_cpu_count.h b/src/tools/mhd_tool_get_cpu_count.h
new file mode 100644
index 00000000..84091e0f
--- /dev/null
+++ b/src/tools/mhd_tool_get_cpu_count.h
@@ -0,0 +1,55 @@
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.h
22 * @brief Declaration of functions to detect the number of available
23 * CPU cores.
24 * @author Karlson2k (Evgeny Grin)
25 */
26
27
28#ifndef SRC_TOOLS_MHD_TOOL_GET_CPU_COUNT_H_
29#define SRC_TOOLS_MHD_TOOL_GET_CPU_COUNT_H_ 1
30
31
32/**
33 * Detect the number of logical CPU cores available for the process.
34 * The number of cores available for this process could be different from
35 * value of cores available on the system. The OS may have limit on number
36 * assigned/allowed cores for single process and process may have limited
37 * CPU affinity.
38 * @return the number of logical CPU cores available for the process or
39 * -1 if failed to detect
40 */
41int
42mhd_tool_get_proc_cpu_count (void);
43
44
45/**
46 * Try to detect the number of logical CPU cores available for the system.
47 * The number of available logical CPU cores could be changed any time due to
48 * CPU hotplug.
49 * @return the number of logical CPU cores available,
50 * -1 if failed to detect.
51 */
52int
53mhd_tool_get_system_cpu_count (void);
54
55#endif /* SRC_TOOLS_MHD_TOOL_GET_CPU_COUNT_H_ */
diff --git a/src/tools/perf_replies.c b/src/tools/perf_replies.c
index 24df534f..9f2a5305 100644
--- a/src/tools/perf_replies.c
+++ b/src/tools/perf_replies.c
@@ -39,6 +39,7 @@
39#include "mhd_options.h" 39#include "mhd_options.h"
40#include "microhttpd.h" 40#include "microhttpd.h"
41#include "mhd_tool_str_to_uint.h" 41#include "mhd_tool_str_to_uint.h"
42#include "mhd_tool_get_cpu_count.h"
42 43
43#if defined(MHD_REAL_CPU_COUNT) 44#if defined(MHD_REAL_CPU_COUNT)
44#if MHD_REAL_CPU_COUNT == 0 45#if MHD_REAL_CPU_COUNT == 0
@@ -165,28 +166,37 @@ get_cmd_out_as_number (const char *cmd)
165static unsigned int 166static unsigned int
166detect_cpu_core_count (void) 167detect_cpu_core_count (void)
167{ 168{
168 int sys_cpu_count = -1; 169 int sys_cpu_count;
169#if ! defined(_WIN32) || defined(__CYGWIN__) 170 sys_cpu_count = mhd_tool_get_system_cpu_count ();
170 sys_cpu_count = get_cmd_out_as_number ("nproc 2>/dev/null");
171#endif /* ! _WIN32) || __CYGWIN__ */
172#ifdef _WIN32
173 if (0 >= sys_cpu_count)
174 sys_cpu_count = get_cmd_out_as_number ("echo %NUMBER_OF_PROCESSORS%");
175#endif /* _WIN32 */
176 if (0 >= sys_cpu_count) 171 if (0 >= sys_cpu_count)
177 { 172 {
178 fprintf (stderr, "Failed to detect the number of available CPU cores.\n"); 173 int proc_cpu_count;
174 fprintf (stderr, "Failed to detect the number of logical CPU cores "
175 "available on the system.\n");
176 proc_cpu_count = mhd_tool_get_proc_cpu_count ();
177 if (0 < proc_cpu_count)
178 {
179 fprintf (stderr, "The number of CPU cores available for this process "
180 "is used as a fallback.\n");
181 sys_cpu_count = proc_cpu_count;
182 }
179#ifdef MHD_REAL_CPU_COUNT 183#ifdef MHD_REAL_CPU_COUNT
180 fprintf (stderr, "Hardcoded number is used as a fallback.\n"); 184 if (0 >= sys_cpu_count)
181 sys_cpu_count = MHD_REAL_CPU_COUNT; 185 {
186 fprintf (stderr, "configure-detected hardcoded number is used "
187 "as a fallback.\n");
188 sys_cpu_count = MHD_REAL_CPU_COUNT;
189 }
182#endif 190#endif
183 if (0 >= sys_cpu_count) 191 if (0 >= sys_cpu_count)
184 sys_cpu_count = 1; 192 sys_cpu_count = 1;
185 printf ("Assuming %d CPU cores.\n", sys_cpu_count); 193 printf ("Assuming %d logical CPU core%s on this system.\n", sys_cpu_count,
194 (1 == sys_cpu_count) ? "" : "s");
186 } 195 }
187 else 196 else
188 { 197 {
189 printf ("Detected %d CPU cores.\n", sys_cpu_count); 198 printf ("Detected %d logical CPU core%s on this system.\n", sys_cpu_count,
199 (1 == sys_cpu_count) ? "" : "s");
190 } 200 }
191 return (unsigned int) sys_cpu_count; 201 return (unsigned int) sys_cpu_count;
192} 202}
@@ -202,20 +212,84 @@ get_cpu_core_count (void)
202} 212}
203 213
204 214
215static unsigned int
216detect_process_cpu_core_count (void)
217{
218 unsigned int num_proc_cpu_cores;
219 unsigned int sys_cpu_cores;
220 int res;
221
222 sys_cpu_cores = get_cpu_core_count ();
223 res = mhd_tool_get_proc_cpu_count ();
224 if (0 > res)
225 {
226 fprintf (stderr, "Cannot detect the number of logical CPU cores available "
227 "for this process.\n");
228 if (1 != sys_cpu_cores)
229 printf ("Assuming all %u system logical CPU cores are available to run "
230 "threads of this process.\n", sys_cpu_cores);
231 else
232 printf ("Assuming single logical CPU core available for this process.\n");
233 num_proc_cpu_cores = sys_cpu_cores;
234 }
235 else
236 {
237 printf ("Detected %d logical CPU core%s available to run threads "
238 "of this process.\n", res, (1 == res) ? "" : "s");
239 num_proc_cpu_cores = (unsigned int) res;
240 }
241 if (num_proc_cpu_cores > sys_cpu_cores)
242 {
243 fprintf (stderr, "WARNING: Detected number of CPU cores available "
244 "for this process (%u) is larger than detected number "
245 "of CPU cores on the system (%u).\n",
246 num_proc_cpu_cores, sys_cpu_cores);
247 num_proc_cpu_cores = sys_cpu_cores;
248 fprintf (stderr, "Using %u as the number of logical CPU cores available "
249 "for this process.\n", num_proc_cpu_cores);
250 }
251 return num_proc_cpu_cores;
252}
253
254
255static unsigned int
256get_process_cpu_core_count (void)
257{
258 static unsigned int proc_num_cpu_cores = 0;
259 if (0 == proc_num_cpu_cores)
260 proc_num_cpu_cores = detect_process_cpu_core_count ();
261 return proc_num_cpu_cores;
262}
263
264
205static unsigned int num_threads = 0; 265static unsigned int num_threads = 0;
206 266
207static unsigned int 267static unsigned int
208get_num_threads (void) 268get_num_threads (void)
209{ 269{
210 static const unsigned int max_threads = 32; 270 static const unsigned int max_threads = 32;
271 if (0 < num_threads)
272 return num_threads;
273
274 num_threads = get_cpu_core_count () / 2;
211 if (0 == num_threads) 275 if (0 == num_threads)
276 num_threads = 1;
277 else
212 { 278 {
213 num_threads = get_cpu_core_count () / 2; 279 unsigned int num_proc_cpus;
214 if (0 == num_threads) 280 num_proc_cpus = get_process_cpu_core_count ();
215 num_threads = 1; 281 if (num_proc_cpus >= num_threads)
216 else 282 {
217 printf ("Using half of all available CPU cores, assuming the other half " 283 printf ("Using half of all available CPU cores, assuming the other half "
218 "is used by client / requests generator.\n"); 284 "is used by client / requests generator.\n");
285 }
286 else
287 {
288 printf ("Using all CPU cores available for this process as more than "
289 "half of CPU cores on this system are still available for use "
290 "by client / requests generator.\n");
291 num_threads = num_proc_cpus;
292 }
219 } 293 }
220 if (max_threads < num_threads) 294 if (max_threads < num_threads)
221 { 295 {
@@ -1059,9 +1133,10 @@ check_apply_param__all_cpus (void)
1059 if (! tool_params.all_cpus) 1133 if (! tool_params.all_cpus)
1060 return; 1134 return;
1061 1135
1062 num_threads = get_cpu_core_count (); 1136 num_threads = get_process_cpu_core_count ();
1063 printf ("Requested use of all available CPU cores for MHD threads.\n"); 1137 printf ("Requested use of all available CPU cores for MHD threads.\n");
1064 print_all_cores_used (); 1138 if (get_cpu_core_count () == num_threads)
1139 print_all_cores_used ();
1065} 1140}
1066 1141
1067 1142
@@ -1075,20 +1150,21 @@ check_apply_param__threads (void)
1075 return; 1150 return;
1076 1151
1077 num_threads = tool_params.threads; 1152 num_threads = tool_params.threads;
1153
1154 if (get_process_cpu_core_count () < num_threads)
1155 {
1156 fprintf (stderr, "WARNING: The requested number of threads (%u) is "
1157 "higher than the number of detected available CPU cores (%u).\n",
1158 num_threads, get_process_cpu_core_count ());
1159 fprintf (stderr, "This decreases the performance. "
1160 "Consider using fewer threads.\n");
1161 }
1078 if (get_cpu_core_count () == num_threads) 1162 if (get_cpu_core_count () == num_threads)
1079 { 1163 {
1080 printf ("The requested number of threads is equal to the number of " 1164 printf ("The requested number of threads is equal to the number of "
1081 "detected CPU cores.\n"); 1165 "detected CPU cores.\n");
1082 print_all_cores_used (); 1166 print_all_cores_used ();
1083 } 1167 }
1084 else if (get_cpu_core_count () < num_threads)
1085 {
1086 fprintf (stderr, "WARNING: The requested number of threads (%u) is "
1087 "higher than the number of detected CPU cores (%u).\n",
1088 num_threads, get_cpu_core_count ());
1089 fprintf (stderr, "This decreases the performance. "
1090 "Consider using fewer threads.\n");
1091 }
1092} 1168}
1093 1169
1094 1170
@@ -1228,7 +1304,7 @@ static struct MHD_Response *resp_single = NULL;
1228 The system will keep it in cache. */ 1304 The system will keep it in cache. */
1229static const char tiny_body[] = "Hi!"; 1305static const char tiny_body[] = "Hi!";
1230static char *body_dyn = NULL; /* Non-static body data */ 1306static char *body_dyn = NULL; /* Non-static body data */
1231size_t body_dyn_size; 1307static size_t body_dyn_size;
1232 1308
1233/* Non-zero - success, zero - failure */ 1309/* Non-zero - success, zero - failure */
1234static int 1310static int