Bug report
test_asyncio.test_sendfile never completes on DragonFly BSD. Every test which transfers the whole DATA takes about 27 seconds, so the test file times out and looks like it hangs:
Timeout (0:05:00)!
File "Lib/selectors.py", line 548 in select
File "Lib/asyncio/base_events.py", line 2034 in _run_once
...
File "Lib/test/test_asyncio/test_sendfile.py", line 134 in run_loop
The transfer does progress, but at exactly 100 ms per 4096 bytes, and DATA is 1114113 bytes, which is 272 round trips.
This is not specific to asyncio. A plain select() loop driving both ends of a socket pair with the same buffer size reproduces it:
|
sends |
time |
median gap |
| DragonFly |
102 of 273 |
10.1 s |
100.0 ms |
| FreeBSD |
273 |
0.01 s |
0.0 ms |
The test reduces the socket buffers to 4 KiB (BUF_SIZE) from the platform default of 57344. With such a small receive buffer DragonFly defers the window update to the delayed ACK timer, so the sender waits 100 ms for every buffer worth of data. TCP_NODELAY on both ends changes nothing, which is expected: this is the receiving side, not Nagle.
The threshold is sharp, and it is close to the loopback MSS — lo0 has an MTU of 16384:
BUF_SIZE |
result |
| 4 KiB |
100 ms per send |
| 16 KiB |
100 ms per send |
| 18432 |
100 ms per send |
| 20480 |
68 sends, 0.00 s |
| 24 KiB … 57344 (default) |
0.00 s |
FreeBSD has the same loopback MTU and does not stall with a 4 KiB buffer, so this is a difference in when DragonFly sends the window update, not in TCP itself.
Using a 32 KiB socket buffer on DragonFly makes the whole file run in 3.1 seconds (66 tests). The buffer is still smaller than the default write buffer limit, so the protocol is still paused long before all data is sent, and the tests which close the peer in the middle of receiving still see only a small part of the data (2.9% and 1.5% of DATA).
Linked PRs
Bug report
test_asyncio.test_sendfilenever completes on DragonFly BSD. Every test which transfers the wholeDATAtakes about 27 seconds, so the test file times out and looks like it hangs:The transfer does progress, but at exactly 100 ms per 4096 bytes, and
DATAis 1114113 bytes, which is 272 round trips.This is not specific to asyncio. A plain
select()loop driving both ends of a socket pair with the same buffer size reproduces it:The test reduces the socket buffers to 4 KiB (
BUF_SIZE) from the platform default of 57344. With such a small receive buffer DragonFly defers the window update to the delayed ACK timer, so the sender waits 100 ms for every buffer worth of data.TCP_NODELAYon both ends changes nothing, which is expected: this is the receiving side, not Nagle.The threshold is sharp, and it is close to the loopback MSS —
lo0has an MTU of 16384:BUF_SIZEFreeBSD has the same loopback MTU and does not stall with a 4 KiB buffer, so this is a difference in when DragonFly sends the window update, not in TCP itself.
Using a 32 KiB socket buffer on DragonFly makes the whole file run in 3.1 seconds (66 tests). The buffer is still smaller than the default write buffer limit, so the protocol is still paused long before all data is sent, and the tests which close the peer in the middle of receiving still see only a small part of the data (2.9% and 1.5% of
DATA).Linked PRs