azadkuh / qhttp

a light-weight and asynchronous HTTP library (both server & client) in Qt5 and c++14
MIT License
484 stars 158 forks source link

QObject: Cannot create children for a parent that is in a different thread. #25

Open iamazeem opened 8 years ago

iamazeem commented 8 years ago

I've got this error for all the examples. Even for my own code for starting a simple server issued this warning. Couldn't find any issue here.

Release mode is fine but debug mode wouldn't let you run it. Debugger shows a chain of exceptions generated here.

Assembly by the debugger:

0x77713540                   cc  int3
0x77713541  <+0x0001>        c3  ret
0x77713542  <+0x0002>        90  nop
0x77713543  <+0x0003>        90  nop
0x77713544  <+0x0004>        90  nop
0x77713545  <+0x0005>        90  nop
0x77713546  <+0x0006>        90  nop
0x77713547  <+0x0007>        90  nop

I may be wrong but it's a serious bug. I'd appreciate if you could elaborate on this.

Thanks!

azadkuh commented 8 years ago

@iamAzeem I can't reproduce this error

i'm using g++ 5/6 and clang 3.8/3.9 and Qt 5.6.2 / 5.7 under Ubuntu 16.04.1 server and MacOS 10.11.6.

both Release and Debug builds are normally functional.

iamazeem commented 8 years ago

@azadkuh : Thank you for your response.

Well, here's the complete scenario:

Specs

- Windows 7 (32-bit) - VM
- Qt 5.7 (MinGW)
- Optimization: O3 + Os
- RTTI + exceptions are turned off

I've built QHttp separately, put the .a and .dll files with my project and tried to link it. Now, when I run this from IDE in Debug mode, I get this error on console:

QObject: Cannot create children for a parent that is in a different thread.
(Parent is QCoreApplication(0x22fe9c), parent's thread is QThread(0x2e1ed8), current thread is QThread(0x2eef20)

And, the server doesn't accept any requests (something to do with signals). I'm not using commondir.pri as have been used in the examples.

Another thing that I've noticed is that when I test the server (when it is running under Release), the HTTP request when partial kind-of blocks the server. The server doesn't respond. Is there any timeout for reading requests? I looked at the code and I didn't see any QNetworkAccessManager in it. Wouldn't it be more efficient if the new features are used? Just saying.

You might wanna suggest how to use QHttp as a library - the preferred way.

Thanks a lot!

~ AZEEM ~

azadkuh commented 8 years ago

@iamAzeem I don't have a win machine right now, but will test it if i find some, there is no difference between release / debug mode in this regard. as a general rule of thumb, the QObject instances are not freely movable between threads. see here. you have to use all QObject instances on a single thread or add a message loop to the worker threads (the reason why aQObject on a child thread won't receive any signal or message).

the qhttp server never blocks (at least by design), can you describe more or leak some code to show where you actually face a blocking call?

iamazeem commented 8 years ago

@azadkuh Yes. I also do believe that there should not be any difference in different modes. Regarding your comment about threading, if I want to run the QHttpServer with a GUI, I just have to start the QHttpServer first then run the GUI. Right?

Well, here's the minimal code example that works in Release mode and shows the error in Debug mode. The function runServer() is from one of the examples without change.

Here's the .pro file:

QT += core network
QT -= gui

CONFIG += c++11 c++14

TARGET = QhttpTest-HelloWorld
CONFIG += console
CONFIG -= app_bundle

TEMPLATE = app

SOURCES += main.cpp

win32: LIBS += -L$$PWD/../../../../qhttp/qhttp/xbin/ -lqhttp

INCLUDEPATH += $$PWD/../../../../qhttp/qhttp/src
DEPENDPATH += $$PWD/../../../../qhttp/qhttp/src

Here is the main.cpp file:

#include <QCoreApplication>
#include <QDebug>

#include "qhttpserver.hpp"
#include "qhttpserverresponse.hpp"
#include "qhttpserverrequest.hpp"

#include "qhttpclient.hpp"
#include "qhttpclientrequest.hpp"
#include "qhttpclientresponse.hpp"

void runServer(const QString& portOrPath)
{
    using namespace qhttp::server;

    qDebug() << "Running server...";

    QHttpServer server(qApp);
    // listening tcp port or Unix path
    server.listen(portOrPath, [](QHttpRequest* req, QHttpResponse* res) {
        req->collectData();

        req->onEnd([req, res](){
            res->setStatusCode(qhttp::ESTATUS_OK); // status 200
            res->addHeader("connection", "close"); // optional(default) header

            int size = req->collectedData().size();
            auto message = [size]() -> QByteArray {
                if ( size == 0 )
                    return "Hello World!\n";

                char buffer[65] = {0};
                qsnprintf(buffer, 64, "Hello!\nyou've sent me %d bytes!\n", size);
                return buffer;
            };

            res->end(message());  // reponse body data
        });

        const auto& h = req->headers();
        // optionally let the clients to shut down the server
        if ( h.keyHasValue("command", "quit") ) {
            printf("a client sends a quit command.\nserver quits.\n");
            QCoreApplication::quit();
            return;
        }

        // just for fun! print meta information:
        qDebug("\n--> %s : %s",
                qhttp::Stringify::toString(req->method()),
                qPrintable(req->url().toString().toUtf8())
              );
        qDebug("[Headers (%d)]", h.size());
        h.forEach([](auto iter) {
            qDebug(" %s : %s",
                    iter.key().constData(),
                    iter.value().constData()
                  );
        });
    });

    if ( !server.isListening() ) {
        fprintf(stderr, "failed. can not listen at port %s!\n", qPrintable(portOrPath));
        return;
    }

    qApp->exec(); // application's main event loop
}

int main(int argc, char *argv[])
{
    QCoreApplication a(argc, argv);
    runServer( "8080" );
    return 0;
}

Here's the output of compilation for Debug:

08:35:05: Running steps for project QhttpTest-HelloWorld...
08:35:05: Configuration unchanged, skipping qmake step.
08:35:05: Starting: "C:\Qt\Qt5.7.0\Tools\mingw530_32\bin\mingw32-make.exe" 
C:/Qt/Qt5.7.0/Tools/mingw530_32/bin/mingw32-make -f Makefile.Debug
mingw32-make[1]: Entering directory 'C:/Users/Azeem/Documents/QhttpTest-HelloWorld/build'
g++ -c -pipe -fno-keep-inline-dllexport -g -std=gnu++1y -frtti 
    -Wall -Wextra -fexceptions -mthreads 
    -DUNICODE -DQT_QML_DEBUG -DQT_NETWORK_LIB -DQT_CORE_LIB 
    -I..\..\QhttpTest-HelloWorld -I. -IC:\qhttp\qhttp\src 
    -IC:\Qt\Qt5.7.0\5.7\mingw53_32\include 
    -IC:\Qt\Qt5.7.0\5.7\mingw53_32\include\QtNetwork 
    -IC:\Qt\Qt5.7.0\5.7\mingw53_32\include\QtCore -Idebug
    -IC:\Qt\Qt5.7.0\5.7\mingw53_32\mkspecs\win32-g++  -o debug\main.o ..\main.cpp
g++ -Wl,-subsystem,console -mthreads -o debug\QhttpTest-HelloWorld.exe debug/main.o  
    -LC:\qhttp\qhttp\xbin -lqhttp 
    -LC:\Qt\Qt5.7.0\5.7\mingw53_32\lib 
    C:\Qt\Qt5.7.0\5.7\mingw53_32\lib\libQt5Networkd.a 
    C:\Qt\Qt5.7.0\5.7\mingw53_32\lib\libQt5Cored.a 
mingw32-make[1]: Leaving directory 'C:/Users/Azeem/Documents/QhttpTest-HelloWorld/build'
08:35:07: The process "C:\Qt\Qt5.7.0\Tools\mingw530_32\bin\mingw32-make.exe" exited normally.
08:35:07: Elapsed time: 00:02.

Here's the compiler output for Release:

08:34:12: Running steps for project QhttpTest-HelloWorld...
08:34:12: Configuration unchanged, skipping qmake step.
08:34:12: Starting: "C:\Qt\Qt5.7.0\Tools\mingw530_32\bin\mingw32-make.exe" 
C:/Qt/Qt5.7.0/Tools/mingw530_32/bin/mingw32-make -f Makefile.Release
mingw32-make[1]: Entering directory 'C:/Users/Azeem/Documents/build-QhttpTest-HelloWorld-Desktop_Qt_5_7_0_MinGW_32bit-Release'
g++ -c -pipe -fno-keep-inline-dllexport -O2 -std=gnu++1y -frtti 
    -Wall -Wextra -fexceptions -mthreads 
    -DUNICODE -DQT_NO_DEBUG -DQT_NETWORK_LIB -DQT_CORE_LIB 
    -I..\QhttpTest-HelloWorld -I. 
    -I..\..\..\..\qhttp\qhttp\src 
    -I..\..\..\..\Qt\Qt5.7.0\5.7\mingw53_32\include 
    -I..\..\..\..\Qt\Qt5.7.0\5.7\mingw53_32\include\QtNetwork 
    -I..\..\..\..\Qt\Qt5.7.0\5.7\mingw53_32\include\QtCore 
    -Irelease -I..\..\..\..\Qt\Qt5.7.0\5.7\mingw53_32\mkspecs\win32-g++  
    -o release\main.o ..\QhttpTest-HelloWorld\main.cpp
g++ -Wl,-s -Wl,-subsystem,console -mthreads -o release\QhttpTest-HelloWorld.exe release/main.o  
    -LC:\qhttp\qhttp\xbin -lqhttp 
    -LC:\Qt\Qt5.7.0\5.7\mingw53_32\lib 
    C:\Qt\Qt5.7.0\5.7\mingw53_32\lib\libQt5Network.a 
    C:\Qt\Qt5.7.0\5.7\mingw53_32\lib\libQt5Core.a
mingw32-make[1]: Leaving directory 'C:/Users/Azeem/Documents/build-QhttpTest-HelloWorld-Desktop_Qt_5_7_0_MinGW_32bit-Release'
08:34:14: The process "C:\Qt\Qt5.7.0\Tools\mingw530_32\bin\mingw32-make.exe" exited normally.
08:34:14: Elapsed time: 00:02.

Blocking was not for the whole server actually. It's for the request that is not well-formed. I'm using netcat for testing the server and Chrome's postman extension. So, sending the request like this GET / HTTP/1.1\r\n\r\n would work fine. But, sending only GET\r\n\r\n or GET \r\n \ \r\n HTTP/1.1 \r\n\r\n or any of combination won't return. The server would be serving other requests just fine. What happens to the ill-formed requests? I've got Bad Request also for some requests but not for the ones I mentioned earlier. I'd like you to shed some light on this.

Regarding moving to thread, in fact, I'm not doing anything special about threading here. I was using QHttp in my project and spent a lot of time about this error. Then, I decided to validate it in isolation before embedding it in the project. So, here it is. I looked at other projects but found it quite useful and it may evolve with time and may be the first choice for writing Qt-specific restful services.

BTW, thanks a lot for writing this in the first place! I really appreciate your help and effort in this regard!

~ AZEEM ~

iamazeem commented 8 years ago

@azadkuh I've tested it with GUI and the server is working fine with GUI app. But, I've an issue that server.isListening() is not working or there is some kind of delay here. For an existing bound port, isListening() is supposed to return false but it does not and obviously the server does not run if the port is already occupied. Any suggestion or fix?

ivanvaccari commented 7 years ago

Don't know if is related to this kind of error but i had the same problem in debug mode. Turn out i was using the wrong .dll, was the one produced by the release mode. I switched then to the debug .dll and now it works. Also did a test using the debug dll in release mode and run without problems.