libmicrohttpd

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

commit e87d26c42902db03560ef2e71e7e656bfd32b5a7
parent 8a6307e08c45672e10736ceed95cf0ee377123e0
Author: Evgeny Grin (Karlson2k) <k2k@narod.ru>
Date:   Wed, 12 Jul 2023 21:29:45 +0300

Implemented proper detection of number of available CPU cores

Diffstat:
Mconfigure.ac | 337+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Msrc/include/mhd_options.h | 76++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Msrc/tools/Makefile.am | 3++-
Asrc/tools/mhd_tool_get_cpu_count.c | 658+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/tools/mhd_tool_get_cpu_count.h | 55+++++++++++++++++++++++++++++++++++++++++++++++++++++++
Msrc/tools/perf_replies.c | 132++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-----------------
6 files changed, 1232 insertions(+), 29 deletions(-)

diff --git a/configure.ac b/configure.ac @@ -3917,6 +3917,343 @@ AS_VAR_IF([enable_tools],["yes"], ) ] ) + AC_CHECK_HEADERS([features.h sys/pstat.h vxCpuLib.h],[],[],[AC_INCLUDES_DEFAULT]) + AC_CHECK_DECLS( + [_SC_NPROCESSORS_ONLN,_SC_NPROC_ONLN,_SC_CRAY_NCPU,_SC_NPROCESSORS_CONF,CTL_HW,HW_NCPUONLINE,HW_NCPU,HW_AVAILCPU], + [],[], + [[ +#ifdef HAVE_SYS_TYPES_H +#include <sys/types.h> +#endif /* HAVE_SYS_TYPES_H */ +#ifdef HAVE_SYS_PARAM_H +#include <sys/param.h> +#endif /* HAVE_SYS_PARAM_H */ +#ifdef HAVE_SYS_SYSCTL_H +#include <sys/sysctl.h> +#endif /* HAVE_SYS_SYSCTL_H */ +#ifdef HAVE_UNISTD_H +#include <unistd.h> +#endif /* HAVE_UNISTD_H */ + ]] + ) + MHD_CHECK_FUNC([pstat_getdynamic],[[ +#include <sys/param.h> +#include <sys/pstat.h> + ]], + [[ + struct pst_dynamic psd_data; + i][f (1 != pstat_getdynamic(&psd_data, sizeof(psd_data), (size_t)1, 0)) + return 2; + i][f (0 >= psd_data.psd_proc_cnt) + return 3; + ]] + ) + MHD_CHECK_FUNC([vxCpuEnabledGet],[[#include <vxCpuLib.h>]], + [[ + cpuset_t enb_set; + enb_set = vxCpuEnabledGet(); + (void) enb_set; + ]] + ) + AC_CHECK_HEADERS([sched.h sys/_cpuset.h sys/cpuset.h],[],[],[AC_INCLUDES_DEFAULT]) + # glibc / Linux kernel + MHD_CHECK_FUNC([getpid],[[ +#ifdef HAVE_SYS_TYPES_H +#include <sys/types.h> +#endif /* HAVE_SYS_TYPES_H */ +#include <unistd.h> + ]], + [[ + pid_t cur_pid; + cur_pid = getpid(); + (void) &cur_pid; /* Mute possible warning */ + ]], + [ + MHD_CHECK_FUNC([sched_getaffinity],[[ +#ifdef HAVE_SYS_TYPES_H +#include <sys/types.h> +#endif /* HAVE_SYS_TYPES_H */ +#include <unistd.h> +#include <sched.h> + ]], + [[ + cpu_set_t cur_set; + i][f (0 != sched_getaffinity(getpid(), sizeof(cur_set), &cur_set)) + return 2; + i][f (0 == CPU_SET (0, &cur_set)) + return 3; /* Actually this could be a valid result */ + ]], + [ + MHD_CHECK_FUNC([CPU_COUNT],[[ +#include <stddef.h> +#include <sched.h> + ]], + [[ + cpu_set_t cur_set; + CPU_ZERO(&cur_set); + i][f (0 != CPU_COUNT(&cur_set)) + return 2; + ]], + [ + MHD_CHECK_FUNC([CPU_COUNT_S],[[ +#include <stddef.h> +#include <sched.h> + ]], + [[ + static const unsigned int set_size_cpus = 2048u; + const size_t set_size_bytes = (size_t) CPU_ALLOC_SIZE(set_size_cpus); + cpu_set_t *p_set; + p_set = CPU_ALLOC(set_size_cpus); + i][f (!p_set) + return 2; + CPU_ZERO_S(set_size_bytes, p_set); + i][f (0 != CPU_COUNT_S(set_size_bytes, p_set)) + { + CPU_FREE(p_set); + return 3; + } + CPU_FREE(p_set); + ]], + [AC_CHECK_DECLS([CPU_SETSIZE],[],[],[#include <sched.h>])] + ) + ] + ) + ] + ) + # NetBSD + # Should work only with -lrt, but actually works without it. + MHD_CHECK_FUNC([sched_getaffinity_np],[[ +#ifdef HAVE_SYS_TYPES_H +#include <sys/types.h> +#endif /* HAVE_SYS_TYPES_H */ +#include <unistd.h> +#include <sched.h> + ]], + [[ + cpuset_t *cpuset_ptr; + cpuid_t cpu_num = 0; + cpuset_ptr = cpuset_create(); + i][f (!cpuset_ptr) + return 2; + i][f (0 != sched_getaffinity_np(getpid(), cpuset_size(cpuset_ptr), cpuset_ptr)) + { + cpuset_destroy(cpuset_ptr); + return 3; + } + i][f (0 >= cpuset_isset(cpu_num, cpuset_ptr)) + { + cpuset_destroy(cpuset_ptr); + return 4; /* Actually this could be a valid result */ + } + cpuset_destroy(cpuset_ptr); + ]] + ) + ] + ) + # FreeBSD + MHD_CHECK_FUNC([cpuset_getaffinity],[[ +#ifdef HAVE_SYS_PARAM_H +#include <sys/param.h> +#endif /* HAVE_SYS_PARAM_H */ +#include <sys/cpuset.h> + ]], + [[ + cpuset_t cur_mask; + i][f (0 != cpuset_getaffinity(CPU_LEVEL_WHICH, CPU_WHICH_PID, (id_t) -1, sizeof(cur_mask), &cur_mask)) + return 2; + ]], + [ + MHD_CHECK_FUNC([CPU_COUNT],[[ +#ifdef HAVE_SYS_PARAM_H +#include <sys/param.h> +#endif /* HAVE_SYS_PARAM_H */ +#ifdef HAVE_SYS__CPUSET_H +#include <sys/_cpuset.h> +#endif /* HAVE_SYS_PARAM_H */ +#include <sys/cpuset.h> + ]], + [[ + cpuset_t test_mask; + CPU_ZERO(&test_mask); + i][f (0 != CPU_COUNT(&test_mask)) + return 2; + ]], + [ + MHD_CHECK_FUNC([CPU_COUNT_S],[[ +#ifdef HAVE_SYS_PARAM_H +#include <sys/param.h> +#endif /* HAVE_SYS_PARAM_H */ +#ifdef HAVE_SYS__CPUSET_H +#include <sys/_cpuset.h> +#endif /* HAVE_SYS_PARAM_H */ +#include <sys/cpuset.h> + ]], + [[ + static const unsigned int max_cpu_num = 2048u; + cpuset_t *mask_ptr; + mask_ptr = CPU_ALLOC(max_cpu_num); + i][f (! mask_ptr) + return 2; + i][f (0 != cpuset_getaffinity(CPU_LEVEL_WHICH, CPU_WHICH_PID, (id_t) -1, + CPU_ALLOC_SIZE(max_cpu_num), mask_ptr)) + { + CPU_FREE(mask_ptr); + return 3; + } + i][f (0 == CPU_COUNT_S(CPU_ALLOC_SIZE(max_cpu_num), &test_mask)) + { + CPU_FREE(mask_ptr); + return 4; + } + CPU_FREE(mask_ptr); + ]] + ) + ] + ) + ] + ) + AS_VAR_IF([mhd_cv_func_CPU_COUNT_S],["yes"], + [ + AC_CACHE_CHECK([whether CPU_COUNT_S() expects max CPU number as 'size' parameter],[mhd_cv_func_cpu_count_s_cpus], + [ + AS_VAR_IF([cross-compiling],["yes"],[mhd_cv_func_cpu_count_s_cpus="assuming no"], + [ + AC_LINK_IFELSE( + [ + AC_LANG_PROGRAM([[ +#ifdef HAVE_SYS_TYPES_H +#include <sys/types.h> +#endif /* HAVE_SYS_TYPES_H */ +#ifdef HAVE_SYS_PARAM_H +#include <sys/param.h> +#endif /* HAVE_SYS_PARAM_H */ +#ifdef HAVE_SYS__CPUSET_H +#include <sys/_cpuset.h> +#endif /* HAVE_SYS_PARAM_H */ +#ifdef HAVE_SYS_CPUSET_H +#include <sys/cpuset.h> +#endif /* HAVE_SYS_CPUSET_H */ +#ifdef HAVE_STDDEF_H +#include <stddef.h> +#endif /* HAVE_STDDEF_H */ +#ifdef HAVE_UNISTD_H +#include <unistd.h> +#endif /* HAVE_UNISTD_H */ +#ifdef HAVE_SCHED_H +#include <sched.h> +#endif /* HAVE_SCHED_H */ +#include <stdio.h> + +#ifdef HAVE_SCHED_GETAFFINITY +#define TEST_CPUSET_TYPE cpu_set_t +#else /* ! HAVE_SCHED_GETAFFINITY */ +#define TEST_CPUSET_TYPE cpuset_t +#endif /* ! HAVE_SCHED_GETAFFINITY */ + ]], + [[ + TEST_CPUSET_TYPE *p_testset; + /* The size of the cpuset that is larger then the test cpuset (in CPUs) */ + static const unsigned int testset_size_oversized_cpus = (8 * sizeof(long)) * 1024; + const size_t testset_size_oversized_bytes = CPU_ALLOC_SIZE(testset_size_oversized_cpus); + /* The size of the test cpuset in number of CPUs */ + const unsigned int testset_size_cpus = (unsigned int) testset_size_oversized_bytes; + /* The size of the test cpuset in bytes */ + const size_t testset_size_bytes = CPU_ALLOC_SIZE(testset_size_cpus); + /* The last one CPU in the test set */ + const unsigned int test_cpu_num1 = testset_size_cpus - 1; + /* The penultimate CPU in the test set */ + const unsigned int test_cpu_num2 = testset_size_cpus - 2; + /* The CPU numbers that should be cleared */ + const unsigned int test_cpu_clear1 = testset_size_cpus - 3; + const unsigned int test_cpu_clear2 = testset_size_cpus - 4; + unsigned int count_res; + int ret = 0; + + /* Allocate oversize area to ensure that memory outside the buffer is not used */ + p_testset = CPU_ALLOC(testset_size_oversized_cpus); + if (! p_testset) + { + fprintf (stderr, "Error allocating memory.\n"); + return 99; + } + /* Set the some CPU numbers and then clear them */ + CPU_SET_S(test_cpu_clear1, testset_size_bytes, p_testset); + CPU_SET_S(test_cpu_clear2, testset_size_bytes, p_testset); + CPU_ZERO_S(testset_size_bytes, p_testset); + /* Set two CPUs on */ + CPU_SET_S(test_cpu_num1, testset_size_bytes, p_testset); + CPU_SET_S(test_cpu_num2, testset_size_bytes, p_testset); + count_res = (unsigned int) CPU_COUNT_S(testset_size_bytes, p_testset); + if (0 == count_res) + { + fprintf (stderr, "Full cpuset cannot be read by CPU_COUNT_S() when using the number of bytes as the size parameter.\n"); + /* Set the some CPU numbers and then clear them */ + CPU_SET_S(test_cpu_clear1, testset_size_cpus, p_testset); + CPU_SET_S(test_cpu_clear2, testset_size_cpus, p_testset); + CPU_ZERO_S(testset_size_cpus, p_testset); + /* Set two CPUs on */ + CPU_SET_S(test_cpu_num1, testset_size_cpus, p_testset); + CPU_SET_S(test_cpu_num2, testset_size_cpus, p_testset); + count_res = (unsigned int) CPU_COUNT_S(testset_size_cpus, p_testset); + if (2 == count_res) + { + fprintf (stderr, "Full cpuset is read by CPU_COUNT_S() only when using the maximum CPU number as the size parameter.\n"); + ret = 3; + } + else + { + fprintf (stderr, "Wrong result returned by CPU_COUNT_S() when using the maximum CPU number as the size parameter.\n"); + fprintf (stderr, "Number of 'enabled' CPUs: 2\n"); + fprintf (stderr, "Number of counted CPUs: %u\n", count_res); + fprintf (stderr, "CPU_COUNT_S() could be unreliable.\n"); + ret = 70; + } + } + else if (2 == count_res) + { + fprintf (stderr, "Full cpuset is read by CPU_COUNT_S() when using the number of bytes as the size parameter.\n"); + } + else + { + fprintf (stderr, "Wrong result returned by CPU_COUNT_S() when using the number of bytes as the size parameter.\n"); + fprintf (stderr, "Number of 'enabled' CPUs: 2\n"); + fprintf (stderr, "Number of counted CPUs: %u\n", count_res); + fprintf (stderr, "CPU_COUNT_S() could be unreliable.\n"); + ret = 71; + } + CPU_FREE(p_testset); + if (0 != ret) + return ret; + ]] + ) + ], + [ + AM_RUN_LOG([./conftest$EXEEXT]) + count_test_res=$? + AS_IF([test $count_test_res -eq 0],[mhd_cv_func_cpu_count_s_cpus="no"], + [test $count_test_res -eq 3],[mhd_cv_func_cpu_count_s_cpus="yes"], + [ + AC_MSG_WARN([Unexpected value returned by CPU_COUNT_S() test program. Please report to ${PACKAGE_BUGREPORT}]) + mhd_cv_func_cpu_count_s_cpus="assuming no" + ] + ) + ], + [ + AC_MSG_WARN([Cannot build CPU_COUNT_S() test program. Please report to ${PACKAGE_BUGREPORT}]) + mhd_cv_func_cpu_count_s_cpus="assuming no" + ] + ) + ] + ) + ] + ) + AS_VAR_IF([mhd_cv_func_cpu_count_s_cpus],["yes"], + [AC_DEFINE([MHD_FUNC_CPU_COUNT_S_GETS_CPUS],[1], + [Define to '1' if CPU_COUNT_S() function expects max CPU number as 'size' parameter]) + ] + ) + ] + ) ] ) diff --git a/src/include/mhd_options.h b/src/include/mhd_options.h @@ -192,5 +192,81 @@ #endif /* MHD_HAVE_MHD_FUNC_ */ #endif +/* Un-define some HAVE_DECL_* macro if they equal zero. + This should allow safely use #ifdef in the code. + Define HAS_DECL_* macros only if matching HAVE_DECL_* macro + has non-zero value. Unlike HAVE_DECL_*, macros HAS_DECL_* + cannot have zero value. */ +#ifdef HAVE_DECL__SC_NPROCESSORS_ONLN +# if 0 == HAVE_DECL__SC_NPROCESSORS_ONLN +# undef HAVE_DECL__SC_NPROCESSORS_ONLN +# else /* 0 != HAVE_DECL__SC_NPROCESSORS_ONLN */ +# define HAS_DECL__SC_NPROCESSORS_ONLN 1 +# endif /* 0 != HAVE_DECL__SC_NPROCESSORS_ONLN */ +#endif /* HAVE_DECL__SC_NPROCESSORS_ONLN */ + +#ifdef HAVE_DECL__SC_NPROCESSORS_CONF +# if 0 == HAVE_DECL__SC_NPROCESSORS_CONF +# undef HAVE_DECL__SC_NPROCESSORS_CONF +# else /* 0 != HAVE_DECL__SC_NPROCESSORS_CONF */ +# define HAS_DECL__SC_NPROCESSORS_CONF 1 +# endif /* 0 != HAVE_DECL__SC_NPROCESSORS_CONF */ +#endif /* HAVE_DECL__SC_NPROCESSORS_CONF */ + +#ifdef HAVE_DECL__SC_NPROC_ONLN +# if 0 == HAVE_DECL__SC_NPROC_ONLN +# undef HAVE_DECL__SC_NPROC_ONLN +# else /* 0 != HAVE_DECL__SC_NPROC_ONLN */ +# define HAS_DECL__SC_NPROC_ONLN 1 +# endif /* 0 != HAVE_DECL__SC_NPROC_ONLN */ +#endif /* HAVE_DECL__SC_NPROC_ONLN */ + +#ifdef HAVE_DECL__SC_CRAY_NCPU +# if 0 == HAVE_DECL__SC_CRAY_NCPU +# undef HAVE_DECL__SC_CRAY_NCPU +# else /* 0 != HAVE_DECL__SC_CRAY_NCPU */ +# define HAS_DECL__SC_CRAY_NCPU 1 +# endif /* 0 != HAVE_DECL__SC_CRAY_NCPU */ +#endif /* HAVE_DECL__SC_CRAY_NCPU */ + +#ifdef HAVE_DECL_CTL_HW +# if 0 == HAVE_DECL_CTL_HW +# undef HAVE_DECL_CTL_HW +# else /* 0 != HAVE_DECL_CTL_HW */ +# define HAS_DECL_CTL_HW 1 +# endif /* 0 != HAVE_DECL_CTL_HW */ +#endif /* HAVE_DECL_CTL_HW */ + +#ifdef HAVE_DECL_HW_NCPUONLINE +# if 0 == HAVE_DECL_HW_NCPUONLINE +# undef HAVE_DECL_HW_NCPUONLINE +# else /* 0 != HAVE_DECL_HW_NCPUONLINE */ +# define HAS_DECL_HW_NCPUONLINE 1 +# endif /* 0 != HAVE_DECL_HW_NCPUONLINE */ +#endif /* HAVE_DECL_HW_NCPUONLINE */ + +#ifdef HAVE_DECL_HW_AVAILCPU +# if 0 == HAVE_DECL_HW_AVAILCPU +# undef HAVE_DECL_HW_AVAILCPU +# else /* 0 != HAVE_DECL_HW_AVAILCPU */ +# define HAS_DECL_HW_AVAILCPU 1 +# endif /* 0 != HAVE_DECL_HW_AVAILCPU */ +#endif /* HAVE_DECL_HW_AVAILCPU */ + +#ifdef HAVE_DECL_HW_NCPU +# if 0 == HAVE_DECL_HW_NCPU +# undef HAVE_DECL_HW_NCPU +# else /* 0 != HAVE_DECL_HW_NCPU */ +# define HAS_DECL_HW_NCPU 1 +# endif /* 0 != HAVE_DECL_HW_NCPU */ +#endif /* HAVE_DECL_HW_NCPU */ + +#ifdef HAVE_DECL_CPU_SETSIZE +# if 0 == HAVE_DECL_CPU_SETSIZE +# undef HAVE_DECL_CPU_SETSIZE +# else /* 0 != HAVE_DECL_CPU_SETSIZE */ +# define HAS_DECL_CPU_SETSIZE 1 +# endif /* 0 != HAVE_DECL_CPU_SETSIZE */ +#endif /* HAVE_DECL_CPU_SETSIZE */ #endif /* MHD_OPTIONS_H */ diff --git a/src/tools/Makefile.am b/src/tools/Makefile.am @@ -36,4 +36,5 @@ endif perf_replies_SOURCES = \ - perf_replies.c mhd_tool_str_to_uint.h + perf_replies.c mhd_tool_str_to_uint.h \ + 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 @@ -0,0 +1,658 @@ +/* + This file is part of GNU libmicrohttpd + Copyright (C) 2023 Evgeny Grin (Karlson2k) + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/** + * @file tools/mhd_tool_get_cpu_count.c + * @brief Implementation of functions to detect the number of available + * CPU cores. + * @author Karlson2k (Evgeny Grin) + */ + +#include "mhd_options.h" +#include "mhd_tool_get_cpu_count.h" +#ifdef HAVE_SYS_TYPES_H +# include <sys/types.h> +#endif /* HAVE_SYS_TYPES_H */ +#ifdef HAVE_SYS_PARAM_H +# include <sys/param.h> +#endif /* HAVE_SYS_PARAM_H */ +#ifdef HAVE_SYS__CPUSET_H +# include <sys/_cpuset.h> +#endif /* HAVE_SYS_PARAM_H */ +#ifdef HAVE_STDDEF_H +# include <stddef.h> +#endif /* HAVE_STDDEF_H */ +#ifdef HAVE_STRING_H +# include <string.h> +#endif /* HAVE_STRING_H */ +#ifdef HAVE_SYS_SYSCTL_H +# include <sys/sysctl.h> +#endif /* HAVE_SYS_SYSCTL_H */ +#ifdef HAVE_FEATURES_H +# include <features.h> +#endif /* HAVE_FEATURES_H */ +#ifdef HAVE_UNISTD_H +# include <unistd.h> +#endif /* HAVE_UNISTD_H */ +#ifdef HAVE_VXCPULIB_H +# include <vxCpuLib.h> +#endif +#ifdef HAVE_WINDOWS_H +# ifndef WIN32_LEAN_AND_MEAN +# define WIN32_LEAN_AND_MEAN 1 +# endif /* ! WIN32_LEAN_AND_MEAN */ +# include <windows.h> +# ifndef ALL_PROCESSOR_GROUPS +# define ALL_PROCESSOR_GROUPS 0xFFFFu +# endif /* ALL_PROCESSOR_GROUPS */ +#elif defined(_WIN32) && ! defined (__CYGWIN__) +# error Windows headers are required for Windows build +#endif /* HAVE_WINDOWS_H */ +#ifdef HAVE_SCHED_H +# include <sched.h> +#endif /* HAVE_SCHED_H */ +#ifdef HAVE_SYS_CPUSET_H +# include <sys/cpuset.h> +#endif /* HAVE_SYS_CPUSET_H */ + +#if ! defined(HAS_DECL_CPU_SETSIZE) && ! defined(CPU_SETSIZE) +# define CPU_SETSIZE (1024) +# define CPU_SETSIZE_SAFE (64) +#else /* HAS_DECL_CPU_SETSIZE || CPU_SETSIZE */ +# define CPU_SETSIZE_SAFE CPU_SETSIZE +#endif /* HAS_DECL_CPU_SETSIZE || CPU_SETSIZE */ + +/* Check and fix possible missing macros */ +#if ! defined(HAS_DECL_CTL_HW) && defined(CTL_HW) +# define HAS_DECL_CTL_HW 1 +#endif /* ! HAS_DECL_CTL_HW && CTL_HW */ + +#if ! defined(HAS_DECL_HW_NCPUONLINE) && defined(HW_NCPUONLINE) +# define HAS_DECL_HW_NCPUONLINE 1 +#endif /* ! HAS_DECL_HW_NCPUONLINE && HW_NCPUONLINE */ + +#if ! defined(HAS_DECL_HW_AVAILCPU) && defined(HW_AVAILCPU) +# define HAS_DECL_HW_AVAILCPU 1 +#endif /* ! HAS_DECL_HW_AVAILCPU && HW_AVAILCPU */ + +#if ! defined(HAS_DECL_HW_NCPU) && defined(HW_NCPU) +# define HAS_DECL_HW_NCPU 1 +#endif /* ! HAS_DECL_HW_NCPU && HW_NCPU */ + +#if ! defined(HAS_DECL__SC_NPROCESSORS_ONLN) && defined(_SC_NPROCESSORS_ONLN) +# define HAS_DECL__SC_NPROCESSORS_ONLN 1 +#endif /* ! HAS_DECL__SC_NPROCESSORS_ONLN && _SC_NPROCESSORS_ONLN */ + +#if ! defined(HAS_DECL__SC_NPROC_ONLN) && defined(_SC_NPROC_ONLN) +# define HAS_DECL__SC_NPROC_ONLN 1 +#endif /* ! HAS_DECL__SC_NPROC_ONLN && _SC_NPROC_ONLN */ + +#if ! defined(HAS_DECL__SC_CRAY_NCPU) && defined(_SC_CRAY_NCPU) +# define HAS_DECL__SC_CRAY_NCPU 1 +#endif /* ! HAS_DECL__SC_CRAY_NCPU && _SC_CRAY_NCPU */ + +#if ! defined(HAS_DECL__SC_NPROCESSORS_CONF) && defined(_SC_NPROCESSORS_CONF) +# define HAS_DECL__SC_NPROCESSORS_CONF 1 +#endif /* ! HAVE_DECL__SC_NPROCESSORS_CONF && _SC_NPROCESSORS_CONF */ + +/* Forward declarations */ + +static int +mhd_tool_get_sys_cpu_count_sysctl_ (void); + +/** + * Detect the number of logical CPU cores available for the process by + * sched_getaffinity() (and related) function. + * @return the number of detected logical CPU cores or + * -1 if failed to detect (or this function unavailable). + */ +static int +mhd_tool_get_proc_cpu_count_sched_getaffinity_ (void) +{ + int ret = -1; +#if defined(HAVE_SCHED_GETAFFINITY) && defined(HAVE_GETPID) + /* Glibc style */ + if (0 >= ret) + { + cpu_set_t cur_set; + if (0 == sched_getaffinity (getpid (), sizeof(cur_set), &cur_set)) + { +#ifdef HAVE_CPU_COUNT + ret = CPU_COUNT (&cur_set); +#else /* ! HAVE_CPU_COUNT */ + unsigned int i; + ret = 0; + for (i = 0; i < CPU_SETSIZE_SAFE; ++i) + { + if (CPU_ISSET (i, &cur_set)) + ++ret; + } + if (0 == ret) + ret = -1; +#endif /* ! HAVE_CPU_COUNT */ + } + } +#ifdef HAVE_CPU_COUNT_S + if (0 >= ret) + { + /* Use 256 times larger size than size for default maximum CPU number. + Hopefully it would be enough even for exotic situations. */ + static const unsigned int set_size_cpus = 256 * CPU_SETSIZE; + const size_t set_size_bytes = CPU_ALLOC_SIZE (set_size_cpus); + cpu_set_t *p_set; + + p_set = CPU_ALLOC (set_size_cpus); + if (NULL != p_set) + { + if (0 == sched_getaffinity (getpid (), set_size_bytes, p_set)) + { +#ifndef MHD_FUNC_CPU_COUNT_S_GETS_CPUS + ret = CPU_COUNT_S (set_size_bytes, p_set); +#else /* MHD_FUNC_CPU_COUNT_S_GETS_CPUS */ + ret = CPU_COUNT_S (set_size_cpus, p_set); +#endif /* MHD_FUNC_CPU_COUNT_S_GETS_CPUS */ + } + CPU_FREE (p_set); + } + } +#endif /* HAVE_CPU_COUNT_S */ +#endif /* HAVE_SCHED_GETAFFINITY && HAVE_GETPID */ + if (0 >= ret) + return -1; + return ret; +} + + +/** + * Detect the number of logical CPU cores available for the process by + * cpuset_getaffinity() function. + * @return the number of detected logical CPU cores or + * -1 if failed to detect (or this function unavailable). + */ +static int +mhd_tool_get_proc_cpu_count_cpuset_getaffinity_ (void) +{ + int ret = -1; +#if defined(HAVE_CPUSET_GETAFFINITY) + /* FreeBSD style */ + if (0 >= ret) + { + cpuset_t cur_mask; + /* The should get "anonymous" mask/set. The anonymous mask is always + a subset of the assigned set (which is a subset of the root set). */ + if (0 == cpuset_getaffinity (CPU_LEVEL_WHICH, CPU_WHICH_PID, (id_t) -1, + sizeof(cur_mask), &cur_mask)) + { +#ifdef HAVE_CPU_COUNT + ret = CPU_COUNT (&cur_mask); +#else /* ! HAVE_CPU_COUNT */ + unsigned int i; + ret = 0; + for (i = 0; i < CPU_SETSIZE_SAFE; ++i) + { + if (CPU_ISSET (i, &cur_mask)) + ++ret; + } + if (0 == ret) + ret = -1; +#endif /* ! HAVE_CPU_COUNT */ + } + } +#ifdef HAVE_CPU_COUNT_S + if (0 >= ret) + { + /* Use 256 times larger size than size for default maximum CPU number. + Hopefully it would be enough even for exotic situations. */ + static const unsigned int mask_size_cpus = 256 * CPU_SETSIZE; + const size_t mask_size_bytes = CPU_ALLOC_SIZE (mask_size_cpus); + cpuset_t *p_mask; + + p_mask = CPU_ALLOC (mask_size_cpus); + if (NULL != p_mask) + { + if (0 == cpuset_getaffinity (CPU_LEVEL_WHICH, CPU_WHICH_PID, (id_t) -1, + mask_size_bytes, p_mask)) + { +#ifndef MHD_FUNC_CPU_COUNT_S_GETS_CPUS + ret = CPU_COUNT_S (mask_size_bytes, p_mask); +#else /* MHD_FUNC_CPU_COUNT_S_GETS_CPUS */ + ret = CPU_COUNT_S (mask_size_cpus, p_mask); +#endif /* MHD_FUNC_CPU_COUNT_S_GETS_CPUS */ + } + CPU_FREE (p_mask); + } + } +#endif /* HAVE_CPU_COUNT_S */ +#endif /* HAVE_CPUSET_GETAFFINITY */ + if (0 >= ret) + return -1; + return ret; +} + + +/** + * Detect the number of logical CPU cores available for the process by + * sched_getaffinity_np() (and related) function. + * @return the number of detected logical CPU cores or + * -1 if failed to detect (or this function unavailable). + */ +static int +mhd_tool_get_proc_cpu_count_sched_getaffinity_np_ (void) +{ + int ret = -1; +#if defined(HAVE_SCHED_GETAFFINITY_NP) && defined(HAVE_GETPID) + /* NetBSD style */ + cpuset_t *cpuset_ptr; + cpuset_ptr = cpuset_create (); + if (NULL != cpuset_ptr) + { + if (0 == sched_getaffinity_np (getpid (), cpuset_size (cpuset_ptr), + cpuset_ptr)) + { + cpuid_t cpu_num; +#if defined(HAVE_SYSCONF) && defined(HAVE_DECL__SC_NPROCESSORS_CONF) + unsigned int max_num = 0; + long sc_value = -1; + sc_value = sysconf (_SC_NPROCESSORS_ONLN); + if (0 < sc_value) + max_num = (unsigned int) sc_value; + if (0 < max_num) + { + ret = 0; + for (cpu_num = 0; cpu_num < max_num; ++cpu_num) + if (0 < cpuset_isset (cpu_num, cpuset_ptr)) + ++ret; + } + else /* Combined with the next 'if' */ +#endif /* HAVE_SYSCONF && HAVE_DECL__SC_NPROCESSORS_CONF */ + if (1) + { + int res; + cpu_num = 0; + ret = 0; + do + { + res = cpuset_isset (cpu_num++, cpuset_ptr); + if (0 < res) + ++ret; + } while (0 <= res); + } +#ifdef __NetBSD__ + if (0 == ret) + { + /* On NetBSD "unset" affinity (exactly zero CPUs) means + "all CPUs are available". */ + ret = mhd_tool_get_sys_cpu_count_sysctl_ (); + } +#endif /* __NetBSD__ */ + } + cpuset_destroy (cpuset_ptr); + } +#endif /* HAVE_SCHED_GETAFFINITY_NP && HAVE_GETPID */ + if (0 >= ret) + return -1; + return ret; +} + + +/** + * Detect the number of logical CPU cores available for the process. + * The number of cores available for this process could be different from + * value of cores available on the system. The OS may have limit on number + * assigned/allowed cores for single process and process may have limited + * CPU affinity. + * @return the number of logical CPU cores available for the process or + * -1 if failed to detect + */ +int +mhd_tool_get_proc_cpu_count (void) +{ + int res; + +#if defined(__linux__) || defined(__GLIBC__) + /* On Linux kernel try first 'sched_getaffinity()' as it should be + the native API. + Also try it first on other kernels if Glibc is used. */ + res = mhd_tool_get_proc_cpu_count_sched_getaffinity_ (); + if (0 < res) + return res; + + res = mhd_tool_get_proc_cpu_count_cpuset_getaffinity_ (); + if (0 < res) + return res; +#else /* ! __linux__ && ! __GLIBC__ */ + /* On non-Linux kernels 'cpuset_getaffinity()' could be the native API, + while 'sched_getaffinity()' could be implemented in compatibility layer. */ + res = mhd_tool_get_proc_cpu_count_cpuset_getaffinity_ (); + if (0 < res) + return res; + + res = mhd_tool_get_proc_cpu_count_sched_getaffinity_ (); + if (0 < res) + return res; +#endif /* ! __linux__ && ! __GLIBC__ */ + + res = mhd_tool_get_proc_cpu_count_sched_getaffinity_np_ (); + if (0 < res) + return res; + + return -1; +} + + +/** + * Detect the number of processors by special API functions + * @return number of processors as returned by special API functions or + * -1 in case of error or special API functions unavailable + */ +static int +mhd_tool_get_sys_cpu_count_special_api_ (void) +{ + int ret = -1; +#ifdef HAVE_PSTAT_GETDYNAMIC + if (0 >= ret) + { + /* HP-UX things */ + struct pst_dynamic psd_data; + memset ((void *) &psd_data, 0, sizeof(psd_data)); + if (1 == pstat_getdynamic (&psd_data, sizeof(psd_data), (size_t) 1, 0)) + { + if (0 < psd_data.psd_proc_cnt) + ret = (int) psd_data.psd_proc_cnt; + } + } +#endif /* HAVE_PSTAT_GETDYNAMIC */ +#ifdef HAVE_VXCPUENABLEDGET + if (0 >= ret) + { + /* VxWorks */ + cpuset_t enb_set; + enb_set = vxCpuEnabledGet (); + /* Count set bits */ + for (ret = 0; 0 != enb_set; enb_set &= enb_set - 1) + ++ret; + } +#endif /* HAVE_VXCPUENABLEDGET */ +#if defined(_WIN32) && ! defined (__CYGWIN__) + if (0 >= ret) + { + /* Native W32 */ + HMODULE k32hndl; + k32hndl = GetModuleHandleA ("kernel32.dll"); + if (NULL != k32hndl) + { + typedef DWORD (WINAPI *GAPC_PTR)(WORD GroupNumber); + GAPC_PTR ptrGetActiveProcessorCount; + /* Available on W7 or later */ + ptrGetActiveProcessorCount = + (GAPC_PTR) (void *) GetProcAddress (k32hndl, "GetActiveProcessorCount"); + if (NULL != ptrGetActiveProcessorCount) + { + DWORD res; + res = ptrGetActiveProcessorCount (ALL_PROCESSOR_GROUPS); + ret = (int) res; + if (res != (DWORD) ret) + ret = -1; /* Overflow */ + } + } + if ((0 >= ret) && (NULL != k32hndl)) + { + typedef void (WINAPI *GNSI_PTR)(SYSTEM_INFO *pSysInfo); + GNSI_PTR ptrGetNativeSystemInfo; + /* May give incorrect (low) result on versions from W7 to W11 + when more then 64 CPUs are available */ + ptrGetNativeSystemInfo = + (GNSI_PTR) (void *) GetProcAddress (k32hndl, "GetNativeSystemInfo"); + if (NULL != ptrGetNativeSystemInfo) + { + SYSTEM_INFO sysInfo; + + memset ((void *) &sysInfo, 0, sizeof(sysInfo)); + ptrGetNativeSystemInfo (&sysInfo); + ret = (int) sysInfo.dwNumberOfProcessors; + if (sysInfo.dwNumberOfProcessors != (DWORD) ret) + ret = -1; /* Overflow */ + } + } + } + if (0 >= ret) + { + /* May give incorrect (low) result on versions from W7 to W11 + when more then 64 CPUs are available */ + SYSTEM_INFO sysInfo; + memset ((void *) &sysInfo, 0, sizeof(sysInfo)); + GetSystemInfo (&sysInfo); + ret = (int) sysInfo.dwNumberOfProcessors; + if (sysInfo.dwNumberOfProcessors != (DWORD) ret) + ret = -1; /* Overflow */ + } +#endif /* _WIN32 && ! __CYGWIN__ */ + if (0 >= ret) + return -1; + return ret; +} + + +/** + * Detect the number of processors by sysctl*() functions + * @return number of processors as returned by 'sysctl*' functions or + * -1 in case of error or the number cannot be detected + * by these functions + */ +static int +mhd_tool_get_sys_cpu_count_sysctl_ (void) +{ + int ret = -1; + /* Do not use sysctl() function on GNU/Linux even if + sysctl() is available */ +#ifndef __linux__ +#ifdef HAVE_SYSCTLBYNAME + if (0 >= ret) + { + size_t value_size = sizeof(ret); + /* Darwin: The number of available logical CPUs */ + if ((0 != sysctlbyname ("hw.logicalcpu", &ret, &value_size, + NULL, 0)) + || (sizeof(ret) != value_size)) + ret = -1; + } + if (0 >= ret) + { + size_t value_size = sizeof(ret); + /* FreeBSD: The number of online CPUs */ + if ((0 != sysctlbyname ("kern.smp.cpus", &ret, &value_size, + NULL, 0)) + || (sizeof(ret) != value_size)) + ret = -1; + } + if (0 >= ret) + { + size_t value_size = sizeof(ret); + /* Darwin: The current number of CPUs available to run threads */ + if ((0 != sysctlbyname ("hw.activecpu", &ret, &value_size, + NULL, 0)) + || (sizeof(ret) != value_size)) + ret = -1; + } + if (0 >= ret) + { + size_t value_size = sizeof(ret); + /* OpenBSD, NetBSD: The number of online CPUs */ + if ((0 != sysctlbyname ("hw.ncpuonline", &ret, &value_size, + NULL, 0)) + || (sizeof(ret) != value_size)) + ret = -1; + } + if (0 >= ret) + { + size_t value_size = sizeof(ret); + /* Darwin: The old/alternative name for "hw.activecpu" */ + if ((0 != sysctlbyname ("hw.availcpu", &ret, &value_size, + NULL, 0)) + || (sizeof(ret) != value_size)) + ret = -1; + } +#endif /* HAVE_SYSCTLBYNAME */ +#if defined(HAVE_SYSCTL) && \ + defined(HAS_DECL_CTL_HW) && \ + defined(HAS_DECL_HW_NCPUONLINE) + if (0 >= ret) + { + /* OpenBSD, NetBSD: The number of online CPUs */ + int mib[2] = { CTL_HW, HW_NCPUONLINE }; + size_t value_size = sizeof(ret); + if ((0 != sysctl (mib, 2, &ret, &value_size, NULL, 0)) + || (sizeof(ret) != value_size)) + ret = -1; + } +#endif /* HAVE_SYSCTL && HAS_DECL_CTL_HW && HAS_DECL_HW_NCPUONLINE */ +#if defined(HAVE_SYSCTL) && \ + defined(HAS_DECL_CTL_HW) && \ + defined(HAS_DECL_HW_AVAILCPU) + if (0 >= ret) + { + /* Darwin: The MIB name for "hw.activecpu" */ + int mib[2] = { CTL_HW, HW_AVAILCPU }; + size_t value_size = sizeof(ret); + if ((0 != sysctl (mib, 2, &ret, &value_size, NULL, 0)) + || (sizeof(ret) != value_size)) + ret = -1; + } +#endif /* HAVE_SYSCTL && HAS_DECL_CTL_HW && HAS_DECL_HW_AVAILCPU */ +#ifdef HAVE_SYSCTLBYNAME + if (0 >= ret) + { + size_t value_size = sizeof(ret); + /* FreeBSD, OpenBSD, NetBSD, Darwin (and others?): The number of CPUs */ + if ((0 != sysctlbyname ("hw.ncpu", &ret, &value_size, + NULL, 0)) + || (sizeof(ret) != value_size)) + ret = -1; + } +#endif /* HAVE_SYSCTLBYNAME */ +#if defined(HAVE_SYSCTL) && \ + defined(HAS_DECL_CTL_HW) && \ + defined(HAS_DECL_HW_NCPU) + if (0 >= ret) + { + /* FreeBSD, OpenBSD, NetBSD, Darwin (and others?): The number of CPUs */ + int mib[2] = { CTL_HW, HW_NCPU }; + size_t value_size = sizeof(ret); + if ((0 != sysctl (mib, 2, &ret, &value_size, NULL, 0)) + || (sizeof(ret) != value_size)) + ret = -1; + } +#endif /* HAVE_SYSCTL && HAS_DECL_CTL_HW && HAS_DECL_HW_NCPU */ +#endif /* ! __linux__ */ + if (0 >= ret) + return -1; + return ret; +} + + +/** + * Detect the number of processors by sysconf() function + * @return number of processors as returned by 'sysconf' function or + * -1 in case of error or 'sysconf' unavailable + */ +static int +mhd_tool_get_sys_cpu_count_sysconf_ (void) +{ + int ret = -1; +#if defined(HAVE_SYSCONF) && \ + (defined(HAS_DECL__SC_NPROCESSORS_ONLN) || \ + defined(HAS_DECL__SC_NPROC_ONLN) || \ + defined(HAS_DECL__SC_CRAY_NCPU)) + long value = -1; +#ifdef HAS_DECL__SC_NPROCESSORS_ONLN + if (0 >= value) + value = sysconf (_SC_NPROCESSORS_ONLN); +#endif /* HAS_DECL__SC_NPROCESSORS_ONLN */ +#ifdef HAS_DECL__SC_NPROC_ONLN + if (0 >= value) + value = sysconf (_SC_NPROC_ONLN); +#endif /* HAS_DECL__SC_NPROC_ONLN */ +#ifdef HAS_DECL__SC_CRAY_NCPU + if (0 >= value) + value = sysconf (_SC_CRAY_NCPU); +#endif /* HAS_DECL__SC_CRAY_NCPU */ + if (0 >= value) + return -1; + ret = (int) value; + if ((long) ret != value) + return -1; /* Overflow */ +#endif /* HAVE_SYSCONF && + (HAS_DECL__SC_NPROCESSORS_ONLN || HAS_DECL__SC_NPROC_ONLN || + HAS_DECL__SC_CRAY_NCPU) */ + return ret; +} + + +/** + * Try to detect the number of logical CPU cores available for the system. + * The number of available logical CPU cores could be changed any time due to + * CPU hotplug. + * @return the number of logical CPU cores available, + * -1 if failed to detect. + */ +int +mhd_tool_get_system_cpu_count (void) +{ + int res; + + /* Try specialised APIs first */ + res = mhd_tool_get_sys_cpu_count_special_api_ (); + if (0 < res) + return res; + + /* Try sysctl*(). This is typically a direct interface to + kernel values. */ + res = mhd_tool_get_sys_cpu_count_sysctl_ (); + if (0 < res) + return res; + + /* Try sysconf() as the last resort as this is a generic interface + which can be implemented by parsing system files. */ + res = mhd_tool_get_sys_cpu_count_sysconf_ (); +#if ! defined(__linux__) && ! defined(__GLIBC__) + if (0 < res) + return res; +#else /* __linux__ || __GLIBC__ */ + if (2 < res) + return res; + if (0 < res) + { + /* '1' or '2' could a be fallback number. + * See get_nprocs_fallback() in glibc + sysdeps/unix/sysv/linux/getsysstats.c */ + + int proc_cpu_count; + + proc_cpu_count = mhd_tool_get_proc_cpu_count (); + if ((0 < proc_cpu_count) && (proc_cpu_count <= res)) + { + /* The detected number of CPUs available for the process + is 1 or 2 and fits detected number of system CPUS. + Assume detected number is correct. */ + return res; + } + } +#endif /* __linux__ || __GLIBC__ */ + return -1; /* Cannot detect */ +} diff --git a/src/tools/mhd_tool_get_cpu_count.h b/src/tools/mhd_tool_get_cpu_count.h @@ -0,0 +1,55 @@ +/* + This file is part of GNU libmicrohttpd + Copyright (C) 2023 Evgeny Grin (Karlson2k) + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/** + * @file tools/mhd_tool_get_cpu_count.h + * @brief Declaration of functions to detect the number of available + * CPU cores. + * @author Karlson2k (Evgeny Grin) + */ + + +#ifndef SRC_TOOLS_MHD_TOOL_GET_CPU_COUNT_H_ +#define SRC_TOOLS_MHD_TOOL_GET_CPU_COUNT_H_ 1 + + +/** + * Detect the number of logical CPU cores available for the process. + * The number of cores available for this process could be different from + * value of cores available on the system. The OS may have limit on number + * assigned/allowed cores for single process and process may have limited + * CPU affinity. + * @return the number of logical CPU cores available for the process or + * -1 if failed to detect + */ +int +mhd_tool_get_proc_cpu_count (void); + + +/** + * Try to detect the number of logical CPU cores available for the system. + * The number of available logical CPU cores could be changed any time due to + * CPU hotplug. + * @return the number of logical CPU cores available, + * -1 if failed to detect. + */ +int +mhd_tool_get_system_cpu_count (void); + +#endif /* SRC_TOOLS_MHD_TOOL_GET_CPU_COUNT_H_ */ diff --git a/src/tools/perf_replies.c b/src/tools/perf_replies.c @@ -39,6 +39,7 @@ #include "mhd_options.h" #include "microhttpd.h" #include "mhd_tool_str_to_uint.h" +#include "mhd_tool_get_cpu_count.h" #if defined(MHD_REAL_CPU_COUNT) #if MHD_REAL_CPU_COUNT == 0 @@ -165,28 +166,37 @@ get_cmd_out_as_number (const char *cmd) static unsigned int detect_cpu_core_count (void) { - int sys_cpu_count = -1; -#if ! defined(_WIN32) || defined(__CYGWIN__) - sys_cpu_count = get_cmd_out_as_number ("nproc 2>/dev/null"); -#endif /* ! _WIN32) || __CYGWIN__ */ -#ifdef _WIN32 - if (0 >= sys_cpu_count) - sys_cpu_count = get_cmd_out_as_number ("echo %NUMBER_OF_PROCESSORS%"); -#endif /* _WIN32 */ + int sys_cpu_count; + sys_cpu_count = mhd_tool_get_system_cpu_count (); if (0 >= sys_cpu_count) { - fprintf (stderr, "Failed to detect the number of available CPU cores.\n"); + int proc_cpu_count; + fprintf (stderr, "Failed to detect the number of logical CPU cores " + "available on the system.\n"); + proc_cpu_count = mhd_tool_get_proc_cpu_count (); + if (0 < proc_cpu_count) + { + fprintf (stderr, "The number of CPU cores available for this process " + "is used as a fallback.\n"); + sys_cpu_count = proc_cpu_count; + } #ifdef MHD_REAL_CPU_COUNT - fprintf (stderr, "Hardcoded number is used as a fallback.\n"); - sys_cpu_count = MHD_REAL_CPU_COUNT; + if (0 >= sys_cpu_count) + { + fprintf (stderr, "configure-detected hardcoded number is used " + "as a fallback.\n"); + sys_cpu_count = MHD_REAL_CPU_COUNT; + } #endif if (0 >= sys_cpu_count) sys_cpu_count = 1; - printf ("Assuming %d CPU cores.\n", sys_cpu_count); + printf ("Assuming %d logical CPU core%s on this system.\n", sys_cpu_count, + (1 == sys_cpu_count) ? "" : "s"); } else { - printf ("Detected %d CPU cores.\n", sys_cpu_count); + printf ("Detected %d logical CPU core%s on this system.\n", sys_cpu_count, + (1 == sys_cpu_count) ? "" : "s"); } return (unsigned int) sys_cpu_count; } @@ -202,20 +212,84 @@ get_cpu_core_count (void) } +static unsigned int +detect_process_cpu_core_count (void) +{ + unsigned int num_proc_cpu_cores; + unsigned int sys_cpu_cores; + int res; + + sys_cpu_cores = get_cpu_core_count (); + res = mhd_tool_get_proc_cpu_count (); + if (0 > res) + { + fprintf (stderr, "Cannot detect the number of logical CPU cores available " + "for this process.\n"); + if (1 != sys_cpu_cores) + printf ("Assuming all %u system logical CPU cores are available to run " + "threads of this process.\n", sys_cpu_cores); + else + printf ("Assuming single logical CPU core available for this process.\n"); + num_proc_cpu_cores = sys_cpu_cores; + } + else + { + printf ("Detected %d logical CPU core%s available to run threads " + "of this process.\n", res, (1 == res) ? "" : "s"); + num_proc_cpu_cores = (unsigned int) res; + } + if (num_proc_cpu_cores > sys_cpu_cores) + { + fprintf (stderr, "WARNING: Detected number of CPU cores available " + "for this process (%u) is larger than detected number " + "of CPU cores on the system (%u).\n", + num_proc_cpu_cores, sys_cpu_cores); + num_proc_cpu_cores = sys_cpu_cores; + fprintf (stderr, "Using %u as the number of logical CPU cores available " + "for this process.\n", num_proc_cpu_cores); + } + return num_proc_cpu_cores; +} + + +static unsigned int +get_process_cpu_core_count (void) +{ + static unsigned int proc_num_cpu_cores = 0; + if (0 == proc_num_cpu_cores) + proc_num_cpu_cores = detect_process_cpu_core_count (); + return proc_num_cpu_cores; +} + + static unsigned int num_threads = 0; static unsigned int get_num_threads (void) { static const unsigned int max_threads = 32; + if (0 < num_threads) + return num_threads; + + num_threads = get_cpu_core_count () / 2; if (0 == num_threads) + num_threads = 1; + else { - num_threads = get_cpu_core_count () / 2; - if (0 == num_threads) - num_threads = 1; - else + unsigned int num_proc_cpus; + num_proc_cpus = get_process_cpu_core_count (); + if (num_proc_cpus >= num_threads) + { printf ("Using half of all available CPU cores, assuming the other half " "is used by client / requests generator.\n"); + } + else + { + printf ("Using all CPU cores available for this process as more than " + "half of CPU cores on this system are still available for use " + "by client / requests generator.\n"); + num_threads = num_proc_cpus; + } } if (max_threads < num_threads) { @@ -1059,9 +1133,10 @@ check_apply_param__all_cpus (void) if (! tool_params.all_cpus) return; - num_threads = get_cpu_core_count (); + num_threads = get_process_cpu_core_count (); printf ("Requested use of all available CPU cores for MHD threads.\n"); - print_all_cores_used (); + if (get_cpu_core_count () == num_threads) + print_all_cores_used (); } @@ -1075,20 +1150,21 @@ check_apply_param__threads (void) return; num_threads = tool_params.threads; + + if (get_process_cpu_core_count () < num_threads) + { + fprintf (stderr, "WARNING: The requested number of threads (%u) is " + "higher than the number of detected available CPU cores (%u).\n", + num_threads, get_process_cpu_core_count ()); + fprintf (stderr, "This decreases the performance. " + "Consider using fewer threads.\n"); + } if (get_cpu_core_count () == num_threads) { printf ("The requested number of threads is equal to the number of " "detected CPU cores.\n"); print_all_cores_used (); } - else if (get_cpu_core_count () < num_threads) - { - fprintf (stderr, "WARNING: The requested number of threads (%u) is " - "higher than the number of detected CPU cores (%u).\n", - num_threads, get_cpu_core_count ()); - fprintf (stderr, "This decreases the performance. " - "Consider using fewer threads.\n"); - } } @@ -1228,7 +1304,7 @@ static struct MHD_Response *resp_single = NULL; The system will keep it in cache. */ static const char tiny_body[] = "Hi!"; static char *body_dyn = NULL; /* Non-static body data */ -size_t body_dyn_size; +static size_t body_dyn_size; /* Non-zero - success, zero - failure */ static int