Details
-
Bug
-
Resolution: Done
-
P1: Critical
-
4.7.4
-
None
-
very simple qml with just a large number of images with sources fetched by http requests;
no multi-threading involved;
(described simple example uses a local python web server / web2py setup, but specific server is irrelevant)
Linux 2.6.27.45-0.1-default #1 x86_64
> gcc --version
gcc (SUSE Linux) 4.3.2 [gcc-4_3-branch revision 141291]
> qmake --version
QMake version 2.01a
Using Qt version 4.7.4very simple qml with just a large number of images with sources fetched by http requests; no multi-threading involved; (described simple example uses a local python web server / web2py setup, but specific server is irrelevant) Linux 2.6.27.45-0.1-default #1 x86_64 > gcc --version gcc (SUSE Linux) 4.3.2 [gcc-4_3-branch revision 141291] > qmake --version QMake version 2.01a Using Qt version 4.7.4
-
4.8: ec6d7694f72498d1b156bb0ae8d305e01931f7b2 , 5.0: 20f80d06f7027e8cefe288f60953109c3e333e81
Description
It seems that commit 145fecc833926c307819752912e6833169799a53 introduced a serious problem that was quite challenging to debug..., but it boils down to the fact that a request can be written to the socket but not flushed yet, which then can be "overwritten by/lost due to" the second of a series of pipelined requests, resulting in all of these/remaining requests receiving the 'wrong' reply data, i.e. the data that belongs to the request just after it; this gets triggered only when the number of parallel channels all are handling a request and additional requests come in and therefore are pipelined, which probably explains why it seems to have gone unnoticed for quite some time; the attached very simple example qml shows the problem by loading a set of images.
Commenting in the socket->flush() call (see attached patch
@@ -221,7 +221,7 @@ bool QHttpNetworkConnectionChannel::sendRequest() #endif socket->write(header); // flushing is dangerous (QSslSocket calls transmit which might read or error) -// socket->flush(); + socket->flush(); QNonContiguousByteDevice* uploadByteDevice = request.uploadByteDevice(); if (uploadByteDevice) { // connect the signals so this function gets called again
) makes the problem disappear in our setup, but obviously given the comment in the code, this most likely is not the optimum solution.
Details of our reproducible example:
The problem is already on the level of sent-out requests, so it should not matter what http server I used, but as the easiest reproducible example which I could describe fully step-by-step I used web2py and its built-in python web server. I did the following:
- I downloaded fresh web2py from http://www.web2py.com/examples/static/nightly/web2py_src.zip and unzipped it;
- I put 10 images parcel01.jpg...parcel10.jpg into web2py/applications/welcome/static/. The images contain (on the image) numbers 01...10 matching file names.
- I started "python web2py.py" and clicked on "start server"
- I created qml which uses above images (see attachment):
- there are just four rows of 10 images; you would expect to get what's on the attached screenshot 110909-missing_requests-correct.png
- to make sure that separate requests are made, I've added to each source unique parameter "?xyy" (which is ignored by the server; x is row number, yy image number; thus parcel07.jpg in the third row has source "http://127.0.0.1:8000/welcome/static/parcel07.jpg?307")
But in fact if you run this qml (e.g. using qmlviewer from Qt) instead of 110909-missing_requests-correct.png you will get 110908-missing_requests-QT_BUG.png => four places (2nd row: 4th and 9th img; 3rd row 5th img; and 4th row 1st img) show wrong images .
Now, after adding a bit of debug to qt, I can say the following:
The requests for different images are handled by a number of QHttpNetworkConnectionChannel-s. Most of them never use pipeline (alreadyPipelinedRequests list is empty, socket->write() is called only from sendRequest() and not from pipelineInto()). In my case, pipelining is used for exactly one channel. And these are the
images which are fetched in a wrong way. If I debug the places just before socket->write() I get for this channel the following (this + _FUNCTION_ + request.url()):
QHttpNetworkConnectionChannel(0x6e1e38) sendRequest http://127.0.0.1:8000/welcome/static/parcel01.jpg?101 QHttpNetworkConnectionChannel(0x6e1e38) sendRequest http://127.0.0.1:8000/welcome/static/parcel01.jpg?201 QHttpNetworkConnectionChannel(0x6e1e38) pipelineInto http://127.0.0.1:8000/welcome/static/parcel04.jpg?204 QHttpNetworkConnectionChannel(0x6e1e38) pipelineInto http://127.0.0.1:8000/welcome/static/parcel09.jpg?209 QHttpNetworkConnectionChannel(0x6e1e38) pipelineInto http://127.0.0.1:8000/welcome/static/parcel05.jpg?305 QHttpNetworkConnectionChannel(0x6e1e38) pipelineInto http://127.0.0.1:8000/welcome/static/parcel01.jpg?401 QHttpNetworkConnectionChannel(0x6e1e38) pipelineInto http://127.0.0.1:8000/welcome/static/parcel07.jpg?407 ...long delay... QHttpNetworkConnectionChannel(0x6e1e38) sendRequest http://127.0.0.1:8000/welcome/static/parcel07.jpg?407
Now, as you can see on the attached snapshot (110908-missing_requests-QT_BUG.png):
image with source parcel01.jpg?101 is correct image with source parcel01.jpg?201 is correct image with source parcel04.jpg?204 shows 209 (it's the 4th image in the 2nd row, but it shows "09") image with source parcel09.jpg?209 shows 305 image with source parcel05.jpg?305 shows 401 image with source parcel01.jpg?401 shows 407 image with source parcel07.jpg?407 shows nothing; only after a long delay the request is repeated (see debug above) and then: image with source parcel07.jpg?407 is correct (from the later, separate sendRequest)
Now, I did the same with a tcp_dump watching the outgoing requests: it turns out that even though debug of socket->write() shows that GET request for "parcel04.jpg?204" was written to the socket, tcp_dump cannot see any such request! So the situation is that Qt writes to the socket requests for 101, 201, 204, 209, 305, 401, 407 and in the case of piped requests expects replies in the same order; BUT in fact sent out requests are: 101, 201, 209, 305, 401, 407 (note that 204 is missing). When replies come back, for the third request server (correctly) replies with image 09, but Qt thinks that the third request was "204" => so the 4th image in the 2nd row shows an image "09" which in fact comes from the next request (209)! And so on:
- image with source "209" shows image from request "305"
- image with source "305" shows image from request "401"
- image with source "401" shows image from request "407"
- image with source "407" never gets any image based on the original requests, and only after timeout when the request gets resend it gets correct image.
This leads to the situation shown on the screenshot 110908-missing_requests-QT_BUG.png. After applying the attached patch, we get the correct/expected screenshot 110909-missing_requests-correct.png.