From 448ba7050ba849a5bcb0dcd0bbb64a7537394dee Mon Sep 17 00:00:00 2001 From: "Bruce A. Mah" Date: Fri, 22 Jul 2022 14:47:15 -0700 Subject: Add (nonfunctional) worker thread per stream. The worker threads don't actually do anything, but with this change we can create and destroy threads roughly right. Towards IPERF-124. --- src/iperf.h | 4 +++ src/iperf_client_api.c | 81 ++++++++++++++++++++++++++++++++++++++++++++++++++ src/iperf_server_api.c | 46 ++++++++++++++++++++++++++++ 3 files changed, 131 insertions(+) diff --git a/src/iperf.h b/src/iperf.h index c3ce333..e184171 100644 --- a/src/iperf.h +++ b/src/iperf.h @@ -73,6 +73,8 @@ #include #endif // HAVE_SSL +#include + #if !defined(__IPERF_API_H) typedef uint64_t iperf_size_t; #endif // __IPERF_API_H @@ -175,6 +177,8 @@ struct iperf_stream { struct iperf_test* test; + pthread_t thr; + /* configurable members */ int local_port; int remote_port; diff --git a/src/iperf_client_api.c b/src/iperf_client_api.c index 8971ef1..7c927a3 100644 --- a/src/iperf_client_api.c +++ b/src/iperf_client_api.c @@ -51,6 +51,19 @@ #endif /* TCP_CA_NAME_MAX */ #endif /* HAVE_TCP_CONGESTION */ +void * +iperf_client_worker_start(void *s) { + struct iperf_stream *sp = (struct iperf_stream *) s; + struct iperf_test *test = sp->test; + + while (1) { + if (test->debug_level >= DEBUG_LEVEL_INFO) { + iperf_printf(test, "Thread FD %d\n", sp->socket); + } + sleep(1); + } +} + int iperf_create_streams(struct iperf_test *test, int sender) { @@ -620,6 +633,23 @@ iperf_run_client(struct iperf_test * test) if (startup) { startup = 0; + /* Create and spin up threads */ + pthread_attr_t attr; + pthread_attr_init(&attr); + + SLIST_FOREACH(sp, &test->streams, streams) { + if (pthread_create(&(sp->thr), &attr, &iperf_client_worker_start, sp) != 0) { + perror("pthread_create"); + } + if (test->debug_level >= DEBUG_LEVEL_INFO) { + iperf_printf(test, "Thread FD %d created\n", sp->socket); + } + } + if (test->debug_level >= DEBUG_LEVEL_INFO) { + iperf_printf(test, "All threads created\n"); + } + pthread_attr_destroy(&attr); + // Set non-blocking for non-UDP tests if (test->protocol->id != Pudp) { SLIST_FOREACH(sp, &test->streams, streams) { @@ -665,6 +695,22 @@ iperf_run_client(struct iperf_test * test) (test->settings->blocks != 0 && (test->blocks_sent >= test->settings->blocks || test->blocks_received >= test->settings->blocks)))) { + /* Cancel sender threads */ + SLIST_FOREACH(sp, &test->streams, streams) { + if (sp->sender) { + if (pthread_cancel(sp->thr) != 0) { + perror("pthread_cancel"); + } + sp->thr = 0; + if (test->debug_level >= DEBUG_LEVEL_INFO) { + iperf_printf(test, "Thread FD %d cancelled\n", sp->socket); + } + } + } + if (test->debug_level >= DEBUG_LEVEL_INFO) { + iperf_printf(test, "Sender threads cancelled\n"); + } + // Unset non-blocking for non-UDP tests if (test->protocol->id != Pudp) { SLIST_FOREACH(sp, &test->streams, streams) { @@ -691,6 +737,25 @@ iperf_run_client(struct iperf_test * test) } } + /* Cancel receiver threads */ + SLIST_FOREACH(sp, &test->streams, streams) { + if (!sp->sender) { + if (pthread_cancel(sp->thr) != 0) { + perror("pthread_cancel"); + } + if (pthread_join(sp->thr, NULL) != 0) { + perror("pthread_join"); + } + sp->thr = 0; + if (test->debug_level >= DEBUG_LEVEL_INFO) { + iperf_printf(test, "Thread FD %d cancelled\n", sp->socket); + } + } + } + if (test->debug_level >= DEBUG_LEVEL_INFO) { + iperf_printf(test, "Receiver threads cancelled\n"); + } + if (test->json_output) { if (iperf_json_finish(test) < 0) return -1; @@ -704,6 +769,22 @@ iperf_run_client(struct iperf_test * test) return 0; cleanup_and_fail: + /* Cancel all threads */ + SLIST_FOREACH(sp, &test->streams, streams) { + if (pthread_cancel(sp->thr) != 0) { + perror("pthread_cancel"); + } + if (pthread_join(sp->thr, NULL) != 0) { + perror("pthread_join"); + } + if (test->debug >= DEBUG_LEVEL_INFO) { + iperf_printf(test, "Thread FD %d cancelled\n", sp->socket); + } + } + if (test->debug_level >= DEBUG_LEVEL_INFO) { + iperf_printf(test, "All threads cancelled\n"); + } + iperf_client_end(test); if (test->json_output) { cJSON_AddStringToObject(test->json_top, "error", iperf_strerror(i_errno)); diff --git a/src/iperf_server_api.c b/src/iperf_server_api.c index ae916f5..8ac3fd9 100644 --- a/src/iperf_server_api.c +++ b/src/iperf_server_api.c @@ -66,6 +66,19 @@ #endif /* TCP_CA_NAME_MAX */ #endif /* HAVE_TCP_CONGESTION */ +void * +iperf_server_worker_start(void *s) { + struct iperf_stream *sp = (struct iperf_stream *) s; + struct iperf_test *test = sp->test; + + while (1) { + if (test->debug_level >= DEBUG_LEVEL_INFO) { + iperf_printf(test, "Thread FD %d\n", sp->socket); + } + sleep(1); + } +} + int iperf_server_listen(struct iperf_test *test) { @@ -388,6 +401,22 @@ cleanup_server(struct iperf_test *test) { struct iperf_stream *sp; + /* Cancel threads */ + SLIST_FOREACH(sp, &test->streams, streams) { + if (pthread_cancel(sp->thr) != 0) { + perror("pthread_cancel"); + } + if (pthread_join(sp->thr, NULL) != 0) { + perror("pthread_join"); + } + if (test->debug >= DEBUG_LEVEL_INFO) { + iperf_printf(test, "Thread FD %d cancelled\n", sp->socket); + } + } + if (test->debug_level >= DEBUG_LEVEL_INFO) { + iperf_printf(test, "All threads cancelled\n"); + } + /* Close open streams */ SLIST_FOREACH(sp, &test->streams, streams) { if (sp->socket > -1) { @@ -803,6 +832,23 @@ iperf_run_server(struct iperf_test *test) cleanup_server(test); return -1; } + + /* Create and spin up threads */ + pthread_attr_t attr; + pthread_attr_init(&attr); + + SLIST_FOREACH(sp, &test->streams, streams) { + if (pthread_create(&(sp->thr), &attr, &iperf_server_worker_start, sp) != 0) { + perror("pthread_create"); + } + if (test->debug_level >= DEBUG_LEVEL_INFO) { + iperf_printf(test, "Thread FD %d created\n", sp->socket); + } + } + if (test->debug_level >= DEBUG_LEVEL_INFO) { + iperf_printf(test, "All threads created\n"); + } + pthread_attr_destroy(&attr); } } -- cgit v1.2.3 From b4c23ab08c021452ba8cdcfe2723ae9764472226 Mon Sep 17 00:00:00 2001 From: "Bruce A. Mah" Date: Mon, 25 Jul 2022 18:46:42 -0700 Subject: Add autoconf support for POSIX threads. --- config/ax_pthread.m4 | 522 +++++++++++++++++++++++++++++++++++++++++++++++++++ configure.ac | 10 + src/iperf_util.c | 10 + 3 files changed, 542 insertions(+) create mode 100644 config/ax_pthread.m4 diff --git a/config/ax_pthread.m4 b/config/ax_pthread.m4 new file mode 100644 index 0000000..9f35d13 --- /dev/null +++ b/config/ax_pthread.m4 @@ -0,0 +1,522 @@ +# =========================================================================== +# https://www.gnu.org/software/autoconf-archive/ax_pthread.html +# =========================================================================== +# +# SYNOPSIS +# +# AX_PTHREAD([ACTION-IF-FOUND[, ACTION-IF-NOT-FOUND]]) +# +# DESCRIPTION +# +# This macro figures out how to build C programs using POSIX threads. It +# sets the PTHREAD_LIBS output variable to the threads library and linker +# flags, and the PTHREAD_CFLAGS output variable to any special C compiler +# flags that are needed. (The user can also force certain compiler +# flags/libs to be tested by setting these environment variables.) +# +# Also sets PTHREAD_CC and PTHREAD_CXX to any special C compiler that is +# needed for multi-threaded programs (defaults to the value of CC +# respectively CXX otherwise). (This is necessary on e.g. AIX to use the +# special cc_r/CC_r compiler alias.) +# +# NOTE: You are assumed to not only compile your program with these flags, +# but also to link with them as well. For example, you might link with +# $PTHREAD_CC $CFLAGS $PTHREAD_CFLAGS $LDFLAGS ... $PTHREAD_LIBS $LIBS +# $PTHREAD_CXX $CXXFLAGS $PTHREAD_CFLAGS $LDFLAGS ... $PTHREAD_LIBS $LIBS +# +# If you are only building threaded programs, you may wish to use these +# variables in your default LIBS, CFLAGS, and CC: +# +# LIBS="$PTHREAD_LIBS $LIBS" +# CFLAGS="$CFLAGS $PTHREAD_CFLAGS" +# CXXFLAGS="$CXXFLAGS $PTHREAD_CFLAGS" +# CC="$PTHREAD_CC" +# CXX="$PTHREAD_CXX" +# +# In addition, if the PTHREAD_CREATE_JOINABLE thread-attribute constant +# has a nonstandard name, this macro defines PTHREAD_CREATE_JOINABLE to +# that name (e.g. PTHREAD_CREATE_UNDETACHED on AIX). +# +# Also HAVE_PTHREAD_PRIO_INHERIT is defined if pthread is found and the +# PTHREAD_PRIO_INHERIT symbol is defined when compiling with +# PTHREAD_CFLAGS. +# +# ACTION-IF-FOUND is a list of shell commands to run if a threads library +# is found, and ACTION-IF-NOT-FOUND is a list of commands to run it if it +# is not found. If ACTION-IF-FOUND is not specified, the default action +# will define HAVE_PTHREAD. +# +# Please let the authors know if this macro fails on any platform, or if +# you have any other suggestions or comments. This macro was based on work +# by SGJ on autoconf scripts for FFTW (http://www.fftw.org/) (with help +# from M. Frigo), as well as ac_pthread and hb_pthread macros posted by +# Alejandro Forero Cuervo to the autoconf macro repository. We are also +# grateful for the helpful feedback of numerous users. +# +# Updated for Autoconf 2.68 by Daniel Richard G. +# +# LICENSE +# +# Copyright (c) 2008 Steven G. Johnson +# Copyright (c) 2011 Daniel Richard G. +# Copyright (c) 2019 Marc Stevens +# +# This program is free software: you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by the +# Free Software Foundation, either version 3 of the License, or (at your +# option) any later version. +# +# This program 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 General +# Public License for more details. +# +# You should have received a copy of the GNU General Public License along +# with this program. If not, see . +# +# As a special exception, the respective Autoconf Macro's copyright owner +# gives unlimited permission to copy, distribute and modify the configure +# scripts that are the output of Autoconf when processing the Macro. You +# need not follow the terms of the GNU General Public License when using +# or distributing such scripts, even though portions of the text of the +# Macro appear in them. The GNU General Public License (GPL) does govern +# all other use of the material that constitutes the Autoconf Macro. +# +# This special exception to the GPL applies to versions of the Autoconf +# Macro released by the Autoconf Archive. When you make and distribute a +# modified version of the Autoconf Macro, you may extend this special +# exception to the GPL to apply to your modified version as well. + +#serial 31 + +AU_ALIAS([ACX_PTHREAD], [AX_PTHREAD]) +AC_DEFUN([AX_PTHREAD], [ +AC_REQUIRE([AC_CANONICAL_HOST]) +AC_REQUIRE([AC_PROG_CC]) +AC_REQUIRE([AC_PROG_SED]) +AC_LANG_PUSH([C]) +ax_pthread_ok=no + +# We used to check for pthread.h first, but this fails if pthread.h +# requires special compiler flags (e.g. on Tru64 or Sequent). +# It gets checked for in the link test anyway. + +# First of all, check if the user has set any of the PTHREAD_LIBS, +# etcetera environment variables, and if threads linking works using +# them: +if test "x$PTHREAD_CFLAGS$PTHREAD_LIBS" != "x"; then + ax_pthread_save_CC="$CC" + ax_pthread_save_CFLAGS="$CFLAGS" + ax_pthread_save_LIBS="$LIBS" + AS_IF([test "x$PTHREAD_CC" != "x"], [CC="$PTHREAD_CC"]) + AS_IF([test "x$PTHREAD_CXX" != "x"], [CXX="$PTHREAD_CXX"]) + CFLAGS="$CFLAGS $PTHREAD_CFLAGS" + LIBS="$PTHREAD_LIBS $LIBS" + AC_MSG_CHECKING([for pthread_join using $CC $PTHREAD_CFLAGS $PTHREAD_LIBS]) + AC_LINK_IFELSE([AC_LANG_CALL([], [pthread_join])], [ax_pthread_ok=yes]) + AC_MSG_RESULT([$ax_pthread_ok]) + if test "x$ax_pthread_ok" = "xno"; then + PTHREAD_LIBS="" + PTHREAD_CFLAGS="" + fi + CC="$ax_pthread_save_CC" + CFLAGS="$ax_pthread_save_CFLAGS" + LIBS="$ax_pthread_save_LIBS" +fi + +# We must check for the threads library under a number of different +# names; the ordering is very important because some systems +# (e.g. DEC) have both -lpthread and -lpthreads, where one of the +# libraries is broken (non-POSIX). + +# Create a list of thread flags to try. Items with a "," contain both +# C compiler flags (before ",") and linker flags (after ","). Other items +# starting with a "-" are C compiler flags, and remaining items are +# library names, except for "none" which indicates that we try without +# any flags at all, and "pthread-config" which is a program returning +# the flags for the Pth emulation library. + +ax_pthread_flags="pthreads none -Kthread -pthread -pthreads -mthreads pthread --thread-safe -mt pthread-config" + +# The ordering *is* (sometimes) important. Some notes on the +# individual items follow: + +# pthreads: AIX (must check this before -lpthread) +# none: in case threads are in libc; should be tried before -Kthread and +# other compiler flags to prevent continual compiler warnings +# -Kthread: Sequent (threads in libc, but -Kthread needed for pthread.h) +# -pthread: Linux/gcc (kernel threads), BSD/gcc (userland threads), Tru64 +# (Note: HP C rejects this with "bad form for `-t' option") +# -pthreads: Solaris/gcc (Note: HP C also rejects) +# -mt: Sun Workshop C (may only link SunOS threads [-lthread], but it +# doesn't hurt to check since this sometimes defines pthreads and +# -D_REENTRANT too), HP C (must be checked before -lpthread, which +# is present but should not be used directly; and before -mthreads, +# because the compiler interprets this as "-mt" + "-hreads") +# -mthreads: Mingw32/gcc, Lynx/gcc +# pthread: Linux, etcetera +# --thread-safe: KAI C++ +# pthread-config: use pthread-config program (for GNU Pth library) + +case $host_os in + + freebsd*) + + # -kthread: FreeBSD kernel threads (preferred to -pthread since SMP-able) + # lthread: LinuxThreads port on FreeBSD (also preferred to -pthread) + + ax_pthread_flags="-kthread lthread $ax_pthread_flags" + ;; + + hpux*) + + # From the cc(1) man page: "[-mt] Sets various -D flags to enable + # multi-threading and also sets -lpthread." + + ax_pthread_flags="-mt -pthread pthread $ax_pthread_flags" + ;; + + openedition*) + + # IBM z/OS requires a feature-test macro to be defined in order to + # enable POSIX threads at all, so give the user a hint if this is + # not set. (We don't define these ourselves, as they can affect + # other portions of the system API in unpredictable ways.) + + AC_EGREP_CPP([AX_PTHREAD_ZOS_MISSING], + [ +# if !defined(_OPEN_THREADS) && !defined(_UNIX03_THREADS) + AX_PTHREAD_ZOS_MISSING +# endif + ], + [AC_MSG_WARN([IBM z/OS requires -D_OPEN_THREADS or -D_UNIX03_THREADS to enable pthreads support.])]) + ;; + + solaris*) + + # On Solaris (at least, for some versions), libc contains stubbed + # (non-functional) versions of the pthreads routines, so link-based + # tests will erroneously succeed. (N.B.: The stubs are missing + # pthread_cleanup_push, or rather a function called by this macro, + # so we could check for that, but who knows whether they'll stub + # that too in a future libc.) So we'll check first for the + # standard Solaris way of linking pthreads (-mt -lpthread). + + ax_pthread_flags="-mt,-lpthread pthread $ax_pthread_flags" + ;; +esac + +# Are we compiling with Clang? + +AC_CACHE_CHECK([whether $CC is Clang], + [ax_cv_PTHREAD_CLANG], + [ax_cv_PTHREAD_CLANG=no + # Note that Autoconf sets GCC=yes for Clang as well as GCC + if test "x$GCC" = "xyes"; then + AC_EGREP_CPP([AX_PTHREAD_CC_IS_CLANG], + [/* Note: Clang 2.7 lacks __clang_[a-z]+__ */ +# if defined(__clang__) && defined(__llvm__) + AX_PTHREAD_CC_IS_CLANG +# endif + ], + [ax_cv_PTHREAD_CLANG=yes]) + fi + ]) +ax_pthread_clang="$ax_cv_PTHREAD_CLANG" + + +# GCC generally uses -pthread, or -pthreads on some platforms (e.g. SPARC) + +# Note that for GCC and Clang -pthread generally implies -lpthread, +# except when -nostdlib is passed. +# This is problematic using libtool to build C++ shared libraries with pthread: +# [1] https://gcc.gnu.org/bugzilla/show_bug.cgi?id=25460 +# [2] https://bugzilla.redhat.com/show_bug.cgi?id=661333 +# [3] https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=468555 +# To solve this, first try -pthread together with -lpthread for GCC + +AS_IF([test "x$GCC" = "xyes"], + [ax_pthread_flags="-pthread,-lpthread -pthread -pthreads $ax_pthread_flags"]) + +# Clang takes -pthread (never supported any other flag), but we'll try with -lpthread first + +AS_IF([test "x$ax_pthread_clang" = "xyes"], + [ax_pthread_flags="-pthread,-lpthread -pthread"]) + + +# The presence of a feature test macro requesting re-entrant function +# definitions is, on some systems, a strong hint that pthreads support is +# correctly enabled + +case $host_os in + darwin* | hpux* | linux* | osf* | solaris*) + ax_pthread_check_macro="_REENTRANT" + ;; + + aix*) + ax_pthread_check_macro="_THREAD_SAFE" + ;; + + *) + ax_pthread_check_macro="--" + ;; +esac +AS_IF([test "x$ax_pthread_check_macro" = "x--"], + [ax_pthread_check_cond=0], + [ax_pthread_check_cond="!defined($ax_pthread_check_macro)"]) + + +if test "x$ax_pthread_ok" = "xno"; then +for ax_pthread_try_flag in $ax_pthread_flags; do + + case $ax_pthread_try_flag in + none) + AC_MSG_CHECKING([whether pthreads work without any flags]) + ;; + + *,*) + PTHREAD_CFLAGS=`echo $ax_pthread_try_flag | sed "s/^\(.*\),\(.*\)$/\1/"` + PTHREAD_LIBS=`echo $ax_pthread_try_flag | sed "s/^\(.*\),\(.*\)$/\2/"` + AC_MSG_CHECKING([whether pthreads work with "$PTHREAD_CFLAGS" and "$PTHREAD_LIBS"]) + ;; + + -*) + AC_MSG_CHECKING([whether pthreads work with $ax_pthread_try_flag]) + PTHREAD_CFLAGS="$ax_pthread_try_flag" + ;; + + pthread-config) + AC_CHECK_PROG([ax_pthread_config], [pthread-config], [yes], [no]) + AS_IF([test "x$ax_pthread_config" = "xno"], [continue]) + PTHREAD_CFLAGS="`pthread-config --cflags`" + PTHREAD_LIBS="`pthread-config --ldflags` `pthread-config --libs`" + ;; + + *) + AC_MSG_CHECKING([for the pthreads library -l$ax_pthread_try_flag]) + PTHREAD_LIBS="-l$ax_pthread_try_flag" + ;; + esac + + ax_pthread_save_CFLAGS="$CFLAGS" + ax_pthread_save_LIBS="$LIBS" + CFLAGS="$CFLAGS $PTHREAD_CFLAGS" + LIBS="$PTHREAD_LIBS $LIBS" + + # Check for various functions. We must include pthread.h, + # since some functions may be macros. (On the Sequent, we + # need a special flag -Kthread to make this header compile.) + # We check for pthread_join because it is in -lpthread on IRIX + # while pthread_create is in libc. We check for pthread_attr_init + # due to DEC craziness with -lpthreads. We check for + # pthread_cleanup_push because it is one of the few pthread + # functions on Solaris that doesn't have a non-functional libc stub. + # We try pthread_create on general principles. + + AC_LINK_IFELSE([AC_LANG_PROGRAM([#include +# if $ax_pthread_check_cond +# error "$ax_pthread_check_macro must be defined" +# endif + static void *some_global = NULL; + static void routine(void *a) + { + /* To avoid any unused-parameter or + unused-but-set-parameter warning. */ + some_global = a; + } + static void *start_routine(void *a) { return a; }], + [pthread_t th; pthread_attr_t attr; + pthread_create(&th, 0, start_routine, 0); + pthread_join(th, 0); + pthread_attr_init(&attr); + pthread_cleanup_push(routine, 0); + pthread_cleanup_pop(0) /* ; */])], + [ax_pthread_ok=yes], + []) + + CFLAGS="$ax_pthread_save_CFLAGS" + LIBS="$ax_pthread_save_LIBS" + + AC_MSG_RESULT([$ax_pthread_ok]) + AS_IF([test "x$ax_pthread_ok" = "xyes"], [break]) + + PTHREAD_LIBS="" + PTHREAD_CFLAGS="" +done +fi + + +# Clang needs special handling, because older versions handle the -pthread +# option in a rather... idiosyncratic way + +if test "x$ax_pthread_clang" = "xyes"; then + + # Clang takes -pthread; it has never supported any other flag + + # (Note 1: This will need to be revisited if a system that Clang + # supports has POSIX threads in a separate library. This tends not + # to be the way of modern systems, but it's conceivable.) + + # (Note 2: On some systems, notably Darwin, -pthread is not needed + # to get POSIX threads support; the API is always present and + # active. We could reasonably leave PTHREAD_CFLAGS empty. But + # -pthread does define _REENTRANT, and while the Darwin headers + # ignore this macro, third-party headers might not.) + + # However, older versions of Clang make a point of warning the user + # that, in an invocation where only linking and no compilation is + # taking place, the -pthread option has no effect ("argument unused + # during compilation"). They expect -pthread to be passed in only + # when source code is being compiled. + # + # Problem is, this is at odds with the way Automake and most other + # C build frameworks function, which is that the same flags used in + # compilation (CFLAGS) are also used in linking. Many systems + # supported by AX_PTHREAD require exactly this for POSIX threads + # support, and in fact it is often not straightforward to specify a + # flag that is used only in the compilation phase and not in + # linking. Such a scenario is extremely rare in practice. + # + # Even though use of the -pthread flag in linking would only print + # a warning, this can be a nuisance for well-run software projects + # that build with -Werror. So if the active version of Clang has + # this misfeature, we search for an option to squash it. + + AC_CACHE_CHECK([whether Clang needs flag to prevent "argument unused" warning when linking with -pthread], + [ax_cv_PTHREAD_CLANG_NO_WARN_FLAG], + [ax_cv_PTHREAD_CLANG_NO_WARN_FLAG=unknown + # Create an alternate version of $ac_link that compiles and + # links in two steps (.c -> .o, .o -> exe) instead of one + # (.c -> exe), because the warning occurs only in the second + # step + ax_pthread_save_ac_link="$ac_link" + ax_pthread_sed='s/conftest\.\$ac_ext/conftest.$ac_objext/g' + ax_pthread_link_step=`AS_ECHO(["$ac_link"]) | sed "$ax_pthread_sed"` + ax_pthread_2step_ac_link="($ac_compile) && (echo ==== >&5) && ($ax_pthread_link_step)" + ax_pthread_save_CFLAGS="$CFLAGS" + for ax_pthread_try in '' -Qunused-arguments -Wno-unused-command-line-argument unknown; do + AS_IF([test "x$ax_pthread_try" = "xunknown"], [break]) + CFLAGS="-Werror -Wunknown-warning-option $ax_pthread_try -pthread $ax_pthread_save_CFLAGS" + ac_link="$ax_pthread_save_ac_link" + AC_LINK_IFELSE([AC_LANG_SOURCE([[int main(void){return 0;}]])], + [ac_link="$ax_pthread_2step_ac_link" + AC_LINK_IFELSE([AC_LANG_SOURCE([[int main(void){return 0;}]])], + [break]) + ]) + done + ac_link="$ax_pthread_save_ac_link" + CFLAGS="$ax_pthread_save_CFLAGS" + AS_IF([test "x$ax_pthread_try" = "x"], [ax_pthread_try=no]) + ax_cv_PTHREAD_CLANG_NO_WARN_FLAG="$ax_pthread_try" + ]) + + case "$ax_cv_PTHREAD_CLANG_NO_WARN_FLAG" in + no | unknown) ;; + *) PTHREAD_CFLAGS="$ax_cv_PTHREAD_CLANG_NO_WARN_FLAG $PTHREAD_CFLAGS" ;; + esac + +fi # $ax_pthread_clang = yes + + + +# Various other checks: +if test "x$ax_pthread_ok" = "xyes"; then + ax_pthread_save_CFLAGS="$CFLAGS" + ax_pthread_save_LIBS="$LIBS" + CFLAGS="$CFLAGS $PTHREAD_CFLAGS" + LIBS="$PTHREAD_LIBS $LIBS" + + # Detect AIX lossage: JOINABLE attribute is called UNDETACHED. + AC_CACHE_CHECK([for joinable pthread attribute], + [ax_cv_PTHREAD_JOINABLE_ATTR], + [ax_cv_PTHREAD_JOINABLE_ATTR=unknown + for ax_pthread_attr in PTHREAD_CREATE_JOINABLE PTHREAD_CREATE_UNDETACHED; do + AC_LINK_IFELSE([AC_LANG_PROGRAM([#include ], + [int attr = $ax_pthread_attr; return attr /* ; */])], + [ax_cv_PTHREAD_JOINABLE_ATTR=$ax_pthread_attr; break], + []) + done + ]) + AS_IF([test "x$ax_cv_PTHREAD_JOINABLE_ATTR" != "xunknown" && \ + test "x$ax_cv_PTHREAD_JOINABLE_ATTR" != "xPTHREAD_CREATE_JOINABLE" && \ + test "x$ax_pthread_joinable_attr_defined" != "xyes"], + [AC_DEFINE_UNQUOTED([PTHREAD_CREATE_JOINABLE], + [$ax_cv_PTHREAD_JOINABLE_ATTR], + [Define to necessary symbol if this constant + uses a non-standard name on your system.]) + ax_pthread_joinable_attr_defined=yes + ]) + + AC_CACHE_CHECK([whether more special flags are required for pthreads], + [ax_cv_PTHREAD_SPECIAL_FLAGS], + [ax_cv_PTHREAD_SPECIAL_FLAGS=no + case $host_os in + solaris*) + ax_cv_PTHREAD_SPECIAL_FLAGS="-D_POSIX_PTHREAD_SEMANTICS" + ;; + esac + ]) + AS_IF([test "x$ax_cv_PTHREAD_SPECIAL_FLAGS" != "xno" && \ + test "x$ax_pthread_special_flags_added" != "xyes"], + [PTHREAD_CFLAGS="$ax_cv_PTHREAD_SPECIAL_FLAGS $PTHREAD_CFLAGS" + ax_pthread_special_flags_added=yes]) + + AC_CACHE_CHECK([for PTHREAD_PRIO_INHERIT], + [ax_cv_PTHREAD_PRIO_INHERIT], + [AC_LINK_IFELSE([AC_LANG_PROGRAM([[#include ]], + [[int i = PTHREAD_PRIO_INHERIT; + return i;]])], + [ax_cv_PTHREAD_PRIO_INHERIT=yes], + [ax_cv_PTHREAD_PRIO_INHERIT=no]) + ]) + AS_IF([test "x$ax_cv_PTHREAD_PRIO_INHERIT" = "xyes" && \ + test "x$ax_pthread_prio_inherit_defined" != "xyes"], + [AC_DEFINE([HAVE_PTHREAD_PRIO_INHERIT], [1], [Have PTHREAD_PRIO_INHERIT.]) + ax_pthread_prio_inherit_defined=yes + ]) + + CFLAGS="$ax_pthread_save_CFLAGS" + LIBS="$ax_pthread_save_LIBS" + + # More AIX lossage: compile with *_r variant + if test "x$GCC" != "xyes"; then + case $host_os in + aix*) + AS_CASE(["x/$CC"], + [x*/c89|x*/c89_128|x*/c99|x*/c99_128|x*/cc|x*/cc128|x*/xlc|x*/xlc_v6|x*/xlc128|x*/xlc128_v6], + [#handle absolute path differently from PATH based program lookup + AS_CASE(["x$CC"], + [x/*], + [ + AS_IF([AS_EXECUTABLE_P([${CC}_r])],[PTHREAD_CC="${CC}_r"]) + AS_IF([test "x${CXX}" != "x"], [AS_IF([AS_EXECUTABLE_P([${CXX}_r])],[PTHREAD_CXX="${CXX}_r"])]) + ], + [ + AC_CHECK_PROGS([PTHREAD_CC],[${CC}_r],[$CC]) + AS_IF([test "x${CXX}" != "x"], [AC_CHECK_PROGS([PTHREAD_CXX],[${CXX}_r],[$CXX])]) + ] + ) + ]) + ;; + esac + fi +fi + +test -n "$PTHREAD_CC" || PTHREAD_CC="$CC" +test -n "$PTHREAD_CXX" || PTHREAD_CXX="$CXX" + +AC_SUBST([PTHREAD_LIBS]) +AC_SUBST([PTHREAD_CFLAGS]) +AC_SUBST([PTHREAD_CC]) +AC_SUBST([PTHREAD_CXX]) + +# Finally, execute ACTION-IF-FOUND/ACTION-IF-NOT-FOUND: +if test "x$ax_pthread_ok" = "xyes"; then + ifelse([$1],,[AC_DEFINE([HAVE_PTHREAD],[1],[Define if you have POSIX threads libraries and header files.])],[$1]) + : +else + ax_pthread_ok=no + $2 +fi +AC_LANG_POP +])dnl AX_PTHREAD diff --git a/configure.ac b/configure.ac index 1b3c413..88671d4 100644 --- a/configure.ac +++ b/configure.ac @@ -27,6 +27,7 @@ AC_PREREQ([2.71]) AC_INIT([iperf],[3.15],[https://github.com/esnet/iperf],[iperf],[https://software.es.net/iperf/]) m4_include([config/ax_check_openssl.m4]) +m4_include([config/ax_pthread.m4]) m4_include([config/iperf_config_static_bin.m4]) AC_LANG(C) @@ -83,6 +84,15 @@ exit 1 # Checks for typedefs, structures, and compiler characteristics. AC_C_CONST +AX_PTHREAD( +[AC_DEFINE([HAVE_PTHREAD],[1],[Define if you have POSIX threads libraries and header files.]) +LIBS="$PTHREAD_LIBS $LIBS" +CFLAGS="$CFLAGS $PTHREAD_CFLAGS" +CXXFLAGS="$CXXFLAGS $PTHREAD_CFLAGS" +CC="$PTHREAD_CC" +CXX="$PTHREAD_CXX" +]) + # Check for poll.h (it's in POSIX so everyone should have it?) AC_CHECK_HEADERS([poll.h]) diff --git a/src/iperf_util.c b/src/iperf_util.c index d5795ee..81e8da1 100644 --- a/src/iperf_util.c +++ b/src/iperf_util.c @@ -337,6 +337,16 @@ get_optional_features(void) numfeatures++; #endif /* HAVE_DONT_FRAGMENT */ +#if defined(HAVE_PTHREAD) + if (numfeatures > 0) { + strncat(features, ", ", + sizeof(features) - strlen(features) - 1); + } + strncat(features, "POSIX threads", + sizeof(features) - strlen(features) - 1); + numfeatures++; +#endif /* HAVE_PTHREAD */ + if (numfeatures == 0) { strncat(features, "None", sizeof(features) - strlen(features) - 1); -- cgit v1.2.3 From 6bcbb202a7f499d4da75518380b595b4bd38cd1b Mon Sep 17 00:00:00 2001 From: "Bruce A. Mah" Date: Mon, 25 Jul 2022 18:50:40 -0700 Subject: Minor configure.ac fixes (remove AC_PROG_RANLIB, only LT_INIT once, fix typo). --- configure.ac | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/configure.ac b/configure.ac index 88671d4..7521f0d 100644 --- a/configure.ac +++ b/configure.ac @@ -49,9 +49,7 @@ AC_CANONICAL_HOST # Checks for tools: c compiler, ranlib (used for creating static libraries), # symlinks and libtool AC_PROG_CC -AC_PROG_RANLIB AC_PROG_LN_S -LT_INIT # Add -Wall if we are using GCC. if test "x$GCC" = "xyes"; then @@ -147,7 +145,7 @@ if test "x$with_openssl" = "xno"; then AC_MSG_WARN( [Building without OpenSSL; disabling iperf_auth functionality.] ) else # Check for OPENSSL support - havs_ssl=false + have_ssl=false AX_CHECK_OPENSSL( [ AC_DEFINE([HAVE_SSL], [1], [OpenSSL Is Available]) have_ssl=true ], -- cgit v1.2.3 From a360f70e7d5fcbbed12fe5dc7c35a889dcc14423 Mon Sep 17 00:00:00 2001 From: "Bruce A. Mah" Date: Thu, 4 Aug 2022 11:19:41 -0700 Subject: Improve error handling. While here, also fix a place where we forgot to join after cancel. --- src/iperf_api.h | 3 +++ src/iperf_client_api.c | 25 +++++++++++++++++++------ src/iperf_error.c | 12 ++++++++++++ src/iperf_server_api.c | 13 ++++++++++--- 4 files changed, 44 insertions(+), 9 deletions(-) diff --git a/src/iperf_api.h b/src/iperf_api.h index 0100d38..3dd61b3 100644 --- a/src/iperf_api.h +++ b/src/iperf_api.h @@ -457,6 +457,9 @@ enum { IEBINDDEVNOSUPPORT = 146, // `ip%%dev` is not supported as system does not support bind to device IEHOSTDEV = 147, // host device name (ip%%) is supported (and required) only for IPv6 link-local address IESETUSERTIMEOUT = 148, // Unable to set TCP USER_TIMEOUT (check perror) + IEPTHREADCREATE=150, // Unable to create thread (check perror) + IEPTHREADCANCEL=151, // Unable to cancel thread (check perror) + IEPTHREADJOIN=152, // Unable to join thread (check perror) /* Stream errors */ IECREATESTREAM = 200, // Unable to create a new stream (check herror/perror) IEINITSTREAM = 201, // Unable to initialize stream (check herror/perror) diff --git a/src/iperf_client_api.c b/src/iperf_client_api.c index 7c927a3..e453399 100644 --- a/src/iperf_client_api.c +++ b/src/iperf_client_api.c @@ -535,6 +535,7 @@ iperf_run_client(struct iperf_test * test) int64_t t_usecs; int64_t timeout_us; int64_t rcv_timeout_us; + int i_errno_save; if (NULL == test) { @@ -639,7 +640,8 @@ iperf_run_client(struct iperf_test * test) SLIST_FOREACH(sp, &test->streams, streams) { if (pthread_create(&(sp->thr), &attr, &iperf_client_worker_start, sp) != 0) { - perror("pthread_create"); + i_errno = IEPTHREADCREATE; + goto cleanup_and_fail; } if (test->debug_level >= DEBUG_LEVEL_INFO) { iperf_printf(test, "Thread FD %d created\n", sp->socket); @@ -699,7 +701,12 @@ iperf_run_client(struct iperf_test * test) SLIST_FOREACH(sp, &test->streams, streams) { if (sp->sender) { if (pthread_cancel(sp->thr) != 0) { - perror("pthread_cancel"); + i_errno = IEPTHREADCANCEL; + goto cleanup_and_fail; + } + if (pthread_join(sp->thr, NULL) != 0) { + i_errno = IEPTHREADJOIN; + goto cleanup_and_fail; } sp->thr = 0; if (test->debug_level >= DEBUG_LEVEL_INFO) { @@ -741,10 +748,12 @@ iperf_run_client(struct iperf_test * test) SLIST_FOREACH(sp, &test->streams, streams) { if (!sp->sender) { if (pthread_cancel(sp->thr) != 0) { - perror("pthread_cancel"); + i_errno = IEPTHREADCANCEL; + goto cleanup_and_fail; } if (pthread_join(sp->thr, NULL) != 0) { - perror("pthread_join"); + i_errno = IEPTHREADJOIN; + goto cleanup_and_fail; } sp->thr = 0; if (test->debug_level >= DEBUG_LEVEL_INFO) { @@ -770,12 +779,15 @@ iperf_run_client(struct iperf_test * test) cleanup_and_fail: /* Cancel all threads */ + i_errno_save = i_errno; SLIST_FOREACH(sp, &test->streams, streams) { if (pthread_cancel(sp->thr) != 0) { - perror("pthread_cancel"); + i_errno = IEPTHREADCANCEL; + iperf_err(test, "cleanup_and_fail - %s", iperf_strerror(i_errno)); } if (pthread_join(sp->thr, NULL) != 0) { - perror("pthread_join"); + i_errno = IEPTHREADCANCEL; + iperf_err(test, "cleanup_and_fail - %s", iperf_strerror(i_errno)); } if (test->debug >= DEBUG_LEVEL_INFO) { iperf_printf(test, "Thread FD %d cancelled\n", sp->socket); @@ -784,6 +796,7 @@ iperf_run_client(struct iperf_test * test) if (test->debug_level >= DEBUG_LEVEL_INFO) { iperf_printf(test, "All threads cancelled\n"); } + i_errno = i_errno_save; iperf_client_end(test); if (test->json_output) { diff --git a/src/iperf_error.c b/src/iperf_error.c index ea7955c..c16d4d1 100644 --- a/src/iperf_error.c +++ b/src/iperf_error.c @@ -470,6 +470,18 @@ iperf_strerror(int int_errno) snprintf(errstr, len, "unable to set TCP USER_TIMEOUT"); perr = 1; break; + case IEPTHREADCREATE: + snprintf(errstr, len, "unable to create thread"); + perr = 1; + break; + case IEPTHREADCANCEL: + snprintf(errstr, len, "unable to cancel thread"); + perr = 1; + break; + case IEPTHREADJOIN: + snprintf(errstr, len, "unable to join thread"); + perr = 1; + break; default: snprintf(errstr, len, "int_errno=%d", int_errno); perr = 1; diff --git a/src/iperf_server_api.c b/src/iperf_server_api.c index 8ac3fd9..6083d14 100644 --- a/src/iperf_server_api.c +++ b/src/iperf_server_api.c @@ -402,17 +402,22 @@ cleanup_server(struct iperf_test *test) struct iperf_stream *sp; /* Cancel threads */ + int i_errno_save = i_errno; SLIST_FOREACH(sp, &test->streams, streams) { if (pthread_cancel(sp->thr) != 0) { - perror("pthread_cancel"); + i_errno = IEPTHREADCANCEL; + iperf_err(test, "cleanup_server - %s", iperf_strerror(i_errno)); } if (pthread_join(sp->thr, NULL) != 0) { - perror("pthread_join"); + i_errno = IEPTHREADJOIN; + iperf_err(test, "cleanup_server - %s", iperf_strerror(i_errno)); } if (test->debug >= DEBUG_LEVEL_INFO) { iperf_printf(test, "Thread FD %d cancelled\n", sp->socket); } } + i_errno = i_errno_save; + if (test->debug_level >= DEBUG_LEVEL_INFO) { iperf_printf(test, "All threads cancelled\n"); } @@ -839,7 +844,9 @@ iperf_run_server(struct iperf_test *test) SLIST_FOREACH(sp, &test->streams, streams) { if (pthread_create(&(sp->thr), &attr, &iperf_server_worker_start, sp) != 0) { - perror("pthread_create"); + i_errno = IEPTHREADCREATE; + cleanup_server(test); + return -1; } if (test->debug_level >= DEBUG_LEVEL_INFO) { iperf_printf(test, "Thread FD %d created\n", sp->socket); -- cgit v1.2.3 From 0b1be2560ecf2949a51e7bd4c173d082907fb260 Mon Sep 17 00:00:00 2001 From: "Bruce A. Mah" Date: Thu, 4 Aug 2022 18:37:18 -0700 Subject: Improve error handling around thread attribute calls. --- src/iperf_api.h | 2 ++ src/iperf_client_api.c | 10 ++++++++-- src/iperf_error.c | 8 ++++++++ src/iperf_server_api.c | 10 ++++++++-- 4 files changed, 26 insertions(+), 4 deletions(-) diff --git a/src/iperf_api.h b/src/iperf_api.h index 3dd61b3..a6756e8 100644 --- a/src/iperf_api.h +++ b/src/iperf_api.h @@ -460,6 +460,8 @@ enum { IEPTHREADCREATE=150, // Unable to create thread (check perror) IEPTHREADCANCEL=151, // Unable to cancel thread (check perror) IEPTHREADJOIN=152, // Unable to join thread (check perror) + IEPTHREADATTRINIT=153, // Unable to initialize thread attribute (check perror) + IEPTHREADATTRDESTROY=154, // Unable to destroy thread attribute (check perror) /* Stream errors */ IECREATESTREAM = 200, // Unable to create a new stream (check herror/perror) IEINITSTREAM = 201, // Unable to initialize stream (check herror/perror) diff --git a/src/iperf_client_api.c b/src/iperf_client_api.c index e453399..1a66c96 100644 --- a/src/iperf_client_api.c +++ b/src/iperf_client_api.c @@ -636,7 +636,10 @@ iperf_run_client(struct iperf_test * test) /* Create and spin up threads */ pthread_attr_t attr; - pthread_attr_init(&attr); + if (pthread_attr_init(&attr) != 0) { + i_errno = IEPTHREADATTRINIT; + goto cleanup_and_fail; + } SLIST_FOREACH(sp, &test->streams, streams) { if (pthread_create(&(sp->thr), &attr, &iperf_client_worker_start, sp) != 0) { @@ -650,7 +653,10 @@ iperf_run_client(struct iperf_test * test) if (test->debug_level >= DEBUG_LEVEL_INFO) { iperf_printf(test, "All threads created\n"); } - pthread_attr_destroy(&attr); + if (pthread_attr_destroy(&attr) != 0) { + i_errno = IEPTHREADATTRDESTROY; + goto cleanup_and_fail; + } // Set non-blocking for non-UDP tests if (test->protocol->id != Pudp) { diff --git a/src/iperf_error.c b/src/iperf_error.c index c16d4d1..2999d5d 100644 --- a/src/iperf_error.c +++ b/src/iperf_error.c @@ -482,6 +482,14 @@ iperf_strerror(int int_errno) snprintf(errstr, len, "unable to join thread"); perr = 1; break; + case IEPTHREADATTRINIT: + snprintf(errstr, len, "unable to create thread attributes"); + perr = 1; + break; + case IEPTHREADATTRDESTROY: + snprintf(errstr, len, "unable to destroy thread attributes"); + perr = 1; + break; default: snprintf(errstr, len, "int_errno=%d", int_errno); perr = 1; diff --git a/src/iperf_server_api.c b/src/iperf_server_api.c index 6083d14..236060e 100644 --- a/src/iperf_server_api.c +++ b/src/iperf_server_api.c @@ -840,7 +840,10 @@ iperf_run_server(struct iperf_test *test) /* Create and spin up threads */ pthread_attr_t attr; - pthread_attr_init(&attr); + if (pthread_attr_init(&attr) != 0) { + i_errno = IEPTHREADATTRINIT; + cleanup_server(test); + }; SLIST_FOREACH(sp, &test->streams, streams) { if (pthread_create(&(sp->thr), &attr, &iperf_server_worker_start, sp) != 0) { @@ -855,7 +858,10 @@ iperf_run_server(struct iperf_test *test) if (test->debug_level >= DEBUG_LEVEL_INFO) { iperf_printf(test, "All threads created\n"); } - pthread_attr_destroy(&attr); + if (pthread_attr_destroy(&attr) != 0) { + i_errno = IEPTHREADATTRDESTROY; + cleanup_server(test); + }; } } -- cgit v1.2.3 From fc86f850cc74aa18d9f78abe1dec46ae348a689d Mon Sep 17 00:00:00 2001 From: "Bruce A. Mah" Date: Thu, 4 Aug 2022 23:10:12 -0700 Subject: Locking around output operations. Allow threads to exit gracefully. --- src/iperf.h | 3 ++ src/iperf_api.c | 90 ++++++++++++++++++++++++++++++++++++++++++++------ src/iperf_client_api.c | 32 ++++++------------ src/iperf_error.c | 17 ++++++++++ src/iperf_server_api.c | 12 +++---- 5 files changed, 115 insertions(+), 39 deletions(-) diff --git a/src/iperf.h b/src/iperf.h index e184171..afdf1b6 100644 --- a/src/iperf.h +++ b/src/iperf.h @@ -178,6 +178,7 @@ struct iperf_stream struct iperf_test* test; pthread_t thr; + int done; /* configurable members */ int local_port; @@ -271,6 +272,8 @@ enum debug_level { struct iperf_test { + pthread_mutex_t print_mutex; + char role; /* 'c' lient or 's' erver */ enum iperf_mode mode; int sender_has_retransmits; diff --git a/src/iperf_api.c b/src/iperf_api.c index ca1ad11..df2f7d5 100644 --- a/src/iperf_api.c +++ b/src/iperf_api.c @@ -2807,6 +2807,7 @@ struct iperf_test * iperf_new_test() { struct iperf_test *test; + int rc; test = (struct iperf_test *) malloc(sizeof(struct iperf_test)); if (!test) { @@ -2816,6 +2817,21 @@ iperf_new_test() /* initialize everything to zero */ memset(test, 0, sizeof(struct iperf_test)); + /* Initialize mutex for printing output */ + pthread_mutexattr_t mutexattr; + pthread_mutexattr_init(&mutexattr); + rc = pthread_mutexattr_settype(&mutexattr, PTHREAD_MUTEX_ERRORCHECK); + if (rc != 0) { + errno = rc; + perror("iperf_new_test: pthread_mutexattr_settype"); + } + + if (pthread_mutex_init(&(test->print_mutex), &mutexattr) != 0) { + perror("iperf_new_test: pthread_mutex_init"); + } + + pthread_mutexattr_destroy(&mutexattr); + test->settings = (struct iperf_settings *) malloc(sizeof(struct iperf_settings)); if (!test->settings) { free(test); @@ -3069,6 +3085,14 @@ iperf_free_test(struct iperf_test *test) free(prot); } + /* Destroy print mutex. iperf_printf() doesn't work after this point */ + int rc; + rc = pthread_mutex_destroy(&(test->print_mutex)); + if (rc != 0) { + errno = rc; + perror("iperf_free_test: pthread_mutex_destroy"); + } + if (test->logfile) { free(test->logfile); test->logfile = NULL; @@ -4798,7 +4822,14 @@ iperf_json_finish(struct iperf_test *test) if (test->json_output_string == NULL) { return -1; } + + if (pthread_mutex_lock(&(test->print_mutex)) != 0) { + perror("iperf_json_finish: pthread_mutex_lock"); + } fprintf(test->outfile, "%s\n", test->json_output_string); + if (pthread_mutex_unlock(&(test->print_mutex)) != 0) { + perror("iperf_json_finish: pthread_mutex_unlock"); + } iflush(test); cJSON_Delete(test->json_top); test->json_top = NULL; @@ -4907,6 +4938,10 @@ iperf_printf(struct iperf_test *test, const char* format, ...) struct tm *ltm = NULL; char *ct = NULL; + if (pthread_mutex_lock(&(test->print_mutex)) != 0) { + perror("iperf_print: pthread_mutex_lock"); + } + /* Timestamp if requested */ if (iperf_get_test_timestamps(test)) { time(&now); @@ -4930,28 +4965,36 @@ iperf_printf(struct iperf_test *test, const char* format, ...) if (test->role == 'c') { if (ct) { r0 = fprintf(test->outfile, "%s", ct); - if (r0 < 0) - return r0; + if (r0 < 0) { + r = r0; + goto bottom; + } r += r0; } if (test->title) { r0 = fprintf(test->outfile, "%s: ", test->title); - if (r0 < 0) - return r0; + if (r0 < 0) { + r = r0; + goto bottom; + } r += r0; } va_start(argp, format); r0 = vfprintf(test->outfile, format, argp); va_end(argp); - if (r0 < 0) - return r0; + if (r0 < 0) { + r = r0; + goto bottom; + } r += r0; } else if (test->role == 's') { if (ct) { r0 = snprintf(linebuffer, sizeof(linebuffer), "%s", ct); - if (r0 < 0) - return r0; + if (r0 < 0) { + r = r0; + goto bottom; + } r += r0; } /* Should always be true as long as sizeof(ct) < sizeof(linebuffer) */ @@ -4959,8 +5002,10 @@ iperf_printf(struct iperf_test *test, const char* format, ...) va_start(argp, format); r0 = vsnprintf(linebuffer + r, sizeof(linebuffer) - r, format, argp); va_end(argp); - if (r0 < 0) - return r0; + if (r0 < 0) { + r = r0; + goto bottom; + } r += r0; } fprintf(test->outfile, "%s", linebuffer); @@ -4971,11 +5016,34 @@ iperf_printf(struct iperf_test *test, const char* format, ...) TAILQ_INSERT_TAIL(&(test->server_output_list), l, textlineentries); } } + + bottom: + if (pthread_mutex_unlock(&(test->print_mutex)) != 0) { + perror("iperf_print: pthread_mutex_unlock"); + } + return r; } int iflush(struct iperf_test *test) { - return fflush(test->outfile); + int rc2; + + int rc; + rc = pthread_mutex_lock(&(test->print_mutex)); + if (rc != 0) { + errno = rc; + perror("iflush: pthread_mutex_lock"); + } + + rc2 = fflush(test->outfile); + + rc = pthread_mutex_unlock(&(test->print_mutex)); + if (rc != 0) { + errno = rc; + perror("iflush: pthread_mutex_unlock"); + } + + return rc2; } diff --git a/src/iperf_client_api.c b/src/iperf_client_api.c index 1a66c96..6e6afb6 100644 --- a/src/iperf_client_api.c +++ b/src/iperf_client_api.c @@ -56,12 +56,13 @@ iperf_client_worker_start(void *s) { struct iperf_stream *sp = (struct iperf_stream *) s; struct iperf_test *test = sp->test; - while (1) { + while (! (test->done)) { if (test->debug_level >= DEBUG_LEVEL_INFO) { iperf_printf(test, "Thread FD %d\n", sp->socket); } sleep(1); } + return NULL; } int @@ -706,22 +707,18 @@ iperf_run_client(struct iperf_test * test) /* Cancel sender threads */ SLIST_FOREACH(sp, &test->streams, streams) { if (sp->sender) { - if (pthread_cancel(sp->thr) != 0) { - i_errno = IEPTHREADCANCEL; - goto cleanup_and_fail; - } + sp->done = 1; if (pthread_join(sp->thr, NULL) != 0) { i_errno = IEPTHREADJOIN; goto cleanup_and_fail; } - sp->thr = 0; if (test->debug_level >= DEBUG_LEVEL_INFO) { - iperf_printf(test, "Thread FD %d cancelled\n", sp->socket); + iperf_printf(test, "Thread FD %d stopped\n", sp->socket); } } } if (test->debug_level >= DEBUG_LEVEL_INFO) { - iperf_printf(test, "Sender threads cancelled\n"); + iperf_printf(test, "Sender threads stopped\n"); } // Unset non-blocking for non-UDP tests @@ -753,22 +750,18 @@ iperf_run_client(struct iperf_test * test) /* Cancel receiver threads */ SLIST_FOREACH(sp, &test->streams, streams) { if (!sp->sender) { - if (pthread_cancel(sp->thr) != 0) { - i_errno = IEPTHREADCANCEL; - goto cleanup_and_fail; - } + sp->done = 1; if (pthread_join(sp->thr, NULL) != 0) { i_errno = IEPTHREADJOIN; goto cleanup_and_fail; } - sp->thr = 0; if (test->debug_level >= DEBUG_LEVEL_INFO) { - iperf_printf(test, "Thread FD %d cancelled\n", sp->socket); + iperf_printf(test, "Thread FD %d stopped\n", sp->socket); } } } if (test->debug_level >= DEBUG_LEVEL_INFO) { - iperf_printf(test, "Receiver threads cancelled\n"); + iperf_printf(test, "Receiver threads stopped\n"); } if (test->json_output) { @@ -787,20 +780,17 @@ iperf_run_client(struct iperf_test * test) /* Cancel all threads */ i_errno_save = i_errno; SLIST_FOREACH(sp, &test->streams, streams) { - if (pthread_cancel(sp->thr) != 0) { - i_errno = IEPTHREADCANCEL; - iperf_err(test, "cleanup_and_fail - %s", iperf_strerror(i_errno)); - } + sp->done = 1; if (pthread_join(sp->thr, NULL) != 0) { i_errno = IEPTHREADCANCEL; iperf_err(test, "cleanup_and_fail - %s", iperf_strerror(i_errno)); } if (test->debug >= DEBUG_LEVEL_INFO) { - iperf_printf(test, "Thread FD %d cancelled\n", sp->socket); + iperf_printf(test, "Thread FD %d stopped\n", sp->socket); } } if (test->debug_level >= DEBUG_LEVEL_INFO) { - iperf_printf(test, "All threads cancelled\n"); + iperf_printf(test, "All threads stopped\n"); } i_errno = i_errno_save; diff --git a/src/iperf_error.c b/src/iperf_error.c index 2999d5d..6426554 100644 --- a/src/iperf_error.c +++ b/src/iperf_error.c @@ -47,6 +47,10 @@ iperf_err(struct iperf_test *test, const char *format, ...) struct tm *ltm = NULL; char *ct = NULL; + if (pthread_mutex_lock(&(test->print_mutex)) != 0) { + perror("iperf_err: pthread_mutex_lock"); + } + /* Timestamp if requested */ if (test != NULL && test->timestamps) { time(&now); @@ -74,6 +78,10 @@ iperf_err(struct iperf_test *test, const char *format, ...) } } va_end(argp); + + if (pthread_mutex_unlock(&(test->print_mutex)) != 0) { + perror("iperf_err: pthread_mutex_unlock"); + } } /* Do a printf to stderr or log file as appropriate, then exit. */ @@ -86,6 +94,10 @@ iperf_errexit(struct iperf_test *test, const char *format, ...) struct tm *ltm = NULL; char *ct = NULL; + if (pthread_mutex_lock(&(test->print_mutex)) != 0) { + perror("iperf_errexit: pthread_mutex_lock"); + } + /* Timestamp if requested */ if (test != NULL && test->timestamps) { time(&now); @@ -114,6 +126,11 @@ iperf_errexit(struct iperf_test *test, const char *format, ...) } fprintf(stderr, "iperf3: %s\n", str); } + + if (pthread_mutex_unlock(&(test->print_mutex)) != 0) { + perror("iperf_errexit: pthread_mutex_unlock"); + } + va_end(argp); if (test) iperf_delete_pidfile(test); diff --git a/src/iperf_server_api.c b/src/iperf_server_api.c index 236060e..33f4c02 100644 --- a/src/iperf_server_api.c +++ b/src/iperf_server_api.c @@ -71,12 +71,13 @@ iperf_server_worker_start(void *s) { struct iperf_stream *sp = (struct iperf_stream *) s; struct iperf_test *test = sp->test; - while (1) { + while (! (test->done)) { if (test->debug_level >= DEBUG_LEVEL_INFO) { iperf_printf(test, "Thread FD %d\n", sp->socket); } sleep(1); } + return NULL; } int @@ -404,22 +405,19 @@ cleanup_server(struct iperf_test *test) /* Cancel threads */ int i_errno_save = i_errno; SLIST_FOREACH(sp, &test->streams, streams) { - if (pthread_cancel(sp->thr) != 0) { - i_errno = IEPTHREADCANCEL; - iperf_err(test, "cleanup_server - %s", iperf_strerror(i_errno)); - } + sp->done = 1; if (pthread_join(sp->thr, NULL) != 0) { i_errno = IEPTHREADJOIN; iperf_err(test, "cleanup_server - %s", iperf_strerror(i_errno)); } if (test->debug >= DEBUG_LEVEL_INFO) { - iperf_printf(test, "Thread FD %d cancelled\n", sp->socket); + iperf_printf(test, "Thread FD %d stopped\n", sp->socket); } } i_errno = i_errno_save; if (test->debug_level >= DEBUG_LEVEL_INFO) { - iperf_printf(test, "All threads cancelled\n"); + iperf_printf(test, "All threads stopped\n"); } /* Close open streams */ -- cgit v1.2.3 From 0755cc4e12b41547c92ec0c826857fa86935c3ae Mon Sep 17 00:00:00 2001 From: "Bruce A. Mah" Date: Fri, 28 Oct 2022 11:52:25 -0700 Subject: Initial version of multi-threaded writers. * Create iperf_send_mt and iperf_recv_mt, the multi-threaded versions of network I/O functions. These handle a single connection only and do not attempt to coordinate timing (for flow control) with any other threads. * Make client and server thread functions call the new multi-threaded network I/O functions. * Remove all network I/O for the test streams from the main thread. * fd_set objects in test object only apply to sockets used by the main thread (not the test streams in the worker threads). Outstanding issues: * No locking of shared data structures at this point. Correctness may be compromised at this point. * Worker threads on the sender side will tend to busy-wait because they do not attempt to sleep while attempting to pace themeselves. * No support (for now) for ending conditions other than time-based (packet-based and byte-based don't work). --- src/iperf_api.c | 40 +++++++++++++------------------------ src/iperf_api.h | 4 ++-- src/iperf_client_api.c | 54 +++++++++++++++----------------------------------- src/iperf_server_api.c | 51 +++++++++++++++-------------------------------- 4 files changed, 48 insertions(+), 101 deletions(-) diff --git a/src/iperf_api.c b/src/iperf_api.c index df2f7d5..561a2ca 100644 --- a/src/iperf_api.c +++ b/src/iperf_api.c @@ -1861,10 +1861,8 @@ iperf_check_throttle(struct iperf_stream *sp, struct iperf_time *nowP) bits_per_second = sp->result->bytes_sent * 8 / seconds; if (bits_per_second < sp->test->settings->rate) { sp->green_light = 1; - FD_SET(sp->socket, &sp->test->write_set); } else { sp->green_light = 0; - FD_CLR(sp->socket, &sp->test->write_set); } } @@ -1909,10 +1907,10 @@ iperf_check_total_rate(struct iperf_test *test, iperf_size_t last_interval_bytes } int -iperf_send(struct iperf_test *test, fd_set *write_setP) +iperf_send_mt(struct iperf_stream *sp) { register int multisend, r, streams_active; - register struct iperf_stream *sp; + register struct iperf_test *test = sp->test; struct iperf_time now; int no_throttle_check; @@ -1931,13 +1929,14 @@ iperf_send(struct iperf_test *test, fd_set *write_setP) if (no_throttle_check) iperf_time_now(&now); streams_active = 0; - SLIST_FOREACH(sp, &test->streams, streams) { - if ((sp->green_light && sp->sender && - (write_setP == NULL || FD_ISSET(sp->socket, write_setP)))) { - if (multisend > 1 && test->settings->bytes != 0 && test->bytes_sent >= test->settings->bytes) - break; - if (multisend > 1 && test->settings->blocks != 0 && test->blocks_sent >= test->settings->blocks) - break; + { + if (sp->green_light && sp->sender) { + // XXX If we hit one of these ending conditions maybe + // want to stop even trying to send something? + if (multisend > 1 && test->settings->bytes != 0 && test->bytes_sent >= test->settings->bytes) + break; + if (multisend > 1 && test->settings->blocks != 0 && test->blocks_sent >= test->settings->blocks) + break; if ((r = sp->snd(sp)) < 0) { if (r == NET_SOFTERROR) break; @@ -1957,35 +1956,24 @@ iperf_send(struct iperf_test *test, fd_set *write_setP) } if (!no_throttle_check) { /* Throttle check if was not checked for each send */ iperf_time_now(&now); - SLIST_FOREACH(sp, &test->streams, streams) - if (sp->sender) - iperf_check_throttle(sp, &now); + if (sp->sender) + iperf_check_throttle(sp, &now); } - if (write_setP != NULL) - SLIST_FOREACH(sp, &test->streams, streams) - if (FD_ISSET(sp->socket, write_setP)) - FD_CLR(sp->socket, write_setP); - return 0; } int -iperf_recv(struct iperf_test *test, fd_set *read_setP) +iperf_recv_mt(struct iperf_stream *sp) { int r; - struct iperf_stream *sp; + struct iperf_test *test = sp->test; - SLIST_FOREACH(sp, &test->streams, streams) { - if (FD_ISSET(sp->socket, read_setP) && !sp->sender) { if ((r = sp->rcv(sp)) < 0) { i_errno = IESTREAMREAD; return r; } test->bytes_received += r; ++test->blocks_received; - FD_CLR(sp->socket, read_setP); - } - } return 0; } diff --git a/src/iperf_api.h b/src/iperf_api.h index a6756e8..ed991e1 100644 --- a/src/iperf_api.h +++ b/src/iperf_api.h @@ -310,8 +310,8 @@ void build_tcpinfo_message(struct iperf_interval_results *r, char *message); int iperf_set_send_state(struct iperf_test *test, signed char state); void iperf_check_throttle(struct iperf_stream *sp, struct iperf_time *nowP); -int iperf_send(struct iperf_test *, fd_set *) /* __attribute__((hot)) */; -int iperf_recv(struct iperf_test *, fd_set *); +int iperf_send_mt(struct iperf_stream *) /* __attribute__((hot)) */; +int iperf_recv_mt(struct iperf_stream *); void iperf_catch_sigend(void (*handler)(int)); void iperf_got_sigend(struct iperf_test *test) __attribute__ ((noreturn)); void usage(void); diff --git a/src/iperf_client_api.c b/src/iperf_client_api.c index 6e6afb6..b10e7e4 100644 --- a/src/iperf_client_api.c +++ b/src/iperf_client_api.c @@ -52,17 +52,28 @@ #endif /* HAVE_TCP_CONGESTION */ void * -iperf_client_worker_start(void *s) { +iperf_client_worker_run(void *s) { struct iperf_stream *sp = (struct iperf_stream *) s; struct iperf_test *test = sp->test; while (! (test->done)) { - if (test->debug_level >= DEBUG_LEVEL_INFO) { - iperf_printf(test, "Thread FD %d\n", sp->socket); + if (sp->sender) { + if (iperf_send_mt(sp) < 0) { + goto cleanup_and_fail; + } + } + else { + if (iperf_recv_mt(sp) < 0) { + goto cleanup_and_fail; + } } - sleep(1); } return NULL; + + cleanup_and_fail: + /* XXX */ + test->done = 0; + return NULL; } int @@ -129,12 +140,6 @@ iperf_create_streams(struct iperf_test *test, int sender) } #endif /* HAVE_TCP_CONGESTION */ - if (sender) - FD_SET(s, &test->write_set); - else - FD_SET(s, &test->read_set); - if (s > test->max_fd) test->max_fd = s; - sp = iperf_new_stream(test, s, sender); if (!sp) return -1; @@ -643,7 +648,7 @@ iperf_run_client(struct iperf_test * test) } SLIST_FOREACH(sp, &test->streams, streams) { - if (pthread_create(&(sp->thr), &attr, &iperf_client_worker_start, sp) != 0) { + if (pthread_create(&(sp->thr), &attr, &iperf_client_worker_run, sp) != 0) { i_errno = IEPTHREADCREATE; goto cleanup_and_fail; } @@ -667,24 +672,6 @@ iperf_run_client(struct iperf_test * test) } } - - if (test->mode == BIDIRECTIONAL) - { - if (iperf_send(test, &write_set) < 0) - goto cleanup_and_fail; - if (iperf_recv(test, &read_set) < 0) - goto cleanup_and_fail; - } else if (test->mode == SENDER) { - // Regular mode. Client sends. - if (iperf_send(test, &write_set) < 0) - goto cleanup_and_fail; - } else { - // Reverse mode. Client receives. - if (iperf_recv(test, &read_set) < 0) - goto cleanup_and_fail; - } - - /* Run the timers. */ iperf_time_now(&now); tmr_run(&now); @@ -736,15 +723,6 @@ iperf_run_client(struct iperf_test * test) goto cleanup_and_fail; } } - // If we're in reverse mode, continue draining the data - // connection(s) even if test is over. This prevents a - // deadlock where the server side fills up its pipe(s) - // and gets blocked, so it can't receive state changes - // from the client side. - else if (test->mode == RECEIVER && test->state == TEST_END) { - if (iperf_recv(test, &read_set) < 0) - goto cleanup_and_fail; - } } /* Cancel receiver threads */ diff --git a/src/iperf_server_api.c b/src/iperf_server_api.c index 33f4c02..2be72ca 100644 --- a/src/iperf_server_api.c +++ b/src/iperf_server_api.c @@ -67,17 +67,28 @@ #endif /* HAVE_TCP_CONGESTION */ void * -iperf_server_worker_start(void *s) { +iperf_server_worker_run(void *s) { struct iperf_stream *sp = (struct iperf_stream *) s; struct iperf_test *test = sp->test; while (! (test->done)) { - if (test->debug_level >= DEBUG_LEVEL_INFO) { - iperf_printf(test, "Thread FD %d\n", sp->socket); + if (sp->sender) { + if (iperf_send_mt(sp) < 0) { + goto cleanup_and_fail; + } + } + else { + if (iperf_recv_mt(sp) < 0) { + goto cleanup_and_fail; + } } - sleep(1); } return NULL; + + cleanup_and_fail: + /* XXX */ + test->done = 0; + return NULL; } int @@ -746,11 +757,6 @@ iperf_run_server(struct iperf_test *test) return -1; } - if (sp->sender) - FD_SET(s, &test->write_set); - else - FD_SET(s, &test->read_set); - if (s > test->max_fd) test->max_fd = s; /* @@ -844,7 +850,7 @@ iperf_run_server(struct iperf_test *test) }; SLIST_FOREACH(sp, &test->streams, streams) { - if (pthread_create(&(sp->thr), &attr, &iperf_server_worker_start, sp) != 0) { + if (pthread_create(&(sp->thr), &attr, &iperf_server_worker_run, sp) != 0) { i_errno = IEPTHREADCREATE; cleanup_server(test); return -1; @@ -862,31 +868,6 @@ iperf_run_server(struct iperf_test *test) }; } } - - if (test->state == TEST_RUNNING) { - if (test->mode == BIDIRECTIONAL) { - if (iperf_recv(test, &read_set) < 0) { - cleanup_server(test); - return -1; - } - if (iperf_send(test, &write_set) < 0) { - cleanup_server(test); - return -1; - } - } else if (test->mode == SENDER) { - // Reverse mode. Server sends. - if (iperf_send(test, &write_set) < 0) { - cleanup_server(test); - return -1; - } - } else { - // Regular mode. Server receives. - if (iperf_recv(test, &read_set) < 0) { - cleanup_server(test); - return -1; - } - } - } } if (result == 0 || -- cgit v1.2.3 From 30ce2d5984c5ef90681e0faed6d1a747b6d95999 Mon Sep 17 00:00:00 2001 From: Sarah Larsen Date: Tue, 17 Jan 2023 17:44:27 -0800 Subject: Add atomic_iperf_size_t for atomic (thread-safe) operations --- src/iperf.h | 25 ++++++++++++++----------- src/iperf_api.c | 2 +- src/iperf_api.h | 4 +++- 3 files changed, 18 insertions(+), 13 deletions(-) diff --git a/src/iperf.h b/src/iperf.h index afdf1b6..87b7b0e 100644 --- a/src/iperf.h +++ b/src/iperf.h @@ -74,14 +74,17 @@ #endif // HAVE_SSL #include +#include #if !defined(__IPERF_API_H) -typedef uint64_t iperf_size_t; +//typedef uint64_t iperf_size_t; +typedef uint_fast64_t iperf_size_t; +typedef atomic_uint_fast64_t atomic_iperf_size_t; #endif // __IPERF_API_H struct iperf_interval_results { - iperf_size_t bytes_transferred; /* bytes transferred in this interval */ + atomic_iperf_size_t bytes_transferred; /* bytes transferred in this interval */ struct iperf_time interval_start_time; struct iperf_time interval_end_time; float interval_duration; @@ -115,11 +118,11 @@ struct iperf_interval_results struct iperf_stream_result { - iperf_size_t bytes_received; - iperf_size_t bytes_sent; - iperf_size_t bytes_received_this_interval; - iperf_size_t bytes_sent_this_interval; - iperf_size_t bytes_sent_omit; + atomic_iperf_size_t bytes_received; + atomic_iperf_size_t bytes_sent; + atomic_iperf_size_t bytes_received_this_interval; + atomic_iperf_size_t bytes_sent_this_interval; + atomic_iperf_size_t bytes_sent_omit; long stream_prev_total_retrans; long stream_retrans; long stream_max_rtt; @@ -359,11 +362,11 @@ struct iperf_test int num_streams; /* total streams in the test (-P) */ - iperf_size_t bytes_sent; - iperf_size_t blocks_sent; + atomic_iperf_size_t bytes_sent; + atomic_iperf_size_t blocks_sent; - iperf_size_t bytes_received; - iperf_size_t blocks_received; + atomic_iperf_size_t bytes_received; + atomic_iperf_size_t blocks_received; iperf_size_t bitrate_limit_stats_count; /* Number of stats periods accumulated for server's total bitrate average */ iperf_size_t *bitrate_limit_intervals_traffic_bytes; /* Pointer to a cyclic array that includes the last interval's bytes transferred */ diff --git a/src/iperf_api.c b/src/iperf_api.c index 561a2ca..eb23403 100644 --- a/src/iperf_api.c +++ b/src/iperf_api.c @@ -1889,7 +1889,7 @@ iperf_check_total_rate(struct iperf_test *test, iperf_size_t last_interval_bytes return; /* Calculating total bytes traffic to be averaged */ - for (total_bytes = 0, i = 0; i < test->settings->bitrate_limit_stats_per_interval; i++) { + for (i = 0, total_bytes = 0; i < test->settings->bitrate_limit_stats_per_interval; i++) { total_bytes += test->bitrate_limit_intervals_traffic_bytes[i]; } diff --git a/src/iperf_api.h b/src/iperf_api.h index ed991e1..1c2cb7e 100644 --- a/src/iperf_api.h +++ b/src/iperf_api.h @@ -38,6 +38,7 @@ extern "C" { /* open extern "C" */ #endif +#include struct iperf_test; struct iperf_stream_result; @@ -46,7 +47,8 @@ struct iperf_stream; struct iperf_time; #if !defined(__IPERF_H) -typedef uint64_t iperf_size_t; +typedef uint_fast64_t iperf_size_t; +typedef atomic_uint_fast64_t atomic_iperf_size_t; #endif // __IPERF_H /* default settings */ -- cgit v1.2.3 From b14c3b938461d6e663f1109948bbe4e77a772ea0 Mon Sep 17 00:00:00 2001 From: "Bruce A. Mah" Date: Wed, 29 Mar 2023 18:52:33 -0700 Subject: Make -n / -k options work correctly. We need to get the threads to exit when the ending conditions for -n / -k are reached, but we weren't doing that. While here, clean up some not-very-often-used error case code and pet copyright dates. Fixes IPERF-151. --- src/iperf_client_api.c | 6 ++---- src/iperf_server_api.c | 6 ++---- 2 files changed, 4 insertions(+), 8 deletions(-) diff --git a/src/iperf_client_api.c b/src/iperf_client_api.c index b10e7e4..f0c4a58 100644 --- a/src/iperf_client_api.c +++ b/src/iperf_client_api.c @@ -1,5 +1,5 @@ /* - * iperf, Copyright (c) 2014-2022, The Regents of the University of + * iperf, Copyright (c) 2014-2023, The Regents of the University of * California, through Lawrence Berkeley National Laboratory (subject * to receipt of any required approvals from the U.S. Dept. of * Energy). All rights reserved. @@ -56,7 +56,7 @@ iperf_client_worker_run(void *s) { struct iperf_stream *sp = (struct iperf_stream *) s; struct iperf_test *test = sp->test; - while (! (test->done)) { + while (! (test->done) && ! (sp->done)) { if (sp->sender) { if (iperf_send_mt(sp) < 0) { goto cleanup_and_fail; @@ -71,8 +71,6 @@ iperf_client_worker_run(void *s) { return NULL; cleanup_and_fail: - /* XXX */ - test->done = 0; return NULL; } diff --git a/src/iperf_server_api.c b/src/iperf_server_api.c index 2be72ca..f36eaa3 100644 --- a/src/iperf_server_api.c +++ b/src/iperf_server_api.c @@ -1,5 +1,5 @@ /* - * iperf, Copyright (c) 2014-2022 The Regents of the University of + * iperf, Copyright (c) 2014-2023 The Regents of the University of * California, through Lawrence Berkeley National Laboratory (subject * to receipt of any required approvals from the U.S. Dept. of * Energy). All rights reserved. @@ -71,7 +71,7 @@ iperf_server_worker_run(void *s) { struct iperf_stream *sp = (struct iperf_stream *) s; struct iperf_test *test = sp->test; - while (! (test->done)) { + while (! (test->done) && ! (sp->done)) { if (sp->sender) { if (iperf_send_mt(sp) < 0) { goto cleanup_and_fail; @@ -86,8 +86,6 @@ iperf_server_worker_run(void *s) { return NULL; cleanup_and_fail: - /* XXX */ - test->done = 0; return NULL; } -- cgit v1.2.3 From a326ec839ae2123d9464d4f96d732e610f144d23 Mon Sep 17 00:00:00 2001 From: "Bruce A. Mah" Date: Thu, 13 Apr 2023 14:43:39 -0700 Subject: Add autoconf test for stdatomic Also do conditional compilation for stdatomic and pthread. --- configure.ac | 3 +++ src/iperf.h | 7 ++++++- src/iperf_api.h | 4 +++- 3 files changed, 12 insertions(+), 2 deletions(-) diff --git a/configure.ac b/configure.ac index 7521f0d..92b9ced 100644 --- a/configure.ac +++ b/configure.ac @@ -91,6 +91,9 @@ CC="$PTHREAD_CC" CXX="$PTHREAD_CXX" ]) +# Atomics +AC_CHECK_HEADERS([stdatomic.h]) + # Check for poll.h (it's in POSIX so everyone should have it?) AC_CHECK_HEADERS([poll.h]) diff --git a/src/iperf.h b/src/iperf.h index 87b7b0e..87f787f 100644 --- a/src/iperf.h +++ b/src/iperf.h @@ -1,5 +1,5 @@ /* - * iperf, Copyright (c) 2014-2020, The Regents of the University of + * iperf, Copyright (c) 2014-2020, 2023, The Regents of the University of * California, through Lawrence Berkeley National Laboratory (subject * to receipt of any required approvals from the U.S. Dept. of * Energy). All rights reserved. @@ -73,8 +73,13 @@ #include #endif // HAVE_SSL +#ifdef HAVE_PTHREAD #include +#endif // HAVE_PTHREAD + +#ifdef HAVE_STDATOMIC_H #include +#endif // HAVE_STDATOMIC_H #if !defined(__IPERF_API_H) //typedef uint64_t iperf_size_t; diff --git a/src/iperf_api.h b/src/iperf_api.h index 1c2cb7e..fd2ab6b 100644 --- a/src/iperf_api.h +++ b/src/iperf_api.h @@ -1,5 +1,5 @@ /* - * iperf, Copyright (c) 2014-2022, The Regents of the University of + * iperf, Copyright (c) 2014-2023, The Regents of the University of * California, through Lawrence Berkeley National Laboratory (subject * to receipt of any required approvals from the U.S. Dept. of * Energy). All rights reserved. @@ -38,7 +38,9 @@ extern "C" { /* open extern "C" */ #endif +#ifdef HAVE_STDATOMIC_H #include +#endif // HAVE_STDATOMIC_H struct iperf_test; struct iperf_stream_result; -- cgit v1.2.3 From 20a02b4d460099d6d1a584c77bda81418bf44bd8 Mon Sep 17 00:00:00 2001 From: "Bruce A. Mah" Date: Thu, 13 Apr 2023 16:04:18 -0700 Subject: Fix build on CentOS 7 / x86_64. The issue is that the default compiler on CentOS 7 (and presumably RHEL 7) is an old GCC that's too old to support atomic types. In this case we attempt to approximate an atomic 64-bit type with a normal 64-bit integer, and warn at runtime if the system doesn't support lock-free operations with this data type. This has been compile-tested and lightly run-tested on CentOS 7 x86_64. It might not work well with ancient non-GCC compilers or 32-bit hosts. --- src/iperf.h | 8 +++++++- src/iperf_api.h | 7 +++++++ src/main.c | 20 +++++++++++++++++++- 3 files changed, 33 insertions(+), 2 deletions(-) diff --git a/src/iperf.h b/src/iperf.h index 87f787f..7b270bd 100644 --- a/src/iperf.h +++ b/src/iperf.h @@ -77,12 +77,18 @@ #include #endif // HAVE_PTHREAD +/* + * Atomic types highly desired, but if not, we approximate what we need + * with normal integers and warn. + */ #ifdef HAVE_STDATOMIC_H #include +#else +#warning "No available." +typedef uint64_t atomic_uint_fast64_t; #endif // HAVE_STDATOMIC_H #if !defined(__IPERF_API_H) -//typedef uint64_t iperf_size_t; typedef uint_fast64_t iperf_size_t; typedef atomic_uint_fast64_t atomic_iperf_size_t; #endif // __IPERF_API_H diff --git a/src/iperf_api.h b/src/iperf_api.h index fd2ab6b..9e70d44 100644 --- a/src/iperf_api.h +++ b/src/iperf_api.h @@ -38,8 +38,15 @@ extern "C" { /* open extern "C" */ #endif +/* + * Atomic types highly desired, but if not, we approximate what we need + * with normal integers and warn. + */ #ifdef HAVE_STDATOMIC_H #include +#else +#warning "No available" +typedef u_int64_t atomic_uint_fast64_t; #endif // HAVE_STDATOMIC_H struct iperf_test; diff --git a/src/main.c b/src/main.c index 3b397c0..b179f5b 100644 --- a/src/main.c +++ b/src/main.c @@ -1,5 +1,5 @@ /* - * iperf, Copyright (c) 2014-2022, The Regents of the University of + * iperf, Copyright (c) 2014-2023, The Regents of the University of * California, through Lawrence Berkeley National Laboratory (subject * to receipt of any required approvals from the U.S. Dept. of * Energy). All rights reserved. @@ -59,6 +59,24 @@ main(int argc, char **argv) { struct iperf_test *test; + /* + * Atomics check. We prefer to have atomic types (which is + * basically on any compiler supporting C11 or better). If we + * don't have them, we try to approximate the type we need with a + * regular integer, but complain if they're not lock-free. We only + * know how to check this on GCC. GCC on CentOS 7 / RHEL 7 is the + * targeted use case for these check. + */ +#ifndef HAVE_STDATOMIC_H +#ifdef __GNUC__ + if (! __atomic_always_lock_free (sizeof (u_int64_t), 0)) { +#endif // __GNUC__ + fprintf(stderr, "Warning: Cannot guarantee lock-free operation with 64-bit data types\n"); +#ifdef __GNUC__ + } +#endif // __GNUC__ +#endif // HAVE_STDATOMIC_H + // XXX: Setting the process affinity requires root on most systems. // Is this a feature we really need? #ifdef TEST_PROC_AFFINITY -- cgit v1.2.3 From 6e75b072613d7b95fee090c4632e98cb1462643e Mon Sep 17 00:00:00 2001 From: "Bruce A. Mah" Date: Wed, 19 Apr 2023 16:21:35 -0700 Subject: Don't set O_NONBLOCK on sockets used for tests. This fixes a problem where every thread would essentially burn a CPU core busy-waiting, when it didn't need to. It's believed that this excess CPU usage might contribute to packet loss and poor performance. Non-blocking sockets were a necessity with the original single- thread process model of iperf3, in order to allow for concurrency between different test streams. With multiple threads, this is no longer necesary (it's perfectly fine for a thread to block on I/O, as it only services a single test stream and won't affect any others). Problem pointed out by @bltierney. Code review by @swlars. Fixes IPERF-177. --- src/iperf_client_api.c | 13 ------------- src/iperf_server_api.c | 11 ----------- src/net.c | 5 ++++- 3 files changed, 4 insertions(+), 25 deletions(-) diff --git a/src/iperf_client_api.c b/src/iperf_client_api.c index f0c4a58..f609cc4 100644 --- a/src/iperf_client_api.c +++ b/src/iperf_client_api.c @@ -662,12 +662,6 @@ iperf_run_client(struct iperf_test * test) goto cleanup_and_fail; } - // Set non-blocking for non-UDP tests - if (test->protocol->id != Pudp) { - SLIST_FOREACH(sp, &test->streams, streams) { - setnonblocking(sp->socket, 1); - } - } } /* Run the timers. */ @@ -706,13 +700,6 @@ iperf_run_client(struct iperf_test * test) iperf_printf(test, "Sender threads stopped\n"); } - // Unset non-blocking for non-UDP tests - if (test->protocol->id != Pudp) { - SLIST_FOREACH(sp, &test->streams, streams) { - setnonblocking(sp->socket, 0); - } - } - /* Yes, done! Send TEST_END. */ test->done = 1; cpu_util(test->cpu_util); diff --git a/src/iperf_server_api.c b/src/iperf_server_api.c index f36eaa3..21fcc2d 100644 --- a/src/iperf_server_api.c +++ b/src/iperf_server_api.c @@ -757,17 +757,6 @@ iperf_run_server(struct iperf_test *test) if (s > test->max_fd) test->max_fd = s; - /* - * If the protocol isn't UDP, or even if it is but - * we're the receiver, set nonblocking sockets. - * We need this to allow a server receiver to - * maintain interactivity with the control channel. - */ - if (test->protocol->id != Pudp || - !sp->sender) { - setnonblocking(s, 1); - } - if (test->on_new_stream) test->on_new_stream(sp); diff --git a/src/net.c b/src/net.c index b80fb64..c82caff 100644 --- a/src/net.c +++ b/src/net.c @@ -1,5 +1,5 @@ /* - * iperf, Copyright (c) 2014-2019, The Regents of the University of + * iperf, Copyright (c) 2014-2023, The Regents of the University of * California, through Lawrence Berkeley National Laboratory (subject * to receipt of any required approvals from the U.S. Dept. of * Energy). All rights reserved. @@ -405,6 +405,7 @@ Nread(int fd, char *buf, size_t count, int prot) while (nleft > 0) { r = read(fd, buf, nleft); if (r < 0) { + /* XXX EWOULDBLOCK can't happen without non-blocking sockets */ if (errno == EINTR || errno == EAGAIN || errno == EWOULDBLOCK) break; else @@ -469,6 +470,7 @@ Nwrite(int fd, const char *buf, size_t count, int prot) case EINTR: case EAGAIN: #if (EAGAIN != EWOULDBLOCK) + /* XXX EWOULDBLOCK can't happen without non-blocking sockets */ case EWOULDBLOCK: #endif return count - nleft; @@ -539,6 +541,7 @@ Nsendfile(int fromfd, int tofd, const char *buf, size_t count) case EINTR: case EAGAIN: #if (EAGAIN != EWOULDBLOCK) + /* XXX EWOULDBLOCK can't happen without non-blocking sockets */ case EWOULDBLOCK: #endif if (count == nleft) -- cgit v1.2.3 From 77685c14524f513bc51c1d97abffc1ae4a18963b Mon Sep 17 00:00:00 2001 From: "Bruce A. Mah" Date: Fri, 21 Apr 2023 14:56:34 -0700 Subject: Update description of -P option for multithreading. --- src/iperf3.1 | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/iperf3.1 b/src/iperf3.1 index 18ccfef..df65402 100644 --- a/src/iperf3.1 +++ b/src/iperf3.1 @@ -1,4 +1,4 @@ -.TH IPERF3 1 "September 2022" ESnet "User Manuals" +.TH IPERF3 1 "April 2023" ESnet "User Manuals" .SH NAME iperf3 \- perform network throughput tests .SH SYNOPSIS @@ -326,7 +326,9 @@ bind data streams to a specific client port (for TCP and UDP only, default is to use an ephemeral port) .TP .BR -P ", " --parallel " \fIn\fR" -number of parallel client streams to run. Note that iperf3 is single threaded, so if you are CPU bound, this will not yield higher throughput. +number of parallel client streams to run. iperf3 will spawn off a +separate thread for each test stream. Using multiple streams may +result in higher throughput than a single stream. .TP .BR -R ", " --reverse reverse the direction of a test, so that the server sends data to the -- cgit v1.2.3 From 2e80758ad49dfaeae3970d09227e3b28e412f06c Mon Sep 17 00:00:00 2001 From: "Bruce A. Mah" Date: Thu, 27 Apr 2023 18:28:06 -0700 Subject: Fix a bug related to idle timeouts on the test receiver. iperf3 implements a limit intended to allow the receiving side of a test to abort a test in progress if no data has been received for a certain length of time. This time limit is configured with the --rcv-timeout command-line option. The original implementation didn't work correctly with multi-threading because the code that implemented the limit had no visibility into the network I/O activity handled by other threads. The code has been restructured to make this work correctly, by watching the total number of blocks transferred in the test and using that to determine progress (or lack thereof). A minor change was also made to allow worker threads to be cancelled, even if they were blocked waiting for network I/O. While necessary for the testing protocol for this bug, this change might also improve the correctness of thread handling around the end of tests. Fixes IPERF-178. --- src/iperf_client_api.c | 51 ++++++++++++++++++++++++++++++++++++-------- src/iperf_server_api.c | 57 ++++++++++++++++++++++++++++++++++++++------------ 2 files changed, 86 insertions(+), 22 deletions(-) diff --git a/src/iperf_client_api.c b/src/iperf_client_api.c index f609cc4..bb103df 100644 --- a/src/iperf_client_api.c +++ b/src/iperf_client_api.c @@ -56,6 +56,10 @@ iperf_client_worker_run(void *s) { struct iperf_stream *sp = (struct iperf_stream *) s; struct iperf_test *test = sp->test; + /* Allow this thread to be cancelled even if it's in a syscall */ + pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, NULL); + pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL); + while (! (test->done) && ! (sp->done)) { if (sp->sender) { if (iperf_send_mt(sp) < 0) { @@ -536,6 +540,7 @@ iperf_run_client(struct iperf_test * test) struct iperf_time last_receive_time; struct iperf_time diff_time; struct timeval used_timeout; + iperf_size_t last_receive_blocks; int64_t t_usecs; int64_t timeout_us; int64_t rcv_timeout_us; @@ -580,6 +585,9 @@ iperf_run_client(struct iperf_test * test) else rcv_timeout_us = 0; + iperf_time_now(&last_receive_time); // Initialize last time something was received + last_receive_blocks = 0; + startup = 1; while (test->state != IPERF_DONE) { memcpy(&read_set, &test->read_set, sizeof(fd_set)); @@ -595,6 +603,10 @@ iperf_run_client(struct iperf_test * test) used_timeout.tv_usec = timeout->tv_usec; timeout_us = (timeout->tv_sec * SEC_TO_US) + timeout->tv_usec; } + /* Cap the maximum select timeout at 1 second */ + if (timeout_us > SEC_TO_US) { + timeout_us = SEC_TO_US; + } if (timeout_us < 0 || timeout_us > rcv_timeout_us) { used_timeout.tv_sec = test->settings->rcv_timeout.secs; used_timeout.tv_usec = test->settings->rcv_timeout.usecs; @@ -607,23 +619,32 @@ iperf_run_client(struct iperf_test * test) i_errno = IESELECT; goto cleanup_and_fail; } else if (result == 0 && test->state == TEST_RUNNING && rcv_timeout_us > 0) { - // If nothing was received in non-reverse running state then probably something got stack - - // either client, server or network, and test should be terminated. + /* + * If nothing was received in non-reverse running state + * then probably something got stuck - either client, + * server or network, and test should be terminated./ + */ iperf_time_now(&now); if (iperf_time_diff(&now, &last_receive_time, &diff_time) == 0) { t_usecs = iperf_time_in_usecs(&diff_time); if (t_usecs > rcv_timeout_us) { - i_errno = IENOMSG; - goto cleanup_and_fail; + /* Idle timeout if no new blocks received */ + if (test->blocks_received == last_receive_blocks) { + i_errno = IENOMSG; + goto cleanup_and_fail; + } } } } + /* See if the test is making progress */ + if (test->blocks_received > last_receive_blocks) { + last_receive_blocks = test->blocks_received; + last_receive_time = now; + } + if (result > 0) { - if (rcv_timeout_us > 0) { - iperf_time_now(&last_receive_time); - } if (FD_ISSET(test->ctrl_sck, &read_set)) { if (iperf_handle_message_client(test) < 0) { goto cleanup_and_fail; @@ -687,6 +708,10 @@ iperf_run_client(struct iperf_test * test) SLIST_FOREACH(sp, &test->streams, streams) { if (sp->sender) { sp->done = 1; + if (pthread_cancel(sp->thr) != 0) { + i_errno = IEPTHREADCANCEL; + goto cleanup_and_fail; + } if (pthread_join(sp->thr, NULL) != 0) { i_errno = IEPTHREADJOIN; goto cleanup_and_fail; @@ -714,6 +739,10 @@ iperf_run_client(struct iperf_test * test) SLIST_FOREACH(sp, &test->streams, streams) { if (!sp->sender) { sp->done = 1; + if (pthread_cancel(sp->thr) != 0) { + i_errno = IEPTHREADCANCEL; + goto cleanup_and_fail; + } if (pthread_join(sp->thr, NULL) != 0) { i_errno = IEPTHREADJOIN; goto cleanup_and_fail; @@ -744,9 +773,13 @@ iperf_run_client(struct iperf_test * test) i_errno_save = i_errno; SLIST_FOREACH(sp, &test->streams, streams) { sp->done = 1; - if (pthread_join(sp->thr, NULL) != 0) { + if (pthread_cancel(sp->thr) != 0) { i_errno = IEPTHREADCANCEL; - iperf_err(test, "cleanup_and_fail - %s", iperf_strerror(i_errno)); + iperf_err(test, "cleanup_and_fail in cancel - %s", iperf_strerror(i_errno)); + } + if (pthread_join(sp->thr, NULL) != 0) { + i_errno = IEPTHREADJOIN; + iperf_err(test, "cleanup_and_fail in join - %s", iperf_strerror(i_errno)); } if (test->debug >= DEBUG_LEVEL_INFO) { iperf_printf(test, "Thread FD %d stopped\n", sp->socket); diff --git a/src/iperf_server_api.c b/src/iperf_server_api.c index 21fcc2d..ea9864f 100644 --- a/src/iperf_server_api.c +++ b/src/iperf_server_api.c @@ -71,6 +71,10 @@ iperf_server_worker_run(void *s) { struct iperf_stream *sp = (struct iperf_stream *) s; struct iperf_test *test = sp->test; + /* Allow this thread to be cancelled even if it's in a syscall */ + pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, NULL); + pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL); + while (! (test->done) && ! (sp->done)) { if (sp->sender) { if (iperf_send_mt(sp) < 0) { @@ -415,9 +419,13 @@ cleanup_server(struct iperf_test *test) int i_errno_save = i_errno; SLIST_FOREACH(sp, &test->streams, streams) { sp->done = 1; + if (pthread_cancel(sp->thr) != 0) { + i_errno = IEPTHREADCANCEL; + iperf_err(test, "cleanup_server in cancel - %s", iperf_strerror(i_errno)); + } if (pthread_join(sp->thr, NULL) != 0) { i_errno = IEPTHREADJOIN; - iperf_err(test, "cleanup_server - %s", iperf_strerror(i_errno)); + iperf_err(test, "cleanup_server in join - %s", iperf_strerror(i_errno)); } if (test->debug >= DEBUG_LEVEL_INFO) { iperf_printf(test, "Thread FD %d stopped\n", sp->socket); @@ -493,6 +501,7 @@ iperf_run_server(struct iperf_test *test) struct iperf_time diff_time; struct timeval* timeout; struct timeval used_timeout; + iperf_size_t last_receive_blocks; int flag; int64_t t_usecs; int64_t timeout_us; @@ -531,6 +540,7 @@ iperf_run_server(struct iperf_test *test) } iperf_time_now(&last_receive_time); // Initialize last time something was received + last_receive_blocks = 0; test->state = IPERF_START; send_streams_accepted = 0; @@ -566,6 +576,10 @@ iperf_run_server(struct iperf_test *test) used_timeout.tv_usec = timeout->tv_usec; timeout_us = (timeout->tv_sec * SEC_TO_US) + timeout->tv_usec; } + /* Cap the maximum select timeout at 1 second */ + if (timeout_us > SEC_TO_US) { + timeout_us = SEC_TO_US; + } if (timeout_us < 0 || timeout_us > rcv_timeout_us) { used_timeout.tv_sec = test->settings->rcv_timeout.secs; used_timeout.tv_usec = test->settings->rcv_timeout.usecs; @@ -579,13 +593,18 @@ iperf_run_server(struct iperf_test *test) i_errno = IESELECT; return -1; } else if (result == 0) { - // If nothing was received during the specified time (per state) - // then probably something got stack either at the client, server or network, - // and Test should be forced to end. + /* + * If nothing was received during the specified time (per + * state) then probably something got stuck either at the + * client, server or network, and test should be forced to + * end. + */ iperf_time_now(&now); t_usecs = 0; if (iperf_time_diff(&now, &last_receive_time, &diff_time) == 0) { t_usecs = iperf_time_in_usecs(&diff_time); + + /* We're in the state where we're still accepting connections */ if (test->state == IPERF_START) { if (test->settings->idle_timeout > 0 && t_usecs >= test->settings->idle_timeout * SEC_TO_US) { test->server_forced_idle_restarts_count += 1; @@ -603,21 +622,33 @@ iperf_run_server(struct iperf_test *test) return 2; } } + + /* + * Running a test. If we're receiving, be sure we're making + * progress (sender hasn't died/crashed). + */ else if (test->mode != SENDER && t_usecs > rcv_timeout_us) { - test->server_forced_no_msg_restarts_count += 1; - i_errno = IENOMSG; - if (iperf_get_verbose(test)) - iperf_err(test, "Server restart (#%d) during active test due to idle timeout for receiving data", - test->server_forced_no_msg_restarts_count); - cleanup_server(test); - return -1; + /* Idle timeout if no new blocks received */ + if (test->blocks_received == last_receive_blocks) { + test->server_forced_no_msg_restarts_count += 1; + i_errno = IENOMSG; + if (iperf_get_verbose(test)) + iperf_err(test, "Server restart (#%d) during active test due to idle timeout for receiving data", + test->server_forced_no_msg_restarts_count); + cleanup_server(test); + return -1; + } } - } } + /* See if the test is making progress */ + if (test->blocks_received > last_receive_blocks) { + last_receive_blocks = test->blocks_received; + last_receive_time = now; + } + if (result > 0) { - iperf_time_now(&last_receive_time); if (FD_ISSET(test->listener, &read_set)) { if (test->state != CREATE_STREAMS) { if (iperf_accept(test) < 0) { -- cgit v1.2.3 From a40c9b3ec53a3e2c69849db276503d8735defb5d Mon Sep 17 00:00:00 2001 From: "Bruce A. Mah" Date: Fri, 28 Apr 2023 10:23:12 -0700 Subject: Make thread shutdown more tolerant. We now handle the case where the worker threads exited on their own accord before the main thread had a chance to cancel them. While here, tweaked some of the error messages. Fixes IPERF-179. --- src/iperf_client_api.c | 41 ++++++++++++++++++++++++++++++----------- src/iperf_server_api.c | 15 ++++++++++----- 2 files changed, 40 insertions(+), 16 deletions(-) diff --git a/src/iperf_client_api.c b/src/iperf_client_api.c index bb103df..dd824a7 100644 --- a/src/iperf_client_api.c +++ b/src/iperf_client_api.c @@ -704,16 +704,23 @@ iperf_run_client(struct iperf_test * test) (test->settings->blocks != 0 && (test->blocks_sent >= test->settings->blocks || test->blocks_received >= test->settings->blocks)))) { - /* Cancel sender threads */ + /* Cancel outstanding sender threads */ SLIST_FOREACH(sp, &test->streams, streams) { if (sp->sender) { + int rc; sp->done = 1; - if (pthread_cancel(sp->thr) != 0) { + rc = pthread_cancel(sp->thr); + if (rc != 0 && rc != ESRCH) { i_errno = IEPTHREADCANCEL; + errno = rc; + iperf_err(test, "sender cancel in pthread_cancel - %s", iperf_strerror(i_errno)); goto cleanup_and_fail; } - if (pthread_join(sp->thr, NULL) != 0) { + rc = pthread_join(sp->thr, NULL); + if (rc != 0 && rc != ESRCH) { i_errno = IEPTHREADJOIN; + errno = rc; + iperf_err(test, "sender cancel in pthread_join - %s", iperf_strerror(i_errno)); goto cleanup_and_fail; } if (test->debug_level >= DEBUG_LEVEL_INFO) { @@ -735,16 +742,23 @@ iperf_run_client(struct iperf_test * test) } } - /* Cancel receiver threads */ + /* Cancel outstanding receiver threads */ SLIST_FOREACH(sp, &test->streams, streams) { if (!sp->sender) { + int rc; sp->done = 1; - if (pthread_cancel(sp->thr) != 0) { + rc = pthread_cancel(sp->thr); + if (rc != 0 && rc != ESRCH) { i_errno = IEPTHREADCANCEL; + errno = rc; + iperf_err(test, "receiver cancel in pthread_cancel - %s", iperf_strerror(i_errno)); goto cleanup_and_fail; } - if (pthread_join(sp->thr, NULL) != 0) { + rc = pthread_join(sp->thr, NULL); + if (rc != 0 && rc != ESRCH) { i_errno = IEPTHREADJOIN; + errno = rc; + iperf_err(test, "receiver cancel in pthread_join - %s", iperf_strerror(i_errno)); goto cleanup_and_fail; } if (test->debug_level >= DEBUG_LEVEL_INFO) { @@ -769,17 +783,22 @@ iperf_run_client(struct iperf_test * test) return 0; cleanup_and_fail: - /* Cancel all threads */ + /* Cancel all outstanding threads */ i_errno_save = i_errno; SLIST_FOREACH(sp, &test->streams, streams) { sp->done = 1; - if (pthread_cancel(sp->thr) != 0) { + int rc; + rc = pthread_cancel(sp->thr); + if (rc != 0 && rc != ESRCH) { i_errno = IEPTHREADCANCEL; - iperf_err(test, "cleanup_and_fail in cancel - %s", iperf_strerror(i_errno)); + errno = rc; + iperf_err(test, "cleanup_and_fail in pthread_cancel - %s", iperf_strerror(i_errno)); } - if (pthread_join(sp->thr, NULL) != 0) { + rc = pthread_join(sp->thr, NULL); + if (rc != 0 && rc != ESRCH) { i_errno = IEPTHREADJOIN; - iperf_err(test, "cleanup_and_fail in join - %s", iperf_strerror(i_errno)); + errno = rc; + iperf_err(test, "cleanup_and_fail in pthread_join - %s", iperf_strerror(i_errno)); } if (test->debug >= DEBUG_LEVEL_INFO) { iperf_printf(test, "Thread FD %d stopped\n", sp->socket); diff --git a/src/iperf_server_api.c b/src/iperf_server_api.c index ea9864f..5f1d947 100644 --- a/src/iperf_server_api.c +++ b/src/iperf_server_api.c @@ -415,17 +415,22 @@ cleanup_server(struct iperf_test *test) { struct iperf_stream *sp; - /* Cancel threads */ + /* Cancel outstanding threads */ int i_errno_save = i_errno; SLIST_FOREACH(sp, &test->streams, streams) { + int rc; sp->done = 1; - if (pthread_cancel(sp->thr) != 0) { + rc = pthread_cancel(sp->thr); + if (rc != 0 && rc != ESRCH) { i_errno = IEPTHREADCANCEL; - iperf_err(test, "cleanup_server in cancel - %s", iperf_strerror(i_errno)); + errno = rc; + iperf_err(test, "cleanup_server in pthread_cancel - %s", iperf_strerror(i_errno)); } - if (pthread_join(sp->thr, NULL) != 0) { + rc = pthread_join(sp->thr, NULL); + if (rc != 0 && rc != ESRCH) { i_errno = IEPTHREADJOIN; - iperf_err(test, "cleanup_server in join - %s", iperf_strerror(i_errno)); + errno = rc; + iperf_err(test, "cleanup_server in pthread_join - %s", iperf_strerror(i_errno)); } if (test->debug >= DEBUG_LEVEL_INFO) { iperf_printf(test, "Thread FD %d stopped\n", sp->socket); -- cgit v1.2.3 From 83693ee974473628c355be41021af71fa968273c Mon Sep 17 00:00:00 2001 From: Sarah Larsen Date: Mon, 8 May 2023 14:37:06 -0700 Subject: Fix debug level --- src/iperf_client_api.c | 2 +- src/iperf_server_api.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/iperf_client_api.c b/src/iperf_client_api.c index dd824a7..5b33369 100644 --- a/src/iperf_client_api.c +++ b/src/iperf_client_api.c @@ -800,7 +800,7 @@ iperf_run_client(struct iperf_test * test) errno = rc; iperf_err(test, "cleanup_and_fail in pthread_join - %s", iperf_strerror(i_errno)); } - if (test->debug >= DEBUG_LEVEL_INFO) { + if (test->debug_level >= DEBUG_LEVEL_INFO) { iperf_printf(test, "Thread FD %d stopped\n", sp->socket); } } diff --git a/src/iperf_server_api.c b/src/iperf_server_api.c index 5f1d947..77e9c35 100644 --- a/src/iperf_server_api.c +++ b/src/iperf_server_api.c @@ -432,7 +432,7 @@ cleanup_server(struct iperf_test *test) errno = rc; iperf_err(test, "cleanup_server in pthread_join - %s", iperf_strerror(i_errno)); } - if (test->debug >= DEBUG_LEVEL_INFO) { + if (test->debug_level >= DEBUG_LEVEL_INFO) { iperf_printf(test, "Thread FD %d stopped\n", sp->socket); } } -- cgit v1.2.3 From 83a92d682afb867423eb67ca4f66cb57572d9713 Mon Sep 17 00:00:00 2001 From: "Bruce A. Mah" Date: Fri, 15 Sep 2023 16:08:33 -0700 Subject: Add accumulated release notes from prior iperf-mt public beta releases. --- RELNOTES.md | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/RELNOTES.md b/RELNOTES.md index 48130e9..94ede52 100644 --- a/RELNOTES.md +++ b/RELNOTES.md @@ -1,6 +1,38 @@ iperf3 Release Notes ==================== +iperf-3.15-mt-beta1 +------------------- +Accumulated release notes from iperf-3.14 and earlier multithreaded +beta releases: + +* Notable user-visible changes + + * Multiple test streams started with -P/--parallel will now be + serviced by different threads. This allows iperf3 to take + advantage of multiple CPU cores on modern processors. + + * Remove some busy-waiting left over from the original + single-threaded implementation, which caused the multi-threaded + iperf3 to consume CPU resources for no particular reason, and + possible subsequent packet loss. + + * CentOS 7's default compiler is a version of GCC that is too old to + compile code using C11 atomic variables. A workaround has been + devised for 64-bit CentOS 7 systems, it is not clear whether this + approach will work on 32-bit CentOS 7 hosts, or other + similarly-vintage build environment. + + * Fix a bug related to idle timeouts, so that the --rcv-timeout + option works correctly. + + * Make shutdown of threads more tolerant in the face of various + orders of operations at the end of tests. + +* Developer-visible changes + + * iperf3 requires pthreads and C atomic variables to compile and run. + iperf-3.15 2023-09-14 --------------------- -- cgit v1.2.3 From 6cfec70000444c4819d3bf674217108429034e21 Mon Sep 17 00:00:00 2001 From: "Bruce A. Mah" Date: Fri, 15 Sep 2023 16:09:40 -0700 Subject: Version update for mt branch. --- configure.ac | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/configure.ac b/configure.ac index 92b9ced..b2c19ca 100644 --- a/configure.ac +++ b/configure.ac @@ -25,7 +25,7 @@ # Initialize the autoconf system for the specified tool, version and mailing list AC_PREREQ([2.71]) -AC_INIT([iperf],[3.15],[https://github.com/esnet/iperf],[iperf],[https://software.es.net/iperf/]) +AC_INIT([iperf],[3.15-mt],[https://github.com/esnet/iperf],[iperf],[https://software.es.net/iperf/]) m4_include([config/ax_check_openssl.m4]) m4_include([config/ax_pthread.m4]) m4_include([config/iperf_config_static_bin.m4]) -- cgit v1.2.3 From 418eef5518d3e6b2db92fd68799f386f1217c521 Mon Sep 17 00:00:00 2001 From: "Bruce A. Mah" Date: Fri, 15 Sep 2023 16:10:56 -0700 Subject: Regen. --- configure | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/configure b/configure index 727ab34..a539d16 100755 --- a/configure +++ b/configure @@ -1,6 +1,6 @@ #! /bin/sh # Guess values for system-dependent variables and create Makefiles. -# Generated by GNU Autoconf 2.71 for iperf 3.15. +# Generated by GNU Autoconf 2.71 for iperf 3.15-mt. # # Report bugs to . # @@ -621,8 +621,8 @@ MAKEFLAGS= # Identity of this package. PACKAGE_NAME='iperf' PACKAGE_TARNAME='iperf' -PACKAGE_VERSION='3.15' -PACKAGE_STRING='iperf 3.15' +PACKAGE_VERSION='3.15-mt' +PACKAGE_STRING='iperf 3.15-mt' PACKAGE_BUGREPORT='https://github.com/esnet/iperf' PACKAGE_URL='https://software.es.net/iperf/' @@ -1366,7 +1366,7 @@ if test "$ac_init_help" = "long"; then # Omit some internal or obsolete options to make the list less imposing. # This message is too long to be a string in the A/UX 3.1 sh. cat <<_ACEOF -\`configure' configures iperf 3.15 to adapt to many kinds of systems. +\`configure' configures iperf 3.15-mt to adapt to many kinds of systems. Usage: $0 [OPTION]... [VAR=VALUE]... @@ -1437,7 +1437,7 @@ fi if test -n "$ac_init_help"; then case $ac_init_help in - short | recursive ) echo "Configuration of iperf 3.15:";; + short | recursive ) echo "Configuration of iperf 3.15-mt:";; esac cat <<\_ACEOF @@ -1555,7 +1555,7 @@ fi test -n "$ac_init_help" && exit $ac_status if $ac_init_version; then cat <<\_ACEOF -iperf configure 3.15 +iperf configure 3.15-mt generated by GNU Autoconf 2.71 Copyright (C) 2021 Free Software Foundation, Inc. @@ -1833,7 +1833,7 @@ cat >config.log <<_ACEOF This file contains any messages produced by compilers while running configure, to aid debugging if configure makes a mistake. -It was created by iperf $as_me 3.15, which was +It was created by iperf $as_me 3.15-mt, which was generated by GNU Autoconf 2.71. Invocation command line was $ $0$ac_configure_args_raw @@ -3200,7 +3200,7 @@ fi # Define the identity of the package. PACKAGE='iperf' - VERSION='3.15' + VERSION='3.15-mt' printf "%s\n" "#define PACKAGE \"$PACKAGE\"" >>confdefs.h @@ -15579,7 +15579,7 @@ cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 # report actual input values of CONFIG_FILES etc. instead of their # values after options handling. ac_log=" -This file was extended by iperf $as_me 3.15, which was +This file was extended by iperf $as_me 3.15-mt, which was generated by GNU Autoconf 2.71. Invocation command line was CONFIG_FILES = $CONFIG_FILES @@ -15648,7 +15648,7 @@ ac_cs_config_escaped=`printf "%s\n" "$ac_cs_config" | sed "s/^ //; s/'/'\\\\\\\\ cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 ac_cs_config='$ac_cs_config_escaped' ac_cs_version="\\ -iperf config.status 3.15 +iperf config.status 3.15-mt configured by $0, generated by GNU Autoconf 2.71, with options \\"\$ac_cs_config\\" @@ -17350,3 +17350,4 @@ printf "%s\n" "$as_me: WARNING: unrecognized options: $ac_unrecognized_opts" >&2 fi + -- cgit v1.2.3 From 6497aad2d4600575ac168dde0cf7aa60b641a3a0 Mon Sep 17 00:00:00 2001 From: Sarah Larsen Date: Fri, 29 Sep 2023 19:04:00 +0000 Subject: Add dates to RELNOTES.md for iperf-3.15-mt-beta1. --- RELNOTES.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/RELNOTES.md b/RELNOTES.md index 94ede52..0a26d5b 100644 --- a/RELNOTES.md +++ b/RELNOTES.md @@ -1,8 +1,8 @@ iperf3 Release Notes ==================== -iperf-3.15-mt-beta1 -------------------- +iperf-3.15-mt-beta1 2023-09-29 +------------------------------ Accumulated release notes from iperf-3.14 and earlier multithreaded beta releases: -- cgit v1.2.3 From 291c48e0e03d0065138a862677a1f16203b15fe4 Mon Sep 17 00:00:00 2001 From: "Bruce A. Mah" Date: Wed, 8 Nov 2023 11:20:21 -0800 Subject: Regen. --- Makefile.in | 7 + configure | 1253 +++++++++++++++++++++++++++++++++++++++++++------ examples/Makefile.in | 7 + src/Makefile.in | 7 + src/iperf_config.h.in | 13 + 5 files changed, 1144 insertions(+), 143 deletions(-) diff --git a/Makefile.in b/Makefile.in index 522ca30..e38f265 100644 --- a/Makefile.in +++ b/Makefile.in @@ -90,6 +90,7 @@ host_triplet = @host@ subdir = . ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 am__aclocal_m4_deps = $(top_srcdir)/config/ax_check_openssl.m4 \ + $(top_srcdir)/config/ax_pthread.m4 \ $(top_srcdir)/config/iperf_config_static_bin.m4 \ $(top_srcdir)/configure.ac am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ @@ -219,6 +220,7 @@ AWK = @AWK@ CC = @CC@ CCDEPMODE = @CCDEPMODE@ CFLAGS = @CFLAGS@ +CPP = @CPP@ CPPFLAGS = @CPPFLAGS@ CSCOPE = @CSCOPE@ CTAGS = @CTAGS@ @@ -273,6 +275,10 @@ PACKAGE_URL = @PACKAGE_URL@ PACKAGE_VERSION = @PACKAGE_VERSION@ PATH_SEPARATOR = @PATH_SEPARATOR@ PKG_CONFIG = @PKG_CONFIG@ +PTHREAD_CC = @PTHREAD_CC@ +PTHREAD_CFLAGS = @PTHREAD_CFLAGS@ +PTHREAD_CXX = @PTHREAD_CXX@ +PTHREAD_LIBS = @PTHREAD_LIBS@ RANLIB = @RANLIB@ SED = @SED@ SET_MAKE = @SET_MAKE@ @@ -291,6 +297,7 @@ am__leading_dot = @am__leading_dot@ am__quote = @am__quote@ am__tar = @am__tar@ am__untar = @am__untar@ +ax_pthread_config = @ax_pthread_config@ bindir = @bindir@ build = @build@ build_alias = @build_alias@ diff --git a/configure b/configure index a539d16..76ab0cc 100755 --- a/configure +++ b/configure @@ -666,6 +666,12 @@ OPENSSL_LDFLAGS OPENSSL_LIBS OPENSSL_INCLUDES PKG_CONFIG +PTHREAD_CFLAGS +PTHREAD_LIBS +PTHREAD_CXX +PTHREAD_CC +ax_pthread_config +CPP ENABLE_PROFILING_FALSE ENABLE_PROFILING_TRUE MAINT @@ -817,7 +823,8 @@ CFLAGS LDFLAGS LIBS CPPFLAGS -LT_SYS_LIBRARY_PATH' +LT_SYS_LIBRARY_PATH +CPP' # Initialize some variables set by options. @@ -1486,6 +1493,7 @@ Some influential environment variables: you have headers in a nonstandard directory LT_SYS_LIBRARY_PATH User-defined run-time library search path. + CPP C preprocessor Use these variables to override the choices made by `configure' or to help it to find libraries and programs with nonstandard names/locations. @@ -1750,6 +1758,44 @@ printf "%s\n" "$ac_res" >&6; } } # ac_fn_c_check_func +# ac_fn_c_try_cpp LINENO +# ---------------------- +# Try to preprocess conftest.$ac_ext, and return whether this succeeded. +ac_fn_c_try_cpp () +{ + as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack + if { { ac_try="$ac_cpp conftest.$ac_ext" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" +printf "%s\n" "$ac_try_echo"; } >&5 + (eval "$ac_cpp conftest.$ac_ext") 2>conftest.err + ac_status=$? + if test -s conftest.err; then + grep -v '^ *+' conftest.err >conftest.er1 + cat conftest.er1 >&5 + mv -f conftest.er1 conftest.err + fi + printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; } > conftest.i && { + test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" || + test ! -s conftest.err + } +then : + ac_retval=0 +else $as_nop + printf "%s\n" "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_retval=1 +fi + eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno + as_fn_set_status $ac_retval + +} # ac_fn_c_try_cpp + # ac_fn_c_check_member LINENO AGGR MEMBER VAR INCLUDES # ---------------------------------------------------- # Tries to find if the field MEMBER exists in type AGGR, after including @@ -2647,6 +2693,119 @@ ac_compiler_gnu=$ac_cv_c_compiler_gnu +# =========================================================================== +# https://www.gnu.org/software/autoconf-archive/ax_pthread.html +# =========================================================================== +# +# SYNOPSIS +# +# AX_PTHREAD([ACTION-IF-FOUND[, ACTION-IF-NOT-FOUND]]) +# +# DESCRIPTION +# +# This macro figures out how to build C programs using POSIX threads. It +# sets the PTHREAD_LIBS output variable to the threads library and linker +# flags, and the PTHREAD_CFLAGS output variable to any special C compiler +# flags that are needed. (The user can also force certain compiler +# flags/libs to be tested by setting these environment variables.) +# +# Also sets PTHREAD_CC and PTHREAD_CXX to any special C compiler that is +# needed for multi-threaded programs (defaults to the value of CC +# respectively CXX otherwise). (This is necessary on e.g. AIX to use the +# special cc_r/CC_r compiler alias.) +# +# NOTE: You are assumed to not only compile your program with these flags, +# but also to link with them as well. For example, you might link with +# $PTHREAD_CC $CFLAGS $PTHREAD_CFLAGS $LDFLAGS ... $PTHREAD_LIBS $LIBS +# $PTHREAD_CXX $CXXFLAGS $PTHREAD_CFLAGS $LDFLAGS ... $PTHREAD_LIBS $LIBS +# +# If you are only building threaded programs, you may wish to use these +# variables in your default LIBS, CFLAGS, and CC: +# +# LIBS="$PTHREAD_LIBS $LIBS" +# CFLAGS="$CFLAGS $PTHREAD_CFLAGS" +# CXXFLAGS="$CXXFLAGS $PTHREAD_CFLAGS" +# CC="$PTHREAD_CC" +# CXX="$PTHREAD_CXX" +# +# In addition, if the PTHREAD_CREATE_JOINABLE thread-attribute constant +# has a nonstandard name, this macro defines PTHREAD_CREATE_JOINABLE to +# that name (e.g. PTHREAD_CREATE_UNDETACHED on AIX). +# +# Also HAVE_PTHREAD_PRIO_INHERIT is defined if pthread is found and the +# PTHREAD_PRIO_INHERIT symbol is defined when compiling with +# PTHREAD_CFLAGS. +# +# ACTION-IF-FOUND is a list of shell commands to run if a threads library +# is found, and ACTION-IF-NOT-FOUND is a list of commands to run it if it +# is not found. If ACTION-IF-FOUND is not specified, the default action +# will define HAVE_PTHREAD. +# +# Please let the authors know if this macro fails on any platform, or if +# you have any other suggestions or comments. This macro was based on work +# by SGJ on autoconf scripts for FFTW (http://www.fftw.org/) (with help +# from M. Frigo), as well as ac_pthread and hb_pthread macros posted by +# Alejandro Forero Cuervo to the autoconf macro repository. We are also +# grateful for the helpful feedback of numerous users. +# +# Updated for Autoconf 2.68 by Daniel Richard G. +# +# LICENSE +# +# Copyright (c) 2008 Steven G. Johnson +# Copyright (c) 2011 Daniel Richard G. +# Copyright (c) 2019 Marc Stevens +# +# This program is free software: you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by the +# Free Software Foundation, either version 3 of the License, or (at your +# option) any later version. +# +# This program 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 General +# Public License for more details. +# +# You should have received a copy of the GNU General Public License along +# with this program. If not, see . +# +# As a special exception, the respective Autoconf Macro's copyright owner +# gives unlimited permission to copy, distribute and modify the configure +# scripts that are the output of Autoconf when processing the Macro. You +# need not follow the terms of the GNU General Public License when using +# or distributing such scripts, even though portions of the text of the +# Macro appear in them. The GNU General Public License (GPL) does govern +# all other use of the material that constitutes the Autoconf Macro. +# +# This special exception to the GPL applies to versions of the Autoconf +# Macro released by the Autoconf Archive. When you make and distribute a +# modified version of the Autoconf Macro, you may extend this special +# exception to the GPL to apply to your modified version as well. + +#serial 31 + +# This is what autoupdate's m4 run will expand. It fires the warning +# (with _au_warn_XXX), outputs it into the updated configure.ac (with +# m4_warn), and then outputs the replacement expansion. We need extra +# quotation around the m4_warn and dnl so they will be written +# unexpanded into the updated configure.ac. + + +# This is an auxiliary macro that is also run when +# autoupdate runs m4. It simply calls m4_warning, but +# we need a wrapper so that each warning is emitted only +# once. We break the quoting in m4_warning's argument in +# order to expand this macro's arguments, not AU_DEFUN's. + + +# Finally, this is the expansion that is picked up by +# autoconf, causing NAME to expand to NEW-CODE, plus +# (if SILENT is not "silent") a m4_warning telling the +# maintainer to run autoupdate. We don't issue MESSAGE +# from autoconf, because that's instructions for what +# to do *after* running autoupdate. + + # Also link binaries as static # Check whether --enable-static-bin was given. if test ${enable_static_bin+y} @@ -13707,108 +13866,6 @@ else fi -if test -n "$ac_tool_prefix"; then - # Extract the first word of "${ac_tool_prefix}ranlib", so it can be a program name with args. -set dummy ${ac_tool_prefix}ranlib; ac_word=$2 -{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 -printf %s "checking for $ac_word... " >&6; } -if test ${ac_cv_prog_RANLIB+y} -then : - printf %s "(cached) " >&6 -else $as_nop - if test -n "$RANLIB"; then - ac_cv_prog_RANLIB="$RANLIB" # Let the user override the test. -else -as_save_IFS=$IFS; IFS=$PATH_SEPARATOR -for as_dir in $PATH -do - IFS=$as_save_IFS - case $as_dir in #((( - '') as_dir=./ ;; - */) ;; - *) as_dir=$as_dir/ ;; - esac - for ac_exec_ext in '' $ac_executable_extensions; do - if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then - ac_cv_prog_RANLIB="${ac_tool_prefix}ranlib" - printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5 - break 2 - fi -done - done -IFS=$as_save_IFS - -fi -fi -RANLIB=$ac_cv_prog_RANLIB -if test -n "$RANLIB"; then - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $RANLIB" >&5 -printf "%s\n" "$RANLIB" >&6; } -else - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 -printf "%s\n" "no" >&6; } -fi - - -fi -if test -z "$ac_cv_prog_RANLIB"; then - ac_ct_RANLIB=$RANLIB - # Extract the first word of "ranlib", so it can be a program name with args. -set dummy ranlib; ac_word=$2 -{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 -printf %s "checking for $ac_word... " >&6; } -if test ${ac_cv_prog_ac_ct_RANLIB+y} -then : - printf %s "(cached) " >&6 -else $as_nop - if test -n "$ac_ct_RANLIB"; then - ac_cv_prog_ac_ct_RANLIB="$ac_ct_RANLIB" # Let the user override the test. -else -as_save_IFS=$IFS; IFS=$PATH_SEPARATOR -for as_dir in $PATH -do - IFS=$as_save_IFS - case $as_dir in #((( - '') as_dir=./ ;; - */) ;; - *) as_dir=$as_dir/ ;; - esac - for ac_exec_ext in '' $ac_executable_extensions; do - if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then - ac_cv_prog_ac_ct_RANLIB="ranlib" - printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5 - break 2 - fi -done - done -IFS=$as_save_IFS - -fi -fi -ac_ct_RANLIB=$ac_cv_prog_ac_ct_RANLIB -if test -n "$ac_ct_RANLIB"; then - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_ct_RANLIB" >&5 -printf "%s\n" "$ac_ct_RANLIB" >&6; } -else - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 -printf "%s\n" "no" >&6; } -fi - - if test "x$ac_ct_RANLIB" = x; then - RANLIB=":" - else - case $cross_compiling:$ac_tool_warned in -yes:) -{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 -printf "%s\n" "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} -ac_tool_warned=yes ;; -esac - RANLIB=$ac_ct_RANLIB - fi -else - RANLIB="$ac_cv_prog_RANLIB" -fi - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether ln -s works" >&5 printf %s "checking whether ln -s works... " >&6; } LN_S=$as_ln_s @@ -13821,7 +13878,6 @@ printf "%s\n" "no, using $LN_S" >&6; } fi - # Add -Wall if we are using GCC. if test "x$GCC" = "xyes"; then CFLAGS="$CFLAGS -Wall" @@ -14126,60 +14182,972 @@ printf "%s\n" "#define const /**/" >>confdefs.h fi -# Check for poll.h (it's in POSIX so everyone should have it?) -ac_fn_c_check_header_compile "$LINENO" "poll.h" "ac_cv_header_poll_h" "$ac_includes_default" -if test "x$ac_cv_header_poll_h" = xyes +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking how to run the C preprocessor" >&5 +printf %s "checking how to run the C preprocessor... " >&6; } +# On Suns, sometimes $CPP names a directory. +if test -n "$CPP" && test -d "$CPP"; then + CPP= +fi +if test -z "$CPP"; then + if test ${ac_cv_prog_CPP+y} +then : + printf %s "(cached) " >&6 +else $as_nop + # Double quotes because $CC needs to be expanded + for CPP in "$CC -E" "$CC -E -traditional-cpp" cpp /lib/cpp + do + ac_preproc_ok=false +for ac_c_preproc_warn_flag in '' yes +do + # Use a header file that comes with gcc, so configuring glibc + # with a fresh cross-compiler works. + # On the NeXT, cc -E runs the code through the compiler's parser, + # not just through cpp. "Syntax error" is here to catch this case. + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include + Syntax error +_ACEOF +if ac_fn_c_try_cpp "$LINENO" then : - printf "%s\n" "#define HAVE_POLL_H 1" >>confdefs.h +else $as_nop + # Broken: fails on valid input. +continue fi +rm -f conftest.err conftest.i conftest.$ac_ext - -# SCTP. Allow user to disable SCTP support with --without-sctp. -# Otherwise we try to find whatever support is required. -try_sctp=true - -# Check whether --with-sctp was given. -if test ${with_sctp+y} + # OK, works on sane cases. Now check whether nonexistent headers + # can be detected and how. + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include +_ACEOF +if ac_fn_c_try_cpp "$LINENO" then : - withval=$with_sctp; - case "$withval" in - y | ye | yes) - ;; - n | no) - try_sctp=false - ;; - *) - as_fn_error $? "Invalid --with-sctp value" "$LINENO" 5 - ;; - esac - + # Broken: success on invalid input. +continue else $as_nop + # Passes both tests. +ac_preproc_ok=: +break +fi +rm -f conftest.err conftest.i conftest.$ac_ext - try_sctp=true - - +done +# Because of `break', _AC_PREPROC_IFELSE's cleaning code was skipped. +rm -f conftest.i conftest.err conftest.$ac_ext +if $ac_preproc_ok +then : + break fi + done + ac_cv_prog_CPP=$CPP -ac_fn_c_check_header_compile "$LINENO" "linux/tcp.h" "ac_cv_header_linux_tcp_h" "$ac_includes_default" -if test "x$ac_cv_header_linux_tcp_h" = xyes +fi + CPP=$ac_cv_prog_CPP +else + ac_cv_prog_CPP=$CPP +fi +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $CPP" >&5 +printf "%s\n" "$CPP" >&6; } +ac_preproc_ok=false +for ac_c_preproc_warn_flag in '' yes +do + # Use a header file that comes with gcc, so configuring glibc + # with a fresh cross-compiler works. + # On the NeXT, cc -E runs the code through the compiler's parser, + # not just through cpp. "Syntax error" is here to catch this case. + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include + Syntax error +_ACEOF +if ac_fn_c_try_cpp "$LINENO" then : - printf "%s\n" "#define HAVE_LINUX_TCP_H 1" >>confdefs.h +else $as_nop + # Broken: fails on valid input. +continue fi +rm -f conftest.err conftest.i conftest.$ac_ext - -# Check for SCTP support -if $try_sctp; then -ac_fn_c_check_header_compile "$LINENO" "sys/socket.h" "ac_cv_header_sys_socket_h" "$ac_includes_default" -if test "x$ac_cv_header_sys_socket_h" = xyes + # OK, works on sane cases. Now check whether nonexistent headers + # can be detected and how. + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include +_ACEOF +if ac_fn_c_try_cpp "$LINENO" then : - printf "%s\n" "#define HAVE_SYS_SOCKET_H 1" >>confdefs.h - + # Broken: success on invalid input. +continue +else $as_nop + # Passes both tests. +ac_preproc_ok=: +break fi +rm -f conftest.err conftest.i conftest.$ac_ext - for ac_header in netinet/sctp.h +done +# Because of `break', _AC_PREPROC_IFELSE's cleaning code was skipped. +rm -f conftest.i conftest.err conftest.$ac_ext +if $ac_preproc_ok +then : + +else $as_nop + { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 +printf "%s\n" "$as_me: error: in \`$ac_pwd':" >&2;} +as_fn_error $? "C preprocessor \"$CPP\" fails sanity check +See \`config.log' for more details" "$LINENO" 5; } +fi + +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu + + + + + + +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu + +ax_pthread_ok=no + +# We used to check for pthread.h first, but this fails if pthread.h +# requires special compiler flags (e.g. on Tru64 or Sequent). +# It gets checked for in the link test anyway. + +# First of all, check if the user has set any of the PTHREAD_LIBS, +# etcetera environment variables, and if threads linking works using +# them: +if test "x$PTHREAD_CFLAGS$PTHREAD_LIBS" != "x"; then + ax_pthread_save_CC="$CC" + ax_pthread_save_CFLAGS="$CFLAGS" + ax_pthread_save_LIBS="$LIBS" + if test "x$PTHREAD_CC" != "x" +then : + CC="$PTHREAD_CC" +fi + if test "x$PTHREAD_CXX" != "x" +then : + CXX="$PTHREAD_CXX" +fi + CFLAGS="$CFLAGS $PTHREAD_CFLAGS" + LIBS="$PTHREAD_LIBS $LIBS" + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for pthread_join using $CC $PTHREAD_CFLAGS $PTHREAD_LIBS" >&5 +printf %s "checking for pthread_join using $CC $PTHREAD_CFLAGS $PTHREAD_LIBS... " >&6; } + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +/* Override any GCC internal prototype to avoid an error. + Use char because int might match the return type of a GCC + builtin and then its argument prototype would still apply. */ +char pthread_join (); +int +main (void) +{ +return pthread_join (); + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO" +then : + ax_pthread_ok=yes +fi +rm -f core conftest.err conftest.$ac_objext conftest.beam \ + conftest$ac_exeext conftest.$ac_ext + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ax_pthread_ok" >&5 +printf "%s\n" "$ax_pthread_ok" >&6; } + if test "x$ax_pthread_ok" = "xno"; then + PTHREAD_LIBS="" + PTHREAD_CFLAGS="" + fi + CC="$ax_pthread_save_CC" + CFLAGS="$ax_pthread_save_CFLAGS" + LIBS="$ax_pthread_save_LIBS" +fi + +# We must check for the threads library under a number of different +# names; the ordering is very important because some systems +# (e.g. DEC) have both -lpthread and -lpthreads, where one of the +# libraries is broken (non-POSIX). + +# Create a list of thread flags to try. Items with a "," contain both +# C compiler flags (before ",") and linker flags (after ","). Other items +# starting with a "-" are C compiler flags, and remaining items are +# library names, except for "none" which indicates that we try without +# any flags at all, and "pthread-config" which is a program returning +# the flags for the Pth emulation library. + +ax_pthread_flags="pthreads none -Kthread -pthread -pthreads -mthreads pthread --thread-safe -mt pthread-config" + +# The ordering *is* (sometimes) important. Some notes on the +# individual items follow: + +# pthreads: AIX (must check this before -lpthread) +# none: in case threads are in libc; should be tried before -Kthread and +# other compiler flags to prevent continual compiler warnings +# -Kthread: Sequent (threads in libc, but -Kthread needed for pthread.h) +# -pthread: Linux/gcc (kernel threads), BSD/gcc (userland threads), Tru64 +# (Note: HP C rejects this with "bad form for `-t' option") +# -pthreads: Solaris/gcc (Note: HP C also rejects) +# -mt: Sun Workshop C (may only link SunOS threads [-lthread], but it +# doesn't hurt to check since this sometimes defines pthreads and +# -D_REENTRANT too), HP C (must be checked before -lpthread, which +# is present but should not be used directly; and before -mthreads, +# because the compiler interprets this as "-mt" + "-hreads") +# -mthreads: Mingw32/gcc, Lynx/gcc +# pthread: Linux, etcetera +# --thread-safe: KAI C++ +# pthread-config: use pthread-config program (for GNU Pth library) + +case $host_os in + + freebsd*) + + # -kthread: FreeBSD kernel threads (preferred to -pthread since SMP-able) + # lthread: LinuxThreads port on FreeBSD (also preferred to -pthread) + + ax_pthread_flags="-kthread lthread $ax_pthread_flags" + ;; + + hpux*) + + # From the cc(1) man page: "[-mt] Sets various -D flags to enable + # multi-threading and also sets -lpthread." + + ax_pthread_flags="-mt -pthread pthread $ax_pthread_flags" + ;; + + openedition*) + + # IBM z/OS requires a feature-test macro to be defined in order to + # enable POSIX threads at all, so give the user a hint if this is + # not set. (We don't define these ourselves, as they can affect + # other portions of the system API in unpredictable ways.) + + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +# if !defined(_OPEN_THREADS) && !defined(_UNIX03_THREADS) + AX_PTHREAD_ZOS_MISSING +# endif + +_ACEOF +if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | + $EGREP "AX_PTHREAD_ZOS_MISSING" >/dev/null 2>&1 +then : + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: IBM z/OS requires -D_OPEN_THREADS or -D_UNIX03_THREADS to enable pthreads support." >&5 +printf "%s\n" "$as_me: WARNING: IBM z/OS requires -D_OPEN_THREADS or -D_UNIX03_THREADS to enable pthreads support." >&2;} +fi +rm -rf conftest* + + ;; + + solaris*) + + # On Solaris (at least, for some versions), libc contains stubbed + # (non-functional) versions of the pthreads routines, so link-based + # tests will erroneously succeed. (N.B.: The stubs are missing + # pthread_cleanup_push, or rather a function called by this macro, + # so we could check for that, but who knows whether they'll stub + # that too in a future libc.) So we'll check first for the + # standard Solaris way of linking pthreads (-mt -lpthread). + + ax_pthread_flags="-mt,-lpthread pthread $ax_pthread_flags" + ;; +esac + +# Are we compiling with Clang? + +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether $CC is Clang" >&5 +printf %s "checking whether $CC is Clang... " >&6; } +if test ${ax_cv_PTHREAD_CLANG+y} +then : + printf %s "(cached) " >&6 +else $as_nop + ax_cv_PTHREAD_CLANG=no + # Note that Autoconf sets GCC=yes for Clang as well as GCC + if test "x$GCC" = "xyes"; then + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +/* Note: Clang 2.7 lacks __clang_[a-z]+__ */ +# if defined(__clang__) && defined(__llvm__) + AX_PTHREAD_CC_IS_CLANG +# endif + +_ACEOF +if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | + $EGREP "AX_PTHREAD_CC_IS_CLANG" >/dev/null 2>&1 +then : + ax_cv_PTHREAD_CLANG=yes +fi +rm -rf conftest* + + fi + +fi +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ax_cv_PTHREAD_CLANG" >&5 +printf "%s\n" "$ax_cv_PTHREAD_CLANG" >&6; } +ax_pthread_clang="$ax_cv_PTHREAD_CLANG" + + +# GCC generally uses -pthread, or -pthreads on some platforms (e.g. SPARC) + +# Note that for GCC and Clang -pthread generally implies -lpthread, +# except when -nostdlib is passed. +# This is problematic using libtool to build C++ shared libraries with pthread: +# [1] https://gcc.gnu.org/bugzilla/show_bug.cgi?id=25460 +# [2] https://bugzilla.redhat.com/show_bug.cgi?id=661333 +# [3] https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=468555 +# To solve this, first try -pthread together with -lpthread for GCC + +if test "x$GCC" = "xyes" +then : + ax_pthread_flags="-pthread,-lpthread -pthread -pthreads $ax_pthread_flags" +fi + +# Clang takes -pthread (never supported any other flag), but we'll try with -lpthread first + +if test "x$ax_pthread_clang" = "xyes" +then : + ax_pthread_flags="-pthread,-lpthread -pthread" +fi + + +# The presence of a feature test macro requesting re-entrant function +# definitions is, on some systems, a strong hint that pthreads support is +# correctly enabled + +case $host_os in + darwin* | hpux* | linux* | osf* | solaris*) + ax_pthread_check_macro="_REENTRANT" + ;; + + aix*) + ax_pthread_check_macro="_THREAD_SAFE" + ;; + + *) + ax_pthread_check_macro="--" + ;; +esac +if test "x$ax_pthread_check_macro" = "x--" +then : + ax_pthread_check_cond=0 +else $as_nop + ax_pthread_check_cond="!defined($ax_pthread_check_macro)" +fi + + +if test "x$ax_pthread_ok" = "xno"; then +for ax_pthread_try_flag in $ax_pthread_flags; do + + case $ax_pthread_try_flag in + none) + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether pthreads work without any flags" >&5 +printf %s "checking whether pthreads work without any flags... " >&6; } + ;; + + *,*) + PTHREAD_CFLAGS=`echo $ax_pthread_try_flag | sed "s/^\(.*\),\(.*\)$/\1/"` + PTHREAD_LIBS=`echo $ax_pthread_try_flag | sed "s/^\(.*\),\(.*\)$/\2/"` + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether pthreads work with \"$PTHREAD_CFLAGS\" and \"$PTHREAD_LIBS\"" >&5 +printf %s "checking whether pthreads work with \"$PTHREAD_CFLAGS\" and \"$PTHREAD_LIBS\"... " >&6; } + ;; + + -*) + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether pthreads work with $ax_pthread_try_flag" >&5 +printf %s "checking whether pthreads work with $ax_pthread_try_flag... " >&6; } + PTHREAD_CFLAGS="$ax_pthread_try_flag" + ;; + + pthread-config) + # Extract the first word of "pthread-config", so it can be a program name with args. +set dummy pthread-config; ac_word=$2 +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +printf %s "checking for $ac_word... " >&6; } +if test ${ac_cv_prog_ax_pthread_config+y} +then : + printf %s "(cached) " >&6 +else $as_nop + if test -n "$ax_pthread_config"; then + ac_cv_prog_ax_pthread_config="$ax_pthread_config" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + case $as_dir in #((( + '') as_dir=./ ;; + */) ;; + *) as_dir=$as_dir/ ;; + esac + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then + ac_cv_prog_ax_pthread_config="yes" + printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + + test -z "$ac_cv_prog_ax_pthread_config" && ac_cv_prog_ax_pthread_config="no" +fi +fi +ax_pthread_config=$ac_cv_prog_ax_pthread_config +if test -n "$ax_pthread_config"; then + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ax_pthread_config" >&5 +printf "%s\n" "$ax_pthread_config" >&6; } +else + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 +printf "%s\n" "no" >&6; } +fi + + + if test "x$ax_pthread_config" = "xno" +then : + continue +fi + PTHREAD_CFLAGS="`pthread-config --cflags`" + PTHREAD_LIBS="`pthread-config --ldflags` `pthread-config --libs`" + ;; + + *) + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for the pthreads library -l$ax_pthread_try_flag" >&5 +printf %s "checking for the pthreads library -l$ax_pthread_try_flag... " >&6; } + PTHREAD_LIBS="-l$ax_pthread_try_flag" + ;; + esac + + ax_pthread_save_CFLAGS="$CFLAGS" + ax_pthread_save_LIBS="$LIBS" + CFLAGS="$CFLAGS $PTHREAD_CFLAGS" + LIBS="$PTHREAD_LIBS $LIBS" + + # Check for various functions. We must include pthread.h, + # since some functions may be macros. (On the Sequent, we + # need a special flag -Kthread to make this header compile.) + # We check for pthread_join because it is in -lpthread on IRIX + # while pthread_create is in libc. We check for pthread_attr_init + # due to DEC craziness with -lpthreads. We check for + # pthread_cleanup_push because it is one of the few pthread + # functions on Solaris that doesn't have a non-functional libc stub. + # We try pthread_create on general principles. + + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include +# if $ax_pthread_check_cond +# error "$ax_pthread_check_macro must be defined" +# endif + static void *some_global = NULL; + static void routine(void *a) + { + /* To avoid any unused-parameter or + unused-but-set-parameter warning. */ + some_global = a; + } + static void *start_routine(void *a) { return a; } +int +main (void) +{ +pthread_t th; pthread_attr_t attr; + pthread_create(&th, 0, start_routine, 0); + pthread_join(th, 0); + pthread_attr_init(&attr); + pthread_cleanup_push(routine, 0); + pthread_cleanup_pop(0) /* ; */ + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO" +then : + ax_pthread_ok=yes +fi +rm -f core conftest.err conftest.$ac_objext conftest.beam \ + conftest$ac_exeext conftest.$ac_ext + + CFLAGS="$ax_pthread_save_CFLAGS" + LIBS="$ax_pthread_save_LIBS" + + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ax_pthread_ok" >&5 +printf "%s\n" "$ax_pthread_ok" >&6; } + if test "x$ax_pthread_ok" = "xyes" +then : + break +fi + + PTHREAD_LIBS="" + PTHREAD_CFLAGS="" +done +fi + + +# Clang needs special handling, because older versions handle the -pthread +# option in a rather... idiosyncratic way + +if test "x$ax_pthread_clang" = "xyes"; then + + # Clang takes -pthread; it has never supported any other flag + + # (Note 1: This will need to be revisited if a system that Clang + # supports has POSIX threads in a separate library. This tends not + # to be the way of modern systems, but it's conceivable.) + + # (Note 2: On some systems, notably Darwin, -pthread is not needed + # to get POSIX threads support; the API is always present and + # active. We could reasonably leave PTHREAD_CFLAGS empty. But + # -pthread does define _REENTRANT, and while the Darwin headers + # ignore this macro, third-party headers might not.) + + # However, older versions of Clang make a point of warning the user + # that, in an invocation where only linking and no compilation is + # taking place, the -pthread option has no effect ("argument unused + # during compilation"). They expect -pthread to be passed in only + # when source code is being compiled. + # + # Problem is, this is at odds with the way Automake and most other + # C build frameworks function, which is that the same flags used in + # compilation (CFLAGS) are also used in linking. Many systems + # supported by AX_PTHREAD require exactly this for POSIX threads + # support, and in fact it is often not straightforward to specify a + # flag that is used only in the compilation phase and not in + # linking. Such a scenario is extremely rare in practice. + # + # Even though use of the -pthread flag in linking would only print + # a warning, this can be a nuisance for well-run software projects + # that build with -Werror. So if the active version of Clang has + # this misfeature, we search for an option to squash it. + + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether Clang needs flag to prevent \"argument unused\" warning when linking with -pthread" >&5 +printf %s "checking whether Clang needs flag to prevent \"argument unused\" warning when linking with -pthread... " >&6; } +if test ${ax_cv_PTHREAD_CLANG_NO_WARN_FLAG+y} +then : + printf %s "(cached) " >&6 +else $as_nop + ax_cv_PTHREAD_CLANG_NO_WARN_FLAG=unknown + # Create an alternate version of $ac_link that compiles and + # links in two steps (.c -> .o, .o -> exe) instead of one + # (.c -> exe), because the warning occurs only in the second + # step + ax_pthread_save_ac_link="$ac_link" + ax_pthread_sed='s/conftest\.\$ac_ext/conftest.$ac_objext/g' + ax_pthread_link_step=`printf "%s\n" "$ac_link" | sed "$ax_pthread_sed"` + ax_pthread_2step_ac_link="($ac_compile) && (echo ==== >&5) && ($ax_pthread_link_step)" + ax_pthread_save_CFLAGS="$CFLAGS" + for ax_pthread_try in '' -Qunused-arguments -Wno-unused-command-line-argument unknown; do + if test "x$ax_pthread_try" = "xunknown" +then : + break +fi + CFLAGS="-Werror -Wunknown-warning-option $ax_pthread_try -pthread $ax_pthread_save_CFLAGS" + ac_link="$ax_pthread_save_ac_link" + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +int main(void){return 0;} +_ACEOF +if ac_fn_c_try_link "$LINENO" +then : + ac_link="$ax_pthread_2step_ac_link" + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +int main(void){return 0;} +_ACEOF +if ac_fn_c_try_link "$LINENO" +then : + break +fi +rm -f core conftest.err conftest.$ac_objext conftest.beam \ + conftest$ac_exeext conftest.$ac_ext + +fi +rm -f core conftest.err conftest.$ac_objext conftest.beam \ + conftest$ac_exeext conftest.$ac_ext + done + ac_link="$ax_pthread_save_ac_link" + CFLAGS="$ax_pthread_save_CFLAGS" + if test "x$ax_pthread_try" = "x" +then : + ax_pthread_try=no +fi + ax_cv_PTHREAD_CLANG_NO_WARN_FLAG="$ax_pthread_try" + +fi +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ax_cv_PTHREAD_CLANG_NO_WARN_FLAG" >&5 +printf "%s\n" "$ax_cv_PTHREAD_CLANG_NO_WARN_FLAG" >&6; } + + case "$ax_cv_PTHREAD_CLANG_NO_WARN_FLAG" in + no | unknown) ;; + *) PTHREAD_CFLAGS="$ax_cv_PTHREAD_CLANG_NO_WARN_FLAG $PTHREAD_CFLAGS" ;; + esac + +fi # $ax_pthread_clang = yes + + + +# Various other checks: +if test "x$ax_pthread_ok" = "xyes"; then + ax_pthread_save_CFLAGS="$CFLAGS" + ax_pthread_save_LIBS="$LIBS" + CFLAGS="$CFLAGS $PTHREAD_CFLAGS" + LIBS="$PTHREAD_LIBS $LIBS" + + # Detect AIX lossage: JOINABLE attribute is called UNDETACHED. + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for joinable pthread attribute" >&5 +printf %s "checking for joinable pthread attribute... " >&6; } +if test ${ax_cv_PTHREAD_JOINABLE_ATTR+y} +then : + printf %s "(cached) " >&6 +else $as_nop + ax_cv_PTHREAD_JOINABLE_ATTR=unknown + for ax_pthread_attr in PTHREAD_CREATE_JOINABLE PTHREAD_CREATE_UNDETACHED; do + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include +int +main (void) +{ +int attr = $ax_pthread_attr; return attr /* ; */ + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO" +then : + ax_cv_PTHREAD_JOINABLE_ATTR=$ax_pthread_attr; break +fi +rm -f core conftest.err conftest.$ac_objext conftest.beam \ + conftest$ac_exeext conftest.$ac_ext + done + +fi +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ax_cv_PTHREAD_JOINABLE_ATTR" >&5 +printf "%s\n" "$ax_cv_PTHREAD_JOINABLE_ATTR" >&6; } + if test "x$ax_cv_PTHREAD_JOINABLE_ATTR" != "xunknown" && \ + test "x$ax_cv_PTHREAD_JOINABLE_ATTR" != "xPTHREAD_CREATE_JOINABLE" && \ + test "x$ax_pthread_joinable_attr_defined" != "xyes" +then : + +printf "%s\n" "#define PTHREAD_CREATE_JOINABLE $ax_cv_PTHREAD_JOINABLE_ATTR" >>confdefs.h + + ax_pthread_joinable_attr_defined=yes + +fi + + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether more special flags are required for pthreads" >&5 +printf %s "checking whether more special flags are required for pthreads... " >&6; } +if test ${ax_cv_PTHREAD_SPECIAL_FLAGS+y} +then : + printf %s "(cached) " >&6 +else $as_nop + ax_cv_PTHREAD_SPECIAL_FLAGS=no + case $host_os in + solaris*) + ax_cv_PTHREAD_SPECIAL_FLAGS="-D_POSIX_PTHREAD_SEMANTICS" + ;; + esac + +fi +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ax_cv_PTHREAD_SPECIAL_FLAGS" >&5 +printf "%s\n" "$ax_cv_PTHREAD_SPECIAL_FLAGS" >&6; } + if test "x$ax_cv_PTHREAD_SPECIAL_FLAGS" != "xno" && \ + test "x$ax_pthread_special_flags_added" != "xyes" +then : + PTHREAD_CFLAGS="$ax_cv_PTHREAD_SPECIAL_FLAGS $PTHREAD_CFLAGS" + ax_pthread_special_flags_added=yes +fi + + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for PTHREAD_PRIO_INHERIT" >&5 +printf %s "checking for PTHREAD_PRIO_INHERIT... " >&6; } +if test ${ax_cv_PTHREAD_PRIO_INHERIT+y} +then : + printf %s "(cached) " >&6 +else $as_nop + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include +int +main (void) +{ +int i = PTHREAD_PRIO_INHERIT; + return i; + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO" +then : + ax_cv_PTHREAD_PRIO_INHERIT=yes +else $as_nop + ax_cv_PTHREAD_PRIO_INHERIT=no +fi +rm -f core conftest.err conftest.$ac_objext conftest.beam \ + conftest$ac_exeext conftest.$ac_ext + +fi +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ax_cv_PTHREAD_PRIO_INHERIT" >&5 +printf "%s\n" "$ax_cv_PTHREAD_PRIO_INHERIT" >&6; } + if test "x$ax_cv_PTHREAD_PRIO_INHERIT" = "xyes" && \ + test "x$ax_pthread_prio_inherit_defined" != "xyes" +then : + +printf "%s\n" "#define HAVE_PTHREAD_PRIO_INHERIT 1" >>confdefs.h + + ax_pthread_prio_inherit_defined=yes + +fi + + CFLAGS="$ax_pthread_save_CFLAGS" + LIBS="$ax_pthread_save_LIBS" + + # More AIX lossage: compile with *_r variant + if test "x$GCC" != "xyes"; then + case $host_os in + aix*) + case "x/$CC" in #( + x*/c89|x*/c89_128|x*/c99|x*/c99_128|x*/cc|x*/cc128|x*/xlc|x*/xlc_v6|x*/xlc128|x*/xlc128_v6) : + #handle absolute path differently from PATH based program lookup + case "x$CC" in #( + x/*) : + + if as_fn_executable_p ${CC}_r +then : + PTHREAD_CC="${CC}_r" +fi + if test "x${CXX}" != "x" +then : + if as_fn_executable_p ${CXX}_r +then : + PTHREAD_CXX="${CXX}_r" +fi +fi + ;; #( + *) : + + for ac_prog in ${CC}_r +do + # Extract the first word of "$ac_prog", so it can be a program name with args. +set dummy $ac_prog; ac_word=$2 +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +printf %s "checking for $ac_word... " >&6; } +if test ${ac_cv_prog_PTHREAD_CC+y} +then : + printf %s "(cached) " >&6 +else $as_nop + if test -n "$PTHREAD_CC"; then + ac_cv_prog_PTHREAD_CC="$PTHREAD_CC" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + case $as_dir in #((( + '') as_dir=./ ;; + */) ;; + *) as_dir=$as_dir/ ;; + esac + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then + ac_cv_prog_PTHREAD_CC="$ac_prog" + printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +PTHREAD_CC=$ac_cv_prog_PTHREAD_CC +if test -n "$PTHREAD_CC"; then + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $PTHREAD_CC" >&5 +printf "%s\n" "$PTHREAD_CC" >&6; } +else + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 +printf "%s\n" "no" >&6; } +fi + + + test -n "$PTHREAD_CC" && break +done +test -n "$PTHREAD_CC" || PTHREAD_CC="$CC" + + if test "x${CXX}" != "x" +then : + for ac_prog in ${CXX}_r +do + # Extract the first word of "$ac_prog", so it can be a program name with args. +set dummy $ac_prog; ac_word=$2 +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +printf %s "checking for $ac_word... " >&6; } +if test ${ac_cv_prog_PTHREAD_CXX+y} +then : + printf %s "(cached) " >&6 +else $as_nop + if test -n "$PTHREAD_CXX"; then + ac_cv_prog_PTHREAD_CXX="$PTHREAD_CXX" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + case $as_dir in #((( + '') as_dir=./ ;; + */) ;; + *) as_dir=$as_dir/ ;; + esac + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then + ac_cv_prog_PTHREAD_CXX="$ac_prog" + printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +PTHREAD_CXX=$ac_cv_prog_PTHREAD_CXX +if test -n "$PTHREAD_CXX"; then + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $PTHREAD_CXX" >&5 +printf "%s\n" "$PTHREAD_CXX" >&6; } +else + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 +printf "%s\n" "no" >&6; } +fi + + + test -n "$PTHREAD_CXX" && break +done +test -n "$PTHREAD_CXX" || PTHREAD_CXX="$CXX" + +fi + + ;; +esac + ;; #( + *) : + ;; +esac + ;; + esac + fi +fi + +test -n "$PTHREAD_CC" || PTHREAD_CC="$CC" +test -n "$PTHREAD_CXX" || PTHREAD_CXX="$CXX" + + + + + + +# Finally, execute ACTION-IF-FOUND/ACTION-IF-NOT-FOUND: +if test "x$ax_pthread_ok" = "xyes"; then + +printf "%s\n" "#define HAVE_PTHREAD 1" >>confdefs.h + +LIBS="$PTHREAD_LIBS $LIBS" +CFLAGS="$CFLAGS $PTHREAD_CFLAGS" +CXXFLAGS="$CXXFLAGS $PTHREAD_CFLAGS" +CC="$PTHREAD_CC" +CXX="$PTHREAD_CXX" + + : +else + ax_pthread_ok=no + +fi +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu + + + +# Atomics +ac_fn_c_check_header_compile "$LINENO" "stdatomic.h" "ac_cv_header_stdatomic_h" "$ac_includes_default" +if test "x$ac_cv_header_stdatomic_h" = xyes +then : + printf "%s\n" "#define HAVE_STDATOMIC_H 1" >>confdefs.h + +fi + + +# Check for poll.h (it's in POSIX so everyone should have it?) +ac_fn_c_check_header_compile "$LINENO" "poll.h" "ac_cv_header_poll_h" "$ac_includes_default" +if test "x$ac_cv_header_poll_h" = xyes +then : + printf "%s\n" "#define HAVE_POLL_H 1" >>confdefs.h + +fi + + +# SCTP. Allow user to disable SCTP support with --without-sctp. +# Otherwise we try to find whatever support is required. +try_sctp=true + +# Check whether --with-sctp was given. +if test ${with_sctp+y} +then : + withval=$with_sctp; + case "$withval" in + y | ye | yes) + ;; + n | no) + try_sctp=false + ;; + *) + as_fn_error $? "Invalid --with-sctp value" "$LINENO" 5 + ;; + esac + +else $as_nop + + try_sctp=true + + +fi + + +ac_fn_c_check_header_compile "$LINENO" "linux/tcp.h" "ac_cv_header_linux_tcp_h" "$ac_includes_default" +if test "x$ac_cv_header_linux_tcp_h" = xyes +then : + printf "%s\n" "#define HAVE_LINUX_TCP_H 1" >>confdefs.h + +fi + + +# Check for SCTP support +if $try_sctp; then +ac_fn_c_check_header_compile "$LINENO" "sys/socket.h" "ac_cv_header_sys_socket_h" "$ac_includes_default" +if test "x$ac_cv_header_sys_socket_h" = xyes +then : + printf "%s\n" "#define HAVE_SYS_SOCKET_H 1" >>confdefs.h + +fi + + for ac_header in netinet/sctp.h do : ac_fn_c_check_header_compile "$LINENO" "netinet/sctp.h" "ac_cv_header_netinet_sctp_h" "#ifdef HAVE_SYS_SOCKET_H #include @@ -14351,7 +15319,7 @@ if test "x$with_openssl" = "xno"; then printf "%s\n" "$as_me: WARNING: Building without OpenSSL; disabling iperf_auth functionality. " >&2;} else # Check for OPENSSL support - havs_ssl=false + have_ssl=false found=false @@ -17350,4 +18318,3 @@ printf "%s\n" "$as_me: WARNING: unrecognized options: $ac_unrecognized_opts" >&2 fi - diff --git a/examples/Makefile.in b/examples/Makefile.in index 93898c0..6e1365b 100644 --- a/examples/Makefile.in +++ b/examples/Makefile.in @@ -92,6 +92,7 @@ noinst_PROGRAMS = mic$(EXEEXT) mis$(EXEEXT) subdir = examples ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 am__aclocal_m4_deps = $(top_srcdir)/config/ax_check_openssl.m4 \ + $(top_srcdir)/config/ax_pthread.m4 \ $(top_srcdir)/config/iperf_config_static_bin.m4 \ $(top_srcdir)/configure.ac am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ @@ -191,6 +192,7 @@ AWK = @AWK@ CC = @CC@ CCDEPMODE = @CCDEPMODE@ CFLAGS = @CFLAGS@ +CPP = @CPP@ CPPFLAGS = @CPPFLAGS@ CSCOPE = @CSCOPE@ CTAGS = @CTAGS@ @@ -245,6 +247,10 @@ PACKAGE_URL = @PACKAGE_URL@ PACKAGE_VERSION = @PACKAGE_VERSION@ PATH_SEPARATOR = @PATH_SEPARATOR@ PKG_CONFIG = @PKG_CONFIG@ +PTHREAD_CC = @PTHREAD_CC@ +PTHREAD_CFLAGS = @PTHREAD_CFLAGS@ +PTHREAD_CXX = @PTHREAD_CXX@ +PTHREAD_LIBS = @PTHREAD_LIBS@ RANLIB = @RANLIB@ SED = @SED@ SET_MAKE = @SET_MAKE@ @@ -263,6 +269,7 @@ am__leading_dot = @am__leading_dot@ am__quote = @am__quote@ am__tar = @am__tar@ am__untar = @am__untar@ +ax_pthread_config = @ax_pthread_config@ bindir = @bindir@ build = @build@ build_alias = @build_alias@ diff --git a/src/Makefile.in b/src/Makefile.in index 91c9b15..e13e4ed 100644 --- a/src/Makefile.in +++ b/src/Makefile.in @@ -103,6 +103,7 @@ TESTS = t_timer$(EXEEXT) t_units$(EXEEXT) t_uuid$(EXEEXT) \ subdir = src ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 am__aclocal_m4_deps = $(top_srcdir)/config/ax_check_openssl.m4 \ + $(top_srcdir)/config/ax_pthread.m4 \ $(top_srcdir)/config/iperf_config_static_bin.m4 \ $(top_srcdir)/configure.ac am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ @@ -517,6 +518,7 @@ AWK = @AWK@ CC = @CC@ CCDEPMODE = @CCDEPMODE@ CFLAGS = @CFLAGS@ +CPP = @CPP@ CPPFLAGS = @CPPFLAGS@ CSCOPE = @CSCOPE@ CTAGS = @CTAGS@ @@ -571,6 +573,10 @@ PACKAGE_URL = @PACKAGE_URL@ PACKAGE_VERSION = @PACKAGE_VERSION@ PATH_SEPARATOR = @PATH_SEPARATOR@ PKG_CONFIG = @PKG_CONFIG@ +PTHREAD_CC = @PTHREAD_CC@ +PTHREAD_CFLAGS = @PTHREAD_CFLAGS@ +PTHREAD_CXX = @PTHREAD_CXX@ +PTHREAD_LIBS = @PTHREAD_LIBS@ RANLIB = @RANLIB@ SED = @SED@ SET_MAKE = @SET_MAKE@ @@ -589,6 +595,7 @@ am__leading_dot = @am__leading_dot@ am__quote = @am__quote@ am__tar = @am__tar@ am__untar = @am__untar@ +ax_pthread_config = @ax_pthread_config@ bindir = @bindir@ build = @build@ build_alias = @build_alias@ diff --git a/src/iperf_config.h.in b/src/iperf_config.h.in index 8831079..bb8853f 100644 --- a/src/iperf_config.h.in +++ b/src/iperf_config.h.in @@ -48,6 +48,12 @@ /* Define to 1 if you have the header file. */ #undef HAVE_POLL_H +/* Define if you have POSIX threads libraries and header files. */ +#undef HAVE_PTHREAD + +/* Have PTHREAD_PRIO_INHERIT. */ +#undef HAVE_PTHREAD_PRIO_INHERIT + /* Define to 1 if you have the `sched_setaffinity' function. */ #undef HAVE_SCHED_SETAFFINITY @@ -69,6 +75,9 @@ /* OpenSSL Is Available */ #undef HAVE_SSL +/* Define to 1 if you have the header file. */ +#undef HAVE_STDATOMIC_H + /* Define to 1 if you have the header file. */ #undef HAVE_STDINT_H @@ -135,6 +144,10 @@ /* Define to the version of this package. */ #undef PACKAGE_VERSION +/* Define to necessary symbol if this constant uses a non-standard name on + your system. */ +#undef PTHREAD_CREATE_JOINABLE + /* Define to 1 if all of the C90 standard headers exist (not just the ones required in a freestanding environment). This macro is provided for backward compatibility; new code need not use it. */ -- cgit v1.2.3