Skip to content

server: Always prefer synchronous I/O in nonblocking mode. (#53486)

foobar2000.exe's UPnP Media Renderer component (foo_out_upnp.dll) expects that, if a select() call completes successfully with a non-empty writefds set, any immediately following send() call on a socket in the writefds set never fails with WSAEWOULDBLOCK.

On Wine, the Winsock select() and send() implementations both call the Unix poll(2) under the hood to test if I/O is possible on the socket. As it turns out, it's entirely possible that Linux poll() may yield POLLOUT on the first call (by select) but not the second (by send), even if no send() call has been made in the meanwhile.

On Linux (as of v5.19), a connected (ESTABLISHED) TCP socket that has not been shut down indicates (E)POLLOUT only if the ratio of sk_wmem_queued (the amount of bytes queued in the send buffer) to sk_sndbuf (the size of send buffer size itself, which can be retrieved via SO_SNDBUF) is below a certain threshold. Therefore, a falling edge in POLLOUT can be triggered due to a number of reasons:

  1. TCP fragmentation. Once a TCP packet is split out from a larger sk_buff, it incurs extra bookkeeping overhead (e.g. sk_buff header) that is counted in sk_wmem_queued alongside application data. See also: tcp_fragment(), tso_fragment() (Linux 5.19).

  2. Control packets (e.g. MTU probing). Such packets share the same buffer with application-initiated packets, and thus counted in sk_wmem_queued. See also: sk_wmem_queued_add() callers (Linux 5.19).

  3. Memory pressure. This causes sk_sndbuf to shrink. See also: sk_stream_moderate_sndbuf() callers (Linux 5.19).

Fix this by always attempting synchronous I/O first if req->force_async is unset and the nonblocking flag is set.

Note: for diagnosis, getsockopt(fd, SOL_SOCKET, SO_MEMINFO, ...) can be used to retrieve both sk_wmem_queued (the amount of bytes queued in the send buffer) and sk_sndbuf (the size of the send buffer itself, which can also be retrieved via SO_SNDBUF).

Edited by Jinoh Kang

Merge request reports

Loading