What I am trying to achieve is a cross-platform TCP socket library built on top of Qt QTcpServer/Socket. I faced an issue that signals, emitted from a non-Qt thread without Qt event loop, are not received by objects in QThread with the event loop.
I have found that emitting from a non-Qt thread worked before with Qt::QueuedConnection connection type set explicitly, according to this and this questions. Those questions are rather old and relate to Qt 4. So I wonder if this functionality is still supported in Qt 5.
I have explored the Qt 5 source code and found:
- Emitting a signal is just a call to QMetaObject::activate
- QMetaObject::activate, in turn, calls queued_activate, if connection type is set to Qt::QueuedConnection or the current thread (emitter thread) is different from the thread receiver lives in (in my case, Qt::QueuedConnection is set explicitly).
- queued_activate creates an event object and calls QCoreApplication::postEvent
- QCoreApplication::postEvent does proper locking and puts the event into the receiver event queue. Despite postEvent is a static QCoreApplication function that uses self - a pointer to current static QCoreApplication singleton, it should work properly even if there is no global QCoreApplication object (i.e. self == 0).
Given this, I suppose that for signal&slot mechanism to work properly, only the receiver thread has to have the Qt event loop that will dispatch the event from the queue, correct me if I am wrong.
Despite that, emitting a signal from a non-Qt thread does not work for me. I have created as simple demo app as possible that demonstrates the malfunctioning of the signal&slot.
MyThread component just inherits QThread and moves inside itself (moveToThread) QObject-derived ThreadWorker.
MyThread.h:
#ifndef MYTHREAD_H
#define MYTHREAD_H
#include <QThread>
#include "ThreadWorker.h"
class MyThread : public QThread
{
Q_OBJECT
public:
MyThread();
signals:
void mySignal();
private:
ThreadWorker m_worker;
};
#endif // MYTHREAD_H
MyThread.cpp:
#include "MyThread.h"
#include "ThreadWorker.h"
MyThread::MyThread()
: m_worker(*this)
{
m_worker.moveToThread(this);
}
Thread worker is needed to live in MyThread thread and to connect to MyThread`s mySignal() signal.
ThreadWorker.h:
#ifndef THREADWORKER_H
#define THREADWORKER_H
#include <QObject>
class MyThread;
class ThreadWorker : public QObject
{
Q_OBJECT
public:
explicit ThreadWorker(const MyThread& thread);
public slots:
void mySlot();
};
#endif // THREADWORKER_H
ThreadWorker.cpp:
#include "ThreadWorker.h"
#include <QDebug>
#include "MyThread.h"
ThreadWorker::ThreadWorker(const MyThread& thread)
: QObject(0)
{
connect(&thread, SIGNAL(mySignal()),
this, SLOT(mySlot()),
Qt::QueuedConnection);
}
void ThreadWorker::mySlot()
{
qDebug() << "mySlot called! It works!";
}
Finally, the main.cpp:
#include <QCoreApplication>
#include <QDebug>
#include "MyThread.h"
int main(int argc, char *argv[])
{
// QCoreApplication a(argc, argv);
MyThread my_thread;
my_thread.start();
emit my_thread.mySignal();
qDebug() << "mySignal emitted";
my_thread.wait();
// return a.exec();
}
Note, that if I uncomment QCoreApplication creation, I get the correct output:
mySignal emitted
mySlot called! It works!
but if I leave it as is, I get only
mySignal emitted
QEventLoop: Cannot be used without QApplication
So, what is the reason signal&slot mechanism does not work in this case? How to make it working?