Deadlocked QMessageBox doesn't appear at signal processing



  • What i want to do

    I have a main GUI thread executing this:

    void main(int argc, char** argv)
    {
        QApplication app(argc, argv);
        MainWindow mainWindow; // an engine thread is started here
        mainWindow.show();
        app.exec(); // here the GUI thread enters the event processing loop
    }
    

    I connect a signal and a slot to handle notifications in GUI thread (this is MainWindow) with BlockingQueuedConnection type:

    connect(&warningSignalEmitter, SIGNAL(popuped(QString)),
        this, SLOT(onPopupWarning(QString)), Qt::BlockingQueuedConnection);
    

    If a signal is emitted, the GUI thread shows a MessageBox:

    void MainWindow::onPopupWarning(QString message)
    {
        QMessageBox::warning(nullptr, "Warning", message);
    }
    

    I have an engine thread creating OpenGL context and executing a rendering loop. QOpenGLDebugLogger is initialized in this thread with this code:

    logger = new QOpenGLDebugLogger();
    logger->initialize();
    
    connect(logger, SIGNAL(messageLogged(QOpenGLDebugMessage)),
        this, SLOT(glDebugMessageLogged(QOpenGLDebugMessage)),
        Qt::ConnectionType::DirectConnection);
    
    logger->startLogging(QOpenGLDebugLogger::LoggingMode::SynchronousLogging);
    

    The connection type is DirectConnection, so if an error occures when the engine thread calls OpenGL function, the engine thread enters the slot, which is:

    void Engine::glDebugMessageLogged(const QOpenGLDebugMessage &debugMessage)
    {
        emit warningSignalEmitter.popuped(debugMessage.message());
    }
    

    The engine thread is locked here until the GUI thread has exited MainWindow::onPopupWarning.

    So i want see a Message Box if an error has occured in OpenGL function call.

    My problem

    If i just emit a signal with emit warningSignalEmitter.popuped(debugMessage.message()) somewhere in engine code everything works fine, i see a MessageBox, the engine thread is locked at signal emitting, and the GUI thread is locked in onPopupWarning slot until i have clicked OK on the message box.

    While the message box is being showed i see this normal stack trace:

    BUT if an error occures in OpenGL function call, the engine thread successfully enteres glDebugMessageLogged slot (because of direct connection) and does signal emitting. After that it is locked as expected. The GUI thread enteres the onPopupWarning slot, but when it calls QMessageBox::warning, the Message Box doesn't appear, the GUI thread has being locked at this call, and i can't click OK and continue execution.

    And I see this unusual stack trace at this deadlock:

    What may it be?


  • Qt Champions 2017

    Why do you pass BlockingQueuedConnection to connect ?

    connect(&warningSignalEmitter, SIGNAL(popuped(QString)),
    this, SLOT(onPopupWarning(QString)), Qt::BlockingQueuedConnection);

    Here the signal is sent from engineThread & slot is in gui thread. Since you are passing blocked queued connection, engine thread will hang till slot is complete. Here inside the slot you are calling message box. unless you close message box, slot is not finished.

    Hence you have threads are blocked. Hope it helps.


  • Qt Champions 2017

    Check that the threads on emission and reception are indeed different. The stack you provided is for the GUI thread only, consider extracting the trace for the two threads to see where it locks and why.



  • @dheerendra I want this behavior and it works fine, as I said, if I emit the signal just somewhere outside OpenGL function call. If I see a message box, both engine and gui threads wait until I have clicked OK. But If an error occurres in OpenGL function call and there are two signals described above are emitted, I cannot see a Message Box, so I can't continue the execution of both the threads



  • @kshegunov the threads are indeed different and the engine thread is locked because of blocking queue connection at signal emitting at one line in both cases, the engine thread works fine, stack traces are equal for it, i cannot show it now


  • Qt Champions 2017

    Fastest thing to check - use the default Qt::QueuedConnection. If you deadlock it's not your connect, otherwise it's something about the connection (like having the objects in the same thread and using Qt::BlockingQueuedConnection).



  • @kshegunov i've checked simple QueuedConnection. Now i see MessageBoxes successfully in both cases: i emit a warningSignalEmitter.popuped(..) signal manually outside of OpenGL function calls AND the QOpenGLDebugLogger emits a messageLogged(..) signal, and in its slot a warningSignalEmitter.popuped(..) signal is emitted. The engine threads just triggers these emittings and continue its execution, what is not good. And the GUI thread shows me both MessageBox at the same time.

    When i switch back to BlockingQueuedConnection, as i described earlier, only emittings outside the OpenGL function calls works fine and i can see a MessageBox and continue execution of both engine and GUI threads, and if a signal is emitted inside OpenGL functions call, i don't see a MessageBox.



  • @kshegunov And i've figured out one thing. If i emit QOpenGLDebugLogger::messageLogged(..) manually outside of OpenGL function calls, none deadlock happens and i see a MessageBox, but if the signal is emitted because of some error in an OpenGL functions call, and so the signal emitting has its place in the stack trace after the OpenGL function call, so the deadlock happens and i don't see a MessageBox. So it seems that if i try to use GUI after some OpenGL functions calls, the GUI becomes broken due to inner errors. I have no idea how to fix it, but only to left this idea and use error logging and notificating in old manner: place calls after each OpenGL function call or its group. Or don't use the GUI and just log errors in some stream.



  • @mr.indieperson

    Hi! You can try to create your QMessageBox as a pointer. For example:

    QMessageBox *msgBox = new QMessageBox(this);
    msgBox->setWindowModality(Qt::ApplicationModal);
    msgBox->setText("This is a test!");
    msgBox->show();
    

    This way you can customize your msgbox. Then in the destructor call delete msgBox to free the resources.

    Also you can check where the deadlock occurs using qDebug() (http://doc.qt.io/qt-5/qdebug.html), place for example: qDebug() << "Test1"; in your signals/slots and then run the program, you will know what is going on in the code.



  • @Cobra91151 thanks, but this doesn't work. MessageBox is deadlocked at show(), if an OpenGL error has occured earlier in a stack trace. And i've learned all i could with debugging.


  • Qt Champions 2017

    @mr.indieperson said in Deadlocked QMessageBox doesn't appear at signal processing:

    And i've figured out one thing.

    Me too, I think. Where do you emit QOpenGLDebugLogger::messageLogged?



  • This post is deleted!


  • @kshegunov So i've written a program, demonstrating the issue. I've posted source files in a bug report, its link below. If you run the program, make sure you've reached "OpenGL has inited!" message.

    I've learned, that not all errors cause a deadlock, the program demonstrates this too. So it seems for me like a bug: the program behaves differently in similar situations, looks like unexpected/undefined behavior. I've decided to report about this issue, they will figure this out: https://bugreports.qt.io/browse/QTBUG-69780


Log in to reply
 

Looks like your connection to Qt Forum was lost, please wait while we try to reconnect.