4
votes

I have a web server in Qt that will read a very large ( ~1Gb ) file and return the data to the requestor through a QTcpSocket. This socket is created by the main server thread. I want to use QtConcurrent to hand off this socket to a worker thread and send the data back there.

// Using QtConcurrent
BackgroundConcurrent childThreadToReturnLotsOfData;
QFuture<void> futureObject = QtConcurrent::run(&childThreadToReturnLotsOfData, &BackgroundConcurrent::returnPartialLargeFile, resp , &fileToCheckExistence);

My 'returnPartialLargeFile' function looks like:

void BackgroundConcurrent::returnPartialLargeFile( QHttpResponse *resp , QFile *fileToCheckExistence  ) const
{

    // We need an event loop for the QTCPSocket slots
    QEventLoop loop;
    //QObject::connect(reply, SIGNAL(finished()), &loop, SLOT(quit()));
    // Execute our event loop
    //loop.exec();

    // To do this in another thread from the one created, you must
    // move that socket to this thread, reparent and move
    resp->m_connection->m_socket->setParent( 0 );
    resp->m_connection->m_socket->moveToThread( QThread::currentThread() );

    // Read in chunks until we have sent all data back to the requestor
    QByteArray dataToWriteToSocket; // Store the data to send back
    while ( dataReadFromFileInBytes > 0 ) {

        // Read some Data into the byte array

        // Write each chunk to the socket
        resp->write(dataToWriteToSocket); // <----- Here is our data from the content of the file
        resp->flushData(); // <----- Flush data to the socket

    }

    // End our response and close the connection
    resp->end();
    return;

}

The error I get , is that if I leave the 'loop.exec()' line commented out, I get the following error:

ASSERT failure in QCoreApplication::sendEvent: "Cannot send events to objects owned by a different thread. Current thread c2630. Receiver '' (of type 'QTcpServer') was created in thread 910a8", file kernel/qcoreapplication.cpp, line 501

If I uncomment it, then my function here short circuits at the exec() line and never writes and data to the socket, but I do not get any errors, I just get a truncated response that does not include any data from the while loop.

I am reparenting the socket and moving it to the new thread, so I hope my issues are only with the event loop and the socket signals and slots. Any ideas on what I am doing wrong here? How can I get this to work? If a signals/slots issue, which do I need to connect here? Thanks -

2
So what you do is to write to the response in the new thread in the while loop. In this case, you don't need moveToThread. It is fine that new thread will operate on the object owned by the main thread. As long as there is no racing. If both of your threads are operating on the socket, then you need a mutex. Even if you move the socket to the new thread, you need a mutex to prevent data racing, if racing exists.Min Lin
Thank you that worked - can you please put your answer below and I will award you the bounty. I appreciate it!PhilBot

2 Answers

1
votes

So what you do is to write to the response in the new thread in the while loop.

In this case, you don't need moveToThread.

It is fine that new thread will operate on the object owned by the main thread. As long as there is no racing.

If both of your threads are operating on the socket, then you need a mutex. Even if you move the socket to the new thread, you need a mutex to prevent data racing, if racing exists.

There are several very good documentation from Qt about threading.

Read this for you to understand when moveToThread is needed, and this for how to do thread synchronization. And all these are worth reading if you would like to know more about threading in Qt.

1
votes

If you just remove moveToThread as was suggested, this code will die in case the socket is disconnected too early by the other end, and the write fails. Because write will close which will delete the socket notifier, and you'll hit the assert "Cannot send events to objects owned by a different thread". I know, because I just hit it...

What you should do instead (which I do in https://github.com/KDAB/KDSoap/blob/master/src/KDSoapServer/KDSoapServerThread.cpp) is pass the socket descriptor (int) to the secondary thread, which will create the socket itself. Then all the socket handling is done in the secondary thread, connecting, reading/writing, disconnecting. And Qt will be happy, no thread violations.