aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBruce A. Mah <bmah@es.net>2023-11-08 14:38:07 -0500
committerGitHub <noreply@github.com>2023-11-08 14:38:07 -0500
commita393df1e1764194044b00ba93ec46e2954eb84a5 (patch)
treea2b57779c8425a9d6b558274ac24ad211b806db6
parentca7c87571dcd7991a263867fc345c0315339027b (diff)
parent291c48e0e03d0065138a862677a1f16203b15fe4 (diff)
downloadiperf3-a393df1e1764194044b00ba93ec46e2954eb84a5.tar.gz
Merge pull request #1591 from esnet/mt
Multithreading support
-rw-r--r--Makefile.in7
-rw-r--r--RELNOTES.md32
-rw-r--r--config/ax_pthread.m4522
-rwxr-xr-xconfigure1198
-rw-r--r--configure.ac19
-rw-r--r--examples/Makefile.in7
-rw-r--r--src/Makefile.in7
-rw-r--r--src/iperf.h45
-rw-r--r--src/iperf3.16
-rw-r--r--src/iperf_api.c132
-rw-r--r--src/iperf_api.h24
-rw-r--r--src/iperf_client_api.c207
-rw-r--r--src/iperf_config.h.in13
-rw-r--r--src/iperf_error.c37
-rw-r--r--src/iperf_server_api.c163
-rw-r--r--src/iperf_util.c10
-rw-r--r--src/main.c20
-rw-r--r--src/net.c5
18 files changed, 2175 insertions, 279 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/RELNOTES.md b/RELNOTES.md
index 48130e9..0a26d5b 100644
--- a/RELNOTES.md
+++ b/RELNOTES.md
@@ -1,6 +1,38 @@
iperf3 Release Notes
====================
+iperf-3.15-mt-beta1 2023-09-29
+------------------------------
+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
---------------------
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 <stevenj@alum.mit.edu>
+# Copyright (c) 2011 Daniel Richard G. <skunk@iSKUNK.ORG>
+# Copyright (c) 2019 Marc Stevens <marc.stevens@cwi.nl>
+#
+# 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 <https://www.gnu.org/licenses/>.
+#
+# 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 <pthread.h>
+# 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 <pthread.h>],
+ [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 <pthread.h>]],
+ [[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 b/configure
index 727ab34..76ab0cc 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 <https://github.com/esnet/iperf>.
#
@@ -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/'
@@ -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.
@@ -1366,7 +1373,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 +1444,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
@@ -1486,6 +1493,7 @@ Some influential environment variables:
you have headers in a nonstandard directory <include dir>
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.
@@ -1555,7 +1563,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.
@@ -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
@@ -1833,7 +1879,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
@@ -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 <stevenj@alum.mit.edu>
+# Copyright (c) 2011 Daniel Richard G. <skunk@iSKUNK.ORG>
+# Copyright (c) 2019 Marc Stevens <marc.stevens@cwi.nl>
+#
+# 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 <https://www.gnu.org/licenses/>.
+#
+# 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}
@@ -3200,7 +3359,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
@@ -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,6 +14182,918 @@ printf "%s\n" "#define const /**/" >>confdefs.h
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
+{ 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 <limits.h>
+ Syntax error
+_ACEOF
+if ac_fn_c_try_cpp "$LINENO"
+then :
+
+else $as_nop
+ # Broken: fails on valid input.
+continue
+fi
+rm -f conftest.err conftest.i conftest.$ac_ext
+
+ # 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 <ac_nonexistent.h>
+_ACEOF
+if ac_fn_c_try_cpp "$LINENO"
+then :
+ # 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
+
+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
+
+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 <limits.h>
+ Syntax error
+_ACEOF
+if ac_fn_c_try_cpp "$LINENO"
+then :
+
+else $as_nop
+ # Broken: fails on valid input.
+continue
+fi
+rm -f conftest.err conftest.i conftest.$ac_ext
+
+ # 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 <ac_nonexistent.h>
+_ACEOF
+if ac_fn_c_try_cpp "$LINENO"
+then :
+ # 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
+
+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 <pthread.h>
+# 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 <pthread.h>
+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 <pthread.h>
+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
@@ -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
@@ -15579,7 +16547,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 +16616,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\\"
diff --git a/configure.ac b/configure.ac
index 1b3c413..b2c19ca 100644
--- a/configure.ac
+++ b/configure.ac
@@ -25,8 +25,9 @@
# 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])
AC_LANG(C)
@@ -48,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
@@ -83,6 +82,18 @@ 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"
+])
+
+# Atomics
+AC_CHECK_HEADERS([stdatomic.h])
+
# Check for poll.h (it's in POSIX so everyone should have it?)
AC_CHECK_HEADERS([poll.h])
@@ -137,7 +148,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 ],
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.h b/src/iperf.h
index c3ce333..7b270bd 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,13 +73,29 @@
#include <openssl/evp.h>
#endif // HAVE_SSL
+#ifdef HAVE_PTHREAD
+#include <pthread.h>
+#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 <stdatomic.h>
+#else
+#warning "No <stdatomic.h> 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
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;
@@ -113,11 +129,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;
@@ -175,6 +191,9 @@ struct iperf_stream
{
struct iperf_test* test;
+ pthread_t thr;
+ int done;
+
/* configurable members */
int local_port;
int remote_port;
@@ -267,6 +286,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;
@@ -352,11 +373,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/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
diff --git a/src/iperf_api.c b/src/iperf_api.c
index ca1ad11..eb23403 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);
}
}
@@ -1891,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];
}
@@ -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;
}
@@ -2807,6 +2795,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 +2805,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 +3073,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 +4810,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 +4926,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 +4953,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 +4990,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 +5004,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_api.h b/src/iperf_api.h
index 0100d38..9e70d44 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,6 +38,16 @@
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 <stdatomic.h>
+#else
+#warning "No <stdatomic.h> available"
+typedef u_int64_t atomic_uint_fast64_t;
+#endif // HAVE_STDATOMIC_H
struct iperf_test;
struct iperf_stream_result;
@@ -46,7 +56,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 */
@@ -310,8 +321,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);
@@ -457,6 +468,11 @@ enum {
IEBINDDEVNOSUPPORT = 146, // `ip%%dev` is not supported as system does not support bind to device
IEHOSTDEV = 147, // host device name (ip%%<dev>) 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)
+ 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 8971ef1..5b33369 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.
@@ -51,6 +51,33 @@
#endif /* TCP_CA_NAME_MAX */
#endif /* HAVE_TCP_CONGESTION */
+void *
+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) {
+ goto cleanup_and_fail;
+ }
+ }
+ else {
+ if (iperf_recv_mt(sp) < 0) {
+ goto cleanup_and_fail;
+ }
+ }
+ }
+ return NULL;
+
+ cleanup_and_fail:
+ return NULL;
+}
+
int
iperf_create_streams(struct iperf_test *test, int sender)
{
@@ -115,12 +142,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;
@@ -519,9 +540,11 @@ 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;
+ int i_errno_save;
if (NULL == test)
{
@@ -562,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));
@@ -577,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;
@@ -589,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;
@@ -620,31 +659,31 @@ iperf_run_client(struct iperf_test * test)
if (startup) {
startup = 0;
- // Set non-blocking for non-UDP tests
- if (test->protocol->id != Pudp) {
- SLIST_FOREACH(sp, &test->streams, streams) {
- setnonblocking(sp->socket, 1);
- }
- }
- }
-
-
- 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)
+ /* Create and spin up threads */
+ pthread_attr_t attr;
+ if (pthread_attr_init(&attr) != 0) {
+ i_errno = IEPTHREADATTRINIT;
goto cleanup_and_fail;
- } else {
- // Reverse mode. Client receives.
- if (iperf_recv(test, &read_set) < 0)
+ }
+
+ SLIST_FOREACH(sp, &test->streams, streams) {
+ if (pthread_create(&(sp->thr), &attr, &iperf_client_worker_run, sp) != 0) {
+ i_errno = IEPTHREADCREATE;
+ goto cleanup_and_fail;
+ }
+ 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");
+ }
+ if (pthread_attr_destroy(&attr) != 0) {
+ i_errno = IEPTHREADATTRDESTROY;
goto cleanup_and_fail;
- }
+ }
+ }
/* Run the timers. */
iperf_time_now(&now);
@@ -665,12 +704,33 @@ iperf_run_client(struct iperf_test * test)
(test->settings->blocks != 0 && (test->blocks_sent >= test->settings->blocks ||
test->blocks_received >= test->settings->blocks)))) {
- // Unset non-blocking for non-UDP tests
- if (test->protocol->id != Pudp) {
- SLIST_FOREACH(sp, &test->streams, streams) {
- setnonblocking(sp->socket, 0);
- }
- }
+ /* Cancel outstanding sender threads */
+ SLIST_FOREACH(sp, &test->streams, streams) {
+ if (sp->sender) {
+ int rc;
+ sp->done = 1;
+ 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;
+ }
+ 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) {
+ iperf_printf(test, "Thread FD %d stopped\n", sp->socket);
+ }
+ }
+ }
+ if (test->debug_level >= DEBUG_LEVEL_INFO) {
+ iperf_printf(test, "Sender threads stopped\n");
+ }
/* Yes, done! Send TEST_END. */
test->done = 1;
@@ -680,15 +740,34 @@ 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 outstanding receiver threads */
+ SLIST_FOREACH(sp, &test->streams, streams) {
+ if (!sp->sender) {
+ int rc;
+ sp->done = 1;
+ 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;
+ }
+ 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) {
+ iperf_printf(test, "Thread FD %d stopped\n", sp->socket);
+ }
+ }
+ }
+ if (test->debug_level >= DEBUG_LEVEL_INFO) {
+ iperf_printf(test, "Receiver threads stopped\n");
}
if (test->json_output) {
@@ -704,6 +783,32 @@ iperf_run_client(struct iperf_test * test)
return 0;
cleanup_and_fail:
+ /* Cancel all outstanding threads */
+ i_errno_save = i_errno;
+ SLIST_FOREACH(sp, &test->streams, streams) {
+ sp->done = 1;
+ int rc;
+ rc = pthread_cancel(sp->thr);
+ if (rc != 0 && rc != ESRCH) {
+ i_errno = IEPTHREADCANCEL;
+ errno = rc;
+ iperf_err(test, "cleanup_and_fail in pthread_cancel - %s", iperf_strerror(i_errno));
+ }
+ rc = pthread_join(sp->thr, NULL);
+ if (rc != 0 && rc != ESRCH) {
+ i_errno = IEPTHREADJOIN;
+ errno = rc;
+ iperf_err(test, "cleanup_and_fail in pthread_join - %s", iperf_strerror(i_errno));
+ }
+ if (test->debug_level >= DEBUG_LEVEL_INFO) {
+ iperf_printf(test, "Thread FD %d stopped\n", sp->socket);
+ }
+ }
+ if (test->debug_level >= DEBUG_LEVEL_INFO) {
+ iperf_printf(test, "All threads stopped\n");
+ }
+ i_errno = i_errno_save;
+
iperf_client_end(test);
if (test->json_output) {
cJSON_AddStringToObject(test->json_top, "error", iperf_strerror(i_errno));
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 <poll.h> 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 <stdatomic.h> header file. */
+#undef HAVE_STDATOMIC_H
+
/* Define to 1 if you have the <stdint.h> 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. */
diff --git a/src/iperf_error.c b/src/iperf_error.c
index ea7955c..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);
@@ -470,6 +487,26 @@ 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;
+ 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 ae916f5..77e9c35 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.
@@ -66,6 +66,33 @@
#endif /* TCP_CA_NAME_MAX */
#endif /* HAVE_TCP_CONGESTION */
+void *
+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) {
+ goto cleanup_and_fail;
+ }
+ }
+ else {
+ if (iperf_recv_mt(sp) < 0) {
+ goto cleanup_and_fail;
+ }
+ }
+ }
+ return NULL;
+
+ cleanup_and_fail:
+ return NULL;
+}
+
int
iperf_server_listen(struct iperf_test *test)
{
@@ -388,6 +415,33 @@ cleanup_server(struct iperf_test *test)
{
struct iperf_stream *sp;
+ /* Cancel outstanding threads */
+ int i_errno_save = i_errno;
+ SLIST_FOREACH(sp, &test->streams, streams) {
+ int rc;
+ sp->done = 1;
+ rc = pthread_cancel(sp->thr);
+ if (rc != 0 && rc != ESRCH) {
+ i_errno = IEPTHREADCANCEL;
+ errno = rc;
+ iperf_err(test, "cleanup_server in pthread_cancel - %s", iperf_strerror(i_errno));
+ }
+ rc = pthread_join(sp->thr, NULL);
+ if (rc != 0 && rc != ESRCH) {
+ i_errno = IEPTHREADJOIN;
+ errno = rc;
+ iperf_err(test, "cleanup_server in pthread_join - %s", iperf_strerror(i_errno));
+ }
+ if (test->debug_level >= DEBUG_LEVEL_INFO) {
+ 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 stopped\n");
+ }
+
/* Close open streams */
SLIST_FOREACH(sp, &test->streams, streams) {
if (sp->socket > -1) {
@@ -452,6 +506,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;
@@ -490,6 +545,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;
@@ -525,6 +581,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;
@@ -538,13 +598,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;
@@ -562,21 +627,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) {
@@ -714,24 +791,8 @@ 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;
- /*
- * 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);
@@ -803,33 +864,33 @@ iperf_run_server(struct iperf_test *test)
cleanup_server(test);
return -1;
}
- }
- }
- if (test->state == TEST_RUNNING) {
- if (test->mode == BIDIRECTIONAL) {
- if (iperf_recv(test, &read_set) < 0) {
+ /* Create and spin up threads */
+ pthread_attr_t attr;
+ if (pthread_attr_init(&attr) != 0) {
+ i_errno = IEPTHREADATTRINIT;
cleanup_server(test);
- return -1;
+ };
+
+ SLIST_FOREACH(sp, &test->streams, streams) {
+ if (pthread_create(&(sp->thr), &attr, &iperf_server_worker_run, sp) != 0) {
+ 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);
+ }
}
- if (iperf_send(test, &write_set) < 0) {
- cleanup_server(test);
- return -1;
+ if (test->debug_level >= DEBUG_LEVEL_INFO) {
+ iperf_printf(test, "All threads created\n");
}
- } 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 (pthread_attr_destroy(&attr) != 0) {
+ i_errno = IEPTHREADATTRDESTROY;
+ cleanup_server(test);
+ };
}
- }
+ }
}
if (result == 0 ||
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);
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
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)