PDA

View Full Version : [QTcpSocket] Cannot create children for a parent that is in a different thread



mentalmushroom
2nd November 2011, 11:24
Hello, I am trying to deal with threading affinity. I think I did everything like it was suggested in the "you're doing it wrong" article, but still I receive error messages from the socket when I call waitForConnected method:
"QObject: Cannot create children for a parent that is in a different thread.
(Parent is QTcpSocket(0x2022608), parent's thread is QThread(0x1fc8320), current thread is QThread(0x40fde8)"

Here is my code, I create controller class that is executed in the separate thread.


test::test(QWidget *parent, Qt::WFlags flags)
: QMainWindow(parent, flags)
{
ui.setupUi(this);

controller = new Controller();
controller->moveToThread(&thread);
connect(&thread, SIGNAL(started()), controller, SLOT(start()));
connect(controller, SIGNAL(finished()), &thread, SLOT(quit()));

connect(this, SIGNAL(fileAdded(const QString &)), controller, SLOT(enqueueFile(const QString &)));

emit fileAdded("lalalala");

thread.start();
}


Controller class declaration:


#ifndef CONTROLLER_H
#define CONTROLLER_H

#include <QThread>
#include <QTcpSocket>

class Controller: public QObject
{
Q_OBJECT

public:
Controller(QObject *parent = 0);
~Controller();

public slots:
void enqueueFile(const QString &fileName);
void start();

signals:
void finished();


private:
QTcpSocket socket;
};

#endif // CONTROLLER_H



Controller class implementation:


#include "controller.h"

#include <QDebug>
#include <QTimer>
#include <QEventLoop>

Controller::Controller(QObject *parent)
{

}

Controller::~Controller()
{

}

void Controller::start()
{
socket.connectToHost("some-host", 777);
socket.waitForConnected(); // here goes the error
socket.disconnectFromHost();
socket.waitForDisconnected(); // here goes the error again
}

void Controller::enqueueFile(const QString &fileName)
{
qDebug() << fileName;
}


I know threads are not really needed for sockets, I could use signal/slot mechanism, but this is just for test. Please help me to understand what is wrong.

shorawitz
21st December 2011, 19:50
Did you ever find a solution? I'm in a similar spot. I have a class that polls a serial port for data. Upon receiving data, it passes it to another class to analyze the data. The Analyze class emits a signal if the data requires logging. A third class, Logger, has a slot for the data to log it to a web server. I separated the Logger class so I could possibly use it elsewhere within the program.

Currently, I'm loading the Polling class in main and shuffling it off to the globalInstace of QThreadPool. I load up the other two classes within the Polling class and connect the SIGNAL/SLOT there.

amleto
21st December 2011, 20:05
I think the problem in the OP is that socket is not a child of Controller, and so it has been 'left behind' in the old thread.

I think this would solve the problem


Controller::Controller(QObject *parent)
: socket(this)
{
}

If your compiler supports it, otherwise



Controller::Controller(QObject *parent)
{
socket.setParent(this);
}

should be ok as well.

shorawitz
21st December 2011, 23:32
Thanks amieto!

What's happening now for me, is the SIGNAL from Analyzer appears to be emitted, but the SLOT in Logger never gets run.


#include <QtCore/QCoreApplication>
#include <QThread>
#include "logger.h"
#include "pollcontroller.h"
#include "analyzer.h"

int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);

QString host = "web-dev";
int port = 85;

PollController *pollController = new PollController();
pollController->setup(1,1000);
Analyzer *analyzer = pollController->getAnalyzer();

QThread pollThread;
pollController->moveToThread(&pollThread);

Logger *logger = new Logger();
logger->setup(host,port,analyzer);

QThread loggerThread;
logger->moveToThread(&loggerThread);

return a.exec();
}


#ifndef POLLCONTROLLER_H
#define POLLCONTROLLER_H

#include <QObject>
#include <QThread>
#include "analyzer.h"
#include "sleep.h"

class PollController : public QObject
{
Q_OBJECT
public:
explicit PollController(QObject *parent = 0);
void setup(int,int);
Analyzer*& getAnalyzer();

signals:

public slots:

private:
void loopNumbers(int,int);
Analyzer *analyzer;

};

#endif // POLLCONTROLLER_H


#include "pollcontroller.h"

PollController::PollController(QObject *parent) :
QObject(parent)
{
}

void PollController::setup(int begin, int end) {
analyzer = new Analyzer(this);
loopNumbers(begin,end);
}

void PollController::loopNumbers(int begin, int end) {
Sleep sleep;
QString logMsg = "99.00.050.01.*";
QString skipMsg = "99.00.121.01.03939021";

// Generate some data to check
for (int x=begin; x<end; x++) {
qDebug() << "poll received:" << x;

if (x % 4 == 0) {
analyzer->analyze(logMsg);
} else if (x % 2 == 0) {
analyzer->analyze(skipMsg);
}

sleep.msleep(1000);
}
}

Analyzer*& PollController::getAnalyzer() {
return analyzer;
}


#ifndef ANALYZER_H
#define ANALYZER_H

#include <QObject>
#include <QDebug>

class Analyzer : public QObject
{
Q_OBJECT
public:
explicit Analyzer(QObject *parent = 0);
void analyze(QString&);

signals:
void msgToLog(QString&);

public slots:

};

#endif // ANALYZER_H


#include "analyzer.h"
#include <QStringList>

Analyzer::Analyzer(QObject *parent) :
QObject(parent)
{
}

void Analyzer::analyze(QString &msg)
{
if (msg.contains(".")) {
QStringList fields = msg.split(".");

if (fields.at(2) == "050") {
emit(msgToLog(msg));
qDebug() << "emit(msgToLog(msg))";
return;
}

qDebug() << "msg" << msg << "didn't contain 050 command";
}
}



#ifndef LOGGER_H
#define LOGGER_H

#include <QObject>
#include <QTcpSocket>
#include <QAbstractSocket>
#include <QDebug>
#include "analyzer.h"

class Logger : public QObject
{
Q_OBJECT
public:
explicit Logger(QObject *parent = 0);
void setup(QString&,int&,Analyzer*&);

signals:

public slots:
void connected();
void disconnected();
void bytesWritten(qint64);
void readyRead();
void write(QString &msg);

private:
QTcpSocket *socket;
QString host;
int port;

};

#endif // LOGGER_H



#include "logger.h"

Logger::Logger(QObject *parent) :
QObject(parent)
{
}

void Logger::setup(QString &host, int &port, Analyzer *&analyzer)
{
connect(analyzer,SIGNAL(msgToLog(QString&)),this,SLOT(write(QString&)),Qt::AutoConnection);

socket = new QTcpSocket(this->parent());

connect(socket,SIGNAL(connected()),this,SLOT(conne cted()));
connect(socket,SIGNAL(disconnected()),this,SLOT(di sconnected()));
connect(socket,SIGNAL(readyRead()),this,SLOT(ready Read()));
connect(socket,SIGNAL(bytesWritten(qint64)),this,S LOT(bytesWritten(qint64)));

this->host = host;
this->port = port;
}

void Logger::connected()
{
qDebug() << "Connected";
}

void Logger::disconnected()
{
qDebug() << "Disconnected";
}

void Logger::bytesWritten(qint64)
{
}

void Logger::readyRead()
{
qDebug() << "socket readAll:" << socket->readAll();
}

void Logger::write(QString &msg)
{
qDebug() << "write msg:" << msg;

socket->connectToHost(host,port);

if (!socket->waitForConnected(500)) {
qDebug() << "Error:" << socket->errorString();
return;
}

QByteArray serverMsg;
serverMsg.append("GET /log/test/msg/");
serverMsg.append(msg);
serverMsg.append("/time/1234567859 HTTP/1.0\r\n\r\n");
socket->write(serverMsg);
}



#ifndef SLEEP_H
#define SLEEP_H

#include <QWaitCondition>
#include <QMutex>

class Sleep
{
public:
Sleep();
void msleep(unsigned long msecs);
};

#endif // SLEEP_H



#include "sleep.h"

Sleep::Sleep()
{
}

void Sleep::msleep(unsigned long msecs) {
QMutex mutex;
mutex.lock();

QWaitCondition waitCondition;
waitCondition.wait(&mutex,msecs);

mutex.unlock();
}

shorawitz
22nd December 2011, 18:55
Ok. So I took a different approach. I decided to just fire up a new instance of my Logger class whenever the polling object that is running on its own thread has something to log to the web server. It seems to work in testing, I'm just wondering if I'm doing things correctly, or just getting lucky.

amleto
22nd December 2011, 19:07
eewww, why are you pasing QString by reference for logging calls? Not even const reference! QString should definitely be passed by value.

If you slot doesn't get hit, first thing you should do is debug the connections - does qt give any error message? Then debug what happens after the signal.

mentalmushroom
22nd December 2011, 21:03
Well, I think I found the issue. Passing QString by reference may be something that wasn't intended, but technically it works (slot will be invoked). I think, the problem is you emit signals before establishing connection.


PollController *pollController = new PollController();

// you emit signals here !
pollController->setup(1,1000);

Analyzer *analyzer = pollController->getAnalyzer();



QThread pollThread;

pollController->moveToThread(&pollThread);



Logger *logger = new Logger();
// you establish connection here! doesn't even get called
logger->setup(host,port,analyzer);


However, it seems to not have any relation to my post. G00d luck.