Is there a guarantee that no signals are delivered from a different thread after QObject::disconnect()? - qt

I am not thinking of the problem that queued signals are still delivered in the receiving thread after disconnect(), instead:
Consider the case that a Sender object is generating signals in thread 1 and there is a Receiver object in thread 2 which has a slot that is connected to Sender's signal via a Qt::DirectConnection.
Now, in Receiver::~Receiver(), I need to make sure that no signals are still delivered while the object is already (maybe partially) destructed. Because the connection is direct, the slot could be invoked in thread 1 at any time and could in particular happen between destruction of Receiver's specific attributes and destruction of the base QObject which will also disconnect signals. So, my question boils down to:
Is it enough to disconnect Sender and Receiver objects in thread 2 before destruction of the Receiver object, or do I need to make sure that no signals are emitted in thread 1 during the disconnect() call?
I am thinking of the case where thread 1 is in the middle of emitting the signal, e.g. right at the top of executing the receiving slot and right at that moment, in thread 2, the disconnect() call is done. If disconnect() waits (via a mutex) for thread 1 to finish delivering the signal before doing the disconnect and blocking further signal deliveries, everything would be fine but I am not sure that's the case.

Yes disconnect() and connect() are using mutexes for protection.
These are the first lines of the disconnect() function:
bool QMetaObjectPrivate::disconnect(const QObject *sender, int signal_index,
const QObject *receiver, int method_index,
DisconnectType disconnectType)
{
if (!sender)
return false;
QObject *s = const_cast<QObject *>(sender);
QMutex *senderMutex = signalSlotLock(sender);
QMutex *receiverMutex = receiver ? signalSlotLock(receiver) : 0;
QOrderedMutexLocker locker(senderMutex, receiverMutex);
And here are the first lines of the connect() function:
bool QMetaObjectPrivate::connect(const QObject *sender, int signal_index,
const QObject *receiver, int method_index, int type, int *types)
{
QObject *s = const_cast<QObject *>(sender);
QObject *r = const_cast<QObject *>(receiver);
QOrderedMutexLocker locker(signalSlotLock(sender),
signalSlotLock(receiver));
If you check the QObject documentation you can see that :
Note: All functions in this class are reentrant, but connect(),
connect(), disconnect(), and disconnect() are also thread-safe.
EDIT
When a signal is emitted the QMetaObject::activate function is called which locks the sender's object mutex:
void QMetaObject::activate(QObject *sender, const QMetaObject *m, int local_signal_index,
void **argv)
{
...
QMutexLocker locker(signalSlotLock(sender));

Related

sending signal with a size_t variable from a thread is not received in the other thread's slot

I have these 2 signals :
void dataSampled(size_t a);
void error(const QString& message);
Elsewhere :
m_acquisitionThread = new QThread(this);
m_acquisitionManager = new AcquisitionManager();
QObject::connect(m_acquisitionManager, &AcquisitionManager::dataSampled,
this, &Application::onDataSampled); // this Application pointer
QObject::connect(m_acquisitionManager, &AcquisitionManager::error,
this, &Application::showError); // this Application pointer
m_acquisitionManager->moveToThread(m_acquisitionThread);
m_acquisitionThread->start();
AcquisitionManager is an object moved to a thread, Application lives in the "main" thread.
When I send signals to Application, the slot connected to dataSampled which requires a size_t is not executed, changing size_t by an int (only the signal, the slot can remain size_t) or even removing it fixes the issue. This is really strange, has anyone an idea why the signal is not sent ? In an another application (but single threaded), I tested that size_t are sent from a signal to a slot without a problem (but again the context is different).
void AcquisitionManager::executeDataAcquisition()
{
emit dataSampled(666); // onDataSampled is never executed (only if I change signal type from size_t to an int or something else)
emit error("foobar"); // Application::showError is always executed !
registering size_t fixed the issue :
qRegisterMetaType<size_t>("size_t");
But, isn't size_t a primitive type like int ?

Qt: How to avoid deadlock when multiple queued signals invoke same slot

In following code I meet deadlock in someOperation:
class A : public QObject {
Q_OBJECT
public:
explicit A(QObject* parent) : QObject(parent), data(0) {}
public slots:
void slot1() {
someOperation();
}
void slot2() {
someOperation();
}
void slot3() {
someOperation();
}
private:
void someOperation() {
QMutexLocker lk(&mutex);
data++;
QMessageBox::warning(NULL, "warning", "warning");
data--;
assert(data == 0);
}
int data;
QMutex mutex; //protect data
};
class Worker: public QThread {
Q_OBJECT
public:
explicit Worker(QObject* parent) : QThread(parent) {}
protected:
virtual void run() {
// some complicated data processing
emit signal1();
// other complicated data processing
emit signal2();
// much complicated data processing
emit signal3();
qDebug() << "end run";
}
signals:
void signal1();
void signal2();
void signal3();
};
int main(int argc, char *argv[])
{
QApplication app(argc, argv);
A* a = new A(&app);
Worker* w = new Worker(a);
QObject::connect(w, SIGNAL(signal1()), a, SLOT(slot1()), Qt::QueuedConnection);
QObject::connect(w, SIGNAL(signal2()), a, SLOT(slot2()), Qt::QueuedConnection);
QObject::connect(w, SIGNAL(signal3()), a, SLOT(slot3()), Qt::QueuedConnection);
w->start();
return app.exec();
}
There is a thread that will emit three signals, all of them queued connected to an instance of class A, and all class A' slots will call to someOperation, and someOperation is protected by mutex and it will popup a message box.
Qt::QueuedConnection 2 The slot is invoked when control returns to the event loop of the receiver's thread. The slot is executed in the receiver's thread.
It seems slot2 is invoked when slot1's message box still doing modal, in main thread, but at that time slot1 has lock mutex, so deadlock.
How to change the code to avoid deadlock?
Update:(Jan.17, 2019)
What I want archive is that: slot2 not be execute before slot1 finished.
What should be kept are:
worker is a background thread to process data, cost long time; so, whatever, the three signals will emit from other thread.
worker should not blocked by emitting signals.
slots should execute in main thread, because they will update GUI.
someOperation is not reentrant.
The requirement that "someOperation is not reentrant" is an odd one. What should happen if reentrancy is attempted? Given that someOperation can only be called from the main thread I can only see two options...
Block completely with mutex/barrier etc. as you have tried.
Block based on a recursion level counter and spin the event loop until that counter decrements to zero.
1) Will block the thread's event loop completely preventing the current message dialog from functioning correctly.
2) Will allow all message dialogs simultaneously rather then serialising them.
Rather than trying to make someOperation non-reentrant I think you need to make sure you use in a way that won't result in reentrancy.
One option might be to make use of a separate QObject derived class instance on its own QThread. Consider the following...
class signal_serialiser: public QObject {
Q_OBJECT;
signals:
void signal1();
void signal2();
void signal3();
};
If an instance of signal_serialiser is moved to its own thread it can act as a queue to buffer and forward the various signals if suitable connection types are used. In your code you currently have...
QObject::connect(w, SIGNAL(signal1()), a, SLOT(slot1()), Qt::QueuedConnection);
QObject::connect(w, SIGNAL(signal2()), a, SLOT(slot2()), Qt::QueuedConnection);
QObject::connect(w, SIGNAL(signal3()), a, SLOT(slot3()), Qt::QueuedConnection);
Change that to...
signal_serialiser signal_serialiser;
QObject::connect(w, SIGNAL(signal1()), &signal_serialiser, SIGNAL(signal1()));
QObject::connect(w, SIGNAL(signal2()), &signal_serialiser, SIGNAL(signal2()));
QObject::connect(w, SIGNAL(signal3()), &signal_serialiser, SIGNAL(signal3()));
/*
* Note the use of Qt::BlockingQueuedConnection for the
* signal_serialiser --> A connections.
*/
QObject::connect(&signal_serialiser, SIGNAL(signal1()), a, SLOT(slot1()), Qt::BlockingQueuedConnection);
QObject::connect(&signal_serialiser, SIGNAL(signal2()), a, SLOT(slot2()), Qt::BlockingQueuedConnection);
QObject::connect(&signal_serialiser, SIGNAL(signal3()), a, SLOT(slot3()), Qt::BlockingQueuedConnection);
QThread signal_serialiser_thread;
signal_serialiser.moveToThread(&signal_serialiser_thread);
signal_serialiser_thread.start();
I've only done basic testing but it appears to give the desired behaviour.
That's because your function void someOperation() is not reentrant.
The static functions of QMessageBox span their own event loop, which calls QCoreApplication::processEvents() repeatedly:
Execution of the first invocation of someOperation() gets stuck at QMessageBox::warning(...).
In there, exec() calls processEvents(), 3. which sees the second signal
and invokes someOperation() again
where trying to re-lock mutex fails.
How to resolve this depends on what you want to achieve...
About your general approach to QThread: You're doing it wrong.
(That link gives a good start into the topic, but not a complete solution.)
You create and start a background thread. But that thread will only emit the three signals and then finish.
The slots will be called inside the main (GUI) event loop, because that's the thread affinity of your A *a.
To make the slots be executed in the background, you need to:
create your A instance without a parent: A *a = new A();
create your Worker instance with the app as parent: Worker *w = new Worker(&app); (or with nothing, at least not with a)
change the thread affinity of your A instance: a->moveToThread(Worker);
don't override Worker::run(), or if you really want to (see point 5), call the base implementation: QThread::run();
emit the signals from main (you can emit them from run(), but that's not necessary).

Emitting signal from callback

I am using RtMidi library to handle midi message in my Qt application and I am facing problem with slot trigger:
My PhMidiInput object is emiting signal from the RtMidi callback upon specific midi message but the slots are not always triggered.
Here is a part of the PhMidiInput class:
class PhMidiInput : QObject
{
Q_OBJECT
public:
void PhMidiInput() {}
signals:
void quarterFrame(unsigned char data);
private:
static void callback(double, std::vector< unsigned char > *message, void *userData ) {
PhMidiInput *midiInput = (PhMidiInput*)userData;
if(midiInput)
midiInput->onMessage(message);
}
void onMessage(std::vector<unsigned char> *message) {
...
emit quarterFrame(data);
...
}
}
Connecting to a lambda functor works:
PhMidiInput midiIn;
int quarterFrameCount;
connect(&midiIn, &PhMidiInput::quarterFrame, [&](unsigned char data) {
quarterFrameCount++;
});
Connecting to my application window works to:
// MyWindow inherits from QMainWindow
connect(_midiIn, &PhMidiInput::quarterFrame, this, &MyWindow::onQuarterFrame);
When trying to connect to a custom class (MidiTest) inheriting from QObject it does'nt trigger:
connect(_midiIn, &PhMidiInput::quarterFrame, this, &MidiTest::onQuarterFrame);
I was wondering if there was something around QObject::moveToThread() but since I don't create the thread myself (the signal is sent from a callback) I don't know if I need to use it or not.
It is as simple as calling emit obj->quarterFrame(data); from the callback. If the connection type is default then this will be perfectly thread safe.
Though you should create a QByteArray from data to pass around as data will likely not be valid by the time the slots get called.
void callback(..., void* user){
//user is the standard void* in most callbacks passed as reinterpret_cast<void*>(this)
unsigned char* data = ...;
QByteArray bytes(data);
emit reinterpret_cast<PhMidiInput>(user)->quarterFrame(bytes);//calling the signal which will behave as you'd expect
}
In the last connect() call you pass this and MidiTest::onQuarterFrame as the receiver object and method. I bet this is not an instance of MidiTest, is it?
The problem here is that you're passing SLOT method from MidiTest, while the receiver object is this, which is not instance of MidiTest. Change receiver from this to some instance of MidiTest.
I'm surprised this code doesn't crash your application when running.

created two thread from within main thread - qthread

I have created two threads from within a main thread in linux but how to create them in QT?
My GUI has two buttons start & stop.
I want that when start button is pressed then a main thread starts & it starts two threads TX & RX.
When stop button is pressed then tx thread stops, then receive thread stops & then main thread stops.
I need to create two threads (TX/RX) from a main thread.
Please suggest how to complete my code.
Do I have to create seperate object for tx/rx thread & then move these objects to respective Qthread m_preceiveThread, m_pwriteThread?
Also which point should tx & rx thread be created(should i create them in constructor of main thread object) ?
What logic should be used to stop the threads when button is pressed (Mostly we use to set a flag which takes control out of the thread work function)?
When dowork() function ends, will the thread terminate?
I have created two Qthread QThread m_preceiveThread; & QThread m_pwriteThread; inside the deviceThreadObject object, is it the right place to have the threads which i will be starting from my main thread ?
6.Also before applying movetothread function to my main thread. Do I will have to prepare the tx & rx thread in the constructor of main thread object deviceThreadObject m_deviceThreadObject; or in the slot() which is triggred when thread is started.
Create thread:
QThread m_deviceThread;
deviceThreadObject m_deviceThreadObject;
connect(&m_deviceThread,SIGNAL(started()),m_deviceThreadObject,SLOT(dowork()));
m_deviceThreadObject.moveToThread(&m_deviceThread);
Tx/Rx Thread object:
/// forward declarations
class deviceThreadObject;
// transmit & receive thread object
class txRxThreadObject : public QObject
{
Q_OBJECT
public:
explicit txRxThreadObject(QObject *parent = 0);
deviceThreadObject *m_pMainThreadObj;
/* Termination control thread*/
bool m_bQuitRx;
bool m_bQuitTx;
signals:
public slots:
void dowork_tx();
void dowork_rx();
};
Main device thread object:
class deviceThreadObject : public QObject
{
Q_OBJECT
public:
explicit deviceThreadObject(QObject *parent = 0);
QThread m_preceiveThread;
QThread m_pwriteThread;
bool m_bQuit;
/// Pointer to QStandardItemModel to be used inside - canTableView
QStandardItemModel *modeltable;
/// pointer to the txRxThreadObject object
class txRxThreadObject *m_ptxRxThreadObject;
/// setup function for device thread
void dosetup(QThread &devThread);
signals:
public slots:
void dowork()
{
for(int i=0; i<100; i++)
{
qDebug() << "hello";
}
}
};

How to delete a QProcess instance correctly?

I have a class looking like this:
class FakeRunner : public QObject
{
Q_OBJECT
private:
QProcess* proc;
public:
FakeRunner();
int run()
{
if (proc)
return -1;
proc = new QProcess();
QStringList args;
QString programName = "fake.exe";
connect(comp, SIGNAL(started()), this, SLOT(procStarted()));
connect(comp, SIGNAL(error(QProcess::ProcessError)), this,
SLOT(procError(QProcess::ProcessError)));
connect(comp, SIGNAL(finished(int, QProcess::ExitStatus)), this,
SLOT(procFinished(int, QProcess::ExitStatus)));
proc->start(programName, args);
return 0;
};
private slots:
void procStarted() {};
void procFinished(int, QProcess::ExitStatus) {};
void procError(QProcess::ProcessError);
}
Since "fake.exe" does not exist on my system, proc emits the error() signal. If I handle it like following, my program crashes:
void FakeRunner::procError(QProcess::ProcessError rc)
{
delete proc;
proc = 0;
}
It works well, though, if I don't delete the pointer. So, the question is how (and when) should I delete the pointer to QProcess? I believe I have to delete it to avoid a memory leak. FakeRunner::run() can be invoked many times, so the leak, if there is one, will grow.
Thanks!
You can't delete QObject instance inside slot which is connected to a signal in this instance using normal delete operator. This is due to the fact that if signal and slot connected using direct connection then the slot actually called from the signal implementation made by moc. This is like attempt to delete this; from inside the member of a class. There is a solution QObject::deleteLater(). Object will be deleted by Qt event loop inside events processing function. So you need to call proc->deleteLater() in your case.
And you don't need to disconnect signal from slot since Qt do it automatically when QObject is deleted.

Resources