3

I develop and compile on RedHat 7.4 with GCC 4.8.5 and Qt 4.8.5. The code has to be statically linked. Then executed on a virtual machine running Scientific Linux release 6.7. memcpy-Wrap is used in order to prevent dependencies on newer GLIBC >= 2.4

I have the following MWE:

#include <iostream>
#include <unistd.h>
#include <QtCore>
#include <QThread>

__asm__(".symver memcpy, memcpy@GLIBC_2.2.5");
extern "C" {
void *__wrap_memcpy(void *dest, const void *src, size_t n) { return memcpy(dest, src, n); }
}

class Worker : public QThread {
    void run() {
        std::cout << "WORKER: Started." << std::endl;
        QObject::connect(this, SIGNAL(finished()), QCoreApplication::instance(), SLOT(quit()));
        int i=0;
        while(i++<3) {
            std::cout << "WORKER: I am running." << std::endl;
            usleep(1e6);
        }
        std::cout << "WORKER: Finished." << std::endl;
    }
};

int main(int argc, char** argv) {

    std::cout << "MAIN: Init QCoreApplication" << std::endl;
    QCoreApplication qtApplication(argc, argv);

    std::cout << "MAIN: Init Worker" << std::endl;
    Worker myWorker;
    myWorker.start();

    std::cout << "MAIN: Start Event-Loop." << std::endl;
    qtApplication.exec();

    std::cout << "MAIN: Event-Loop finished." << std::endl;
    return 0;
}

This code is compiled on the RedHat-System with

g++ -I$QTD/mkspecs/linux-g++ -I$QTD/include -I$QTD/include/QtCore -o mwe mwe.cpp  -Wl,--wrap=memcpy -L$QTD/lib/ -lQtCore -lQtNetwork -lglib-2.0 -lrt -lpthread -ldl -lz

where $QTD holds my installation of my Qt-4.8.5.

The following behavior is expected and observed on the Red-Hat-System:

MAIN: Init QCoreApplication
MAIN: Init Worker
MAIN: Start Event-Loop.
WORKER: Started.
WORKER: I am running.
WORKER: I am running.
WORKER: I am running.
WORKER: Finished.
MAIN: Event-Loop finished.

The following behavior is observed on the Scientific-Linux-System:

MAIN: Init QCoreApplication
MAIN: Init Worker
MAIN: Start Event-Loop.
WORKER: Started.
WORKER: I am running.
WORKER: I am running.
WORKER: I am running.
WORKER: Finished.

And then the application never finishes.

It seems that in the Red-Hat-System, the finished-signal from the worker-thread is connected to the quit-slot in the core application. This doesn't seem to happen in the Scientific-Linux-System. Does anyone have any advice why this happens and how I can debug it?

Tobias
  • 141
  • 12
  • Run it under a debugger to find out where it's stuck. Also, instead of connecting to `QCoreApplication::quit` you could connect to a `lambda` that invokes `QCoreApplication::quit` -- that would allow you to add some extra diagnostic output. – G.M. Apr 04 '18 at 10:15
  • @G.M. I would suggest to prefer Qt5 signals and lambdas as well but OP stated to be on Qt 4.8.5. Are Qt5 signals already available for this version? – Scheff's Cat Apr 04 '18 at 10:19
  • I think you both are talking about the new signature where I don't have to put the capitals "SIGNAL" and "SLOT" but just provide the method-name of the class? Yeah, I also understood that this is only from Qt5 on, but not available in my version. – Tobias Apr 04 '18 at 10:29
  • @Scheff Sorry, I missed the `Qt4` constraint. That rules out a `lambda` but a function that invokes `QCoreApplication::quit` would serve the same purpose. – G.M. Apr 04 '18 at 10:30
  • You might try to establish signal/slot connection of type `Qt::DirectConnection`. I guess your signal is emitted, but slot isn't called because `qtApplication.exec();` blocks the main event loop. – vahancho Apr 04 '18 at 10:35
  • 1
    Have you tried invoking the quit slot manually without the connection at the end of your worther thread? WIth [QMetaObject::invokeMethod](https://doc.qt.io/qt-5/qmetaobject.html#invokeMethod) it should work the same over thread boundaries – xander Apr 04 '18 at 10:41
  • @vahancho: I thought I had to call qtApplication.exec() in order for the main event loop to start? And that this method then listens to its slot "quit"? – Tobias Apr 04 '18 at 11:09
  • @xander: Yes I tried that just now, same behavior. – Tobias Apr 04 '18 at 11:09
  • @Tobias, the thing is that you don't know when exactly qtApplication.exec() will be called. It can be called while your thread is still executing, though. – vahancho Apr 04 '18 at 11:38
  • @vahancho: Ah, ok, but shouldn't matter, I tried with specifying DirectConnection or QueuedConnection. – Tobias Apr 04 '18 at 11:44

2 Answers2

0

You have to use QThread in proper way. overloading run() function is not safe and Nokia accept it before and show how to use QThread.

Look at this documentation.

CMLDMR
  • 334
  • 2
  • 12
  • Thanks for the suggestion, but I think my colleague tried this approach before and told that it also didn't work. I will try it again now. – Tobias Apr 04 '18 at 10:29
  • 2
    Please notice there's a debate around this: https://woboq.com/blog/qthread-you-were-not-doing-so-wrong.html (and please, this is a comment, not an answer). – p-a-o-l-o Apr 04 '18 at 10:35
0

As it was mentioned - it is not recommended way to work with threads in Qt. But it is fine for your case. Anyway, I recommend you to try the next approach:

class Worker : public QThread {
    Q_OBJECT // Don't forget this macro
    void run() {
        std::cout << "WORKER: Started." << std::endl;
        // QObject::connect(this, SIGNAL(finished()), QCoreApplication::instance(), SLOT(quit()));
        int i=0;
        while(i++<3) {
            std::cout << "WORKER: I am running." << std::endl;
            usleep(1e6);
        }
        std::cout << "WORKER: Finished." << std::endl;
        this->deleteLater();
    }
};

int main(int argc, char** argv) {

    std::cout << "MAIN: Init QCoreApplication" << std::endl;
    QCoreApplication qtApplication(argc, argv);

    std::cout << "MAIN: Init Worker" << std::endl;
    Worker *myWorker = new Worker();
    QObject::connect( myWorker, SIGNAL(destroyed()), &qtApplication, SLOT(quit());
    myWorker->start();

    std::cout << "MAIN: Start Event-Loop." << std::endl;
    qtApplication.exec();

    std::cout << "MAIN: Event-Loop finished." << std::endl;
    return 0;
}
Dmitry Sazonov
  • 8,801
  • 1
  • 35
  • 61
  • 1
    I thought, I only need Q_OBJECT when I declare signals/slots in my derived class? The class Worker only overloads the run() method, so it should not require the macro, right? – Tobias Apr 04 '18 at 11:41
  • It shows the same behavior as before on the SL system. – Tobias Apr 04 '18 at 11:52
  • Don't know... Btw, try something like this, in main function: `QTimer::singleShot(100, &app, SLOT(quit()));` - it is works? – Dmitry Sazonov Apr 04 '18 at 11:58
  • I used `QTimer::singleShot(1e4, &qtApplication, SLOT(quit()));` in the main thread. Then the worker finishes in around 3 seconds and the application quits itself after 10 seconds. So the event loop of the qtApplication is indeed active and listens and can be reached by signals of its own thread. – Tobias Apr 04 '18 at 12:07
  • @Tobias can you please add a slot to the `Worker` class and connect it with its own `finished` signal, just to test if the signal is emitted (put a `qDebug << "..."` inside the slot). – p-a-o-l-o Apr 04 '18 at 13:30
  • Try, please, to do it from worker thread with zero first argument. `QTimer::singleShot(0, qApp, SLOT(quit()));` – Dmitry Sazonov Apr 04 '18 at 13:30