http://bugs.winehq.org/show_bug.cgi?id=31438
Alessandro Pignotti alexpigna.dev@gmail.com changed:
What |Removed |Added ---------------------------------------------------------------------------- CC| |alexpigna.dev@gmail.com
--- Comment #118 from Alessandro Pignotti alexpigna.dev@gmail.com --- I've spent a few hours analysing the issue. At this point I believe that the issue is not a bug neither a race condition, but just a side-effect of the high overhead caused by round trips to the wineserver.
Let me put down some facts which helps understanding my analysis:
*) Origin uses Qt5Network to handle the HTTP download and this analysis is based on the open source Qt code. *) HTTP download in Qt happens using unbuffered QTcpSockets *) While parsing HTTP headers and status response Qt read one byte at a time *) Notifications from WSAAsyncSelect are explicitly disabled when the first message is received, and enabled again in QAbstractSocket as soon as the first data is read *) By design, WSARecv will issue a new notification message if more data is available on the socket, this happens in wine as specified by the MSDN
Now, this is what happens
1) Qt send an HTTP request, it register a callback when some data is received. In windows this asynchronous operations is supported using WSAAsyncSelect which ask the system to send a window message when some data is available on the socket. 2) Some data eventually arrives, aynchronous notifications are first disabled by Qt, but then enabled again before reading the first bytes. Since headers are read 1 byte at the time QAbstractSocket::readData will be invoked tons of times. And since the socket is unbuffered WSARecv will be invoked tons of times as well. 3) For each invocation of WSARecv there will be more data available (remember we are parsing HTTP headers 1 byte at the time). Since async notifications are enabled WSARecv will cause a new notification to be sent. 4) After the headers are successifully parsed and there is no more data, there will be tons of pending async notification message, one for each call to WSARecv 5) Each message will cause WSAAsyncSelect to be invoked twice: once to disable notifications and once to enabled them again, even if there is no data available since it has been all consumed in the mean time.
In wine each call to WSARecv and WSAAsyncSelect requires a roundtrip to the wineserver, all togheter they add up and cause the connection to slow down to a crawl, since the applications spend most of the time handling messages for the socket even if there is no data to read.
The reason the usleep(5000) workaround works is that it effectively rate limit the number of useless messages which are processed by the applications.
Ok, so how can this be fixed? Technically wine is working as it should, the only problem is that roundtrips to the wineserver which are kind of equivalent to windows system call are fairly heavyweight.
Tecnically also Qt is doing something which is valid, but I find that their approach is actually questionable and inefficient, I believe there are a couple of fixes which could be done at the Qt level.
1) Enable buffering for the TcpSockets (which is the default, actually). The effect of this would be to reduce the number of actual calls to WSARecv which in turn will reduce the amount of redundant messages being sent to the application. 2) Completely disable async read notificatons while parsing HTTP responses and enable them again after the last available data is used.
By the way, I believe that on native windows Qt seems to be working only because windows is faster in handling system calls and sending messages to the application. I think that overall Qt's approach is causing some slow down and inefficieny on windows as well, but I have not verified this directly so I'm speculating.
Sorry for the long post, I hope this helps in fixing the issue once and for all.