On Sun Jul 5 06:08:28 2026 +0000, Paul Gofman wrote:
I think the missing piece is that `curl` never uses asynchronous or overlapped sends, No, it is not missed. Yet maybe it is just me but I don't actually understand from this description how the suggested checks helps exactly and what curl does: So the problem isn’t that sends happen slightly later than they should. It’s a fixed one-second delay for every \~64 KB burst, which matches the roughly 140 KB/s throughput users report. Under the current Wine behaviour there’s simply no wake-up path that can end that wait early. The discrepancy is in `select()`: on Windows, the pre-check reports the socket as writable whenever a `send()` would accept data, so `curl` never enters the wait in the first place. I don't yet understand how that works on Windows vs Wine exactly. It can't be just pushing data in a tight loop infinitely, it should wait somewhere? Or does it end up on Windows somehow not sleeping on anything at all and just trying send in a tight loop? It is not clear yet to me where is that 1 sec timeoout. If you understand all that maybe it would be easier to write some few lines of [pseudo]code from which it would be clear how it is supposed to wait for send ready without sleeping for 1 sec but yet waiting for send somehow. Fair point. Pseudocode makes it much easier to see what’s going on. This is essentially `curl`’s transfer loop (one multi-loop iteration per burst, simplified):
``` while (data remains) { /* readiness check for this iteration */ if (select(socket, writefds) says writable) { n = send(socket, next_chunk, ~64K); /* non-blocking */ if (n == SOCKET_ERROR && WSAEWOULDBLOCK) { /* buffer genuinely full: FD_WRITE is now armed */ WSAWaitForMultipleEvents(event, timeout=1000ms); /* wakes on FD_WRITE */ } /* else: sent, produce next chunk, loop */ } else { /* select says not writable: wait for the socket to become writable */ WSAWaitForMultipleEvents(event, timeout=1000ms); } } ``` The one-second delay comes from `curl`’s multi-loop poll timeout in `WSAWaitForMultipleEvents()`, using `WSAEventSelect(sock, event, FD_WRITE | ...)`. The key point is that `FD_WRITE` is edge-triggered. As we established earlier in the thread, and as I confirmed on Windows, it is only signalled after a `send()` fails with `WSAEWOULDBLOCK`. That means the wait is only useful if you enter it because a send has just failed, since that’s the only situation where the event is armed. On Windows, `select()` guarantees exactly that. It reports the socket as writable whenever a `send()` would accept data, so `curl` only reaches the wait through the `WSAEWOULDBLOCK` path. At that point, the send buffer is genuinely full, `FD_WRITE` is armed, and the wait is woken as soon as ACKs free enough space. There’s no busy loop or unnecessary sleeping: while the socket can accept data, `curl` simply alternates between `select()` and `send()`, paced by its own data production, and only blocks when the buffer is actually full. On current Wine, things diverge. This sender is application-limited, so its \~64 KB bursts never fill the send buffer. As a result, no `send()` ever returns `WSAEWOULDBLOCK`, which means `FD_WRITE` is never armed. However, `select()` reports the socket as not writable whenever the queued data is above Linux’s `POLLOUT` threshold, which is true for much of the transfer. `curl` therefore takes the `else` branch and waits in a state where the event can never fire. Nothing wakes the wait early, so it always sleeps for the full second, retries, sends one burst, and immediately falls back into the same dead wait. One burst per second is exactly the roughly 140 KB/s throughput users observe. The patch closes that gap. Once `select()` reports the socket as writable whenever the send buffer still has room, matching Windows semantics, `curl` stays on the send path exactly as it does on Windows. It only enters the wait after a `send()` has actually failed with `WSAEWOULDBLOCK`, which is the only time that wait is capable of waking promptly. -- https://gitlab.winehq.org/wine/wine/-/merge_requests/11272#note_145012