Problem
During the post-test result exchange, the client enters a tight polling loop that repeatedly calls select() with a zero timeout.
This occurs after the main test traffic has finished, in the TEST_END state while waiting for the server’s EXCHANGE_RESULTS response.
On high-latency connections, this busy loop wastes CPU cycles without doing useful work. Similar polling may also occur in the DISPLAY_RESULTS state later.
In this code path, timers are no longer updated, so once the last timer expires, select() is called with timeout = 0 and returns immediately. As a result, the loop spins as fast as possible until data arrives from the server.
Expected behavior: select() should block with a non-zero timeout instead of busy polling.
Version
iperf 3.20+ (cJSON 1.7.15)
Linux alexey 6.8.0-106-generic #106-Ubuntu SMP PREEMPT_DYNAMIC Fri Mar 6 07:58:08 UTC 2026 x86_64
Optional features available: CPU affinity setting, IPv6 flow label, TCP congestion algorithm setting, sendfile / zerocopy, socket pacing, authentication, bind to device, support IPv4 don't fragment, POSIX threads, GSO/GRO support
Steps to reproduce
1. Use a server with high network latency. On Linux, this can be emulated by adding artificial delay (e.g. via tc netem) before starting the iperf server. Something like this:
$ sudo tc qdisc add dev lo root handle 1: prio
$ sudo tc qdisc add dev lo parent 1:3 handle 30: netem delay 1s
$ sudo tc filter add dev lo protocol ip parent 1:0 prio 3 u32 match ip dport 5201 0xffff flowid 1:3
$ sudo tc filter add dev lo protocol ip parent 1:0 prio 3 u32 match ip sport 5201 0xffff flowid 1:3
$ ./iperf3 -s -p 5201
2. Run the iperf with low report interval under strace:
strace -f -tt -T -o trace_log.strace -x ./iperf3 -c 127.0.0.1 -p 5201 -t 1 -i 0.2
3. Review the generated trace (trace_log.strace):
1416665 19:48:51.186114 getsockopt(5, SOL_TCP, TCP_INFO, "\x01\x00\x00\x00\x00\x07\x77\x01\x68\x91\x5b\x00\x00\x00\x00\x00\x00\x80\x00\x00\x18\x02\x00\x00\x02\x00\x00\x00\x00\x00\x00\x00"..., [248]) = 0 <0.000005>
1416665 19:48:51.186131 write(4, "\x04", 1) = 1 <0.000009>
1416665 19:48:51.186149 pselect6(5, [4], [], NULL, {tv_sec=0, tv_nsec=199267000}, NULL) = 0 (Timeout) <0.199481>
1416665 19:48:51.385662 pselect6(5, [4], [], NULL, {tv_sec=0, tv_nsec=0}, NULL) = 0 (Timeout) <0.000005>
1416665 19:48:51.385701 pselect6(5, [4], [], NULL, {tv_sec=0, tv_nsec=0}, NULL) = 0 (Timeout) <0.000004>
1416665 19:48:51.385717 pselect6(5, [4], [], NULL, {tv_sec=0, tv_nsec=0}, NULL) = 0 (Timeout) <0.000004>
Notice that after sending the \x04 state to the server, the client enters a tight select() loop with zero timeout, spinning repeatedly until the server responds.
Problem
During the post-test result exchange, the client enters a tight polling loop that repeatedly calls
select()with a zero timeout.This occurs after the main test traffic has finished, in the
TEST_ENDstate while waiting for the server’sEXCHANGE_RESULTSresponse.On high-latency connections, this busy loop wastes CPU cycles without doing useful work. Similar polling may also occur in the
DISPLAY_RESULTSstate later.In this code path, timers are no longer updated, so once the last timer expires,
select()is called with timeout = 0 and returns immediately. As a result, the loop spins as fast as possible until data arrives from the server.Expected behavior:
select()should block with a non-zero timeout instead of busy polling.Version
Steps to reproduce
1. Use a server with high network latency. On Linux, this can be emulated by adding artificial delay (e.g. via
tc netem) before starting the iperf server. Something like this:2. Run the iperf with low report interval under
strace:3. Review the generated trace (
trace_log.strace):Notice that after sending the
\x04state to the server, the client enters a tightselect()loop with zero timeout, spinning repeatedly until the server responds.