abg
abg

Reputation: 43

Is it possible to force execute main thread (GUI thread) from render thread?

We have commands queue which executed in render thread, directly between QQuickWindow::beforeRendering and QQuickWindow::afterRendering, commands do changes on data model, and when data model changes it immediately notify our custom OpenGL render engine to sync data.

The problem is when data model changes it also notify subscribers who aim to update UI. But it is error prone approach to update UI from different thread. One way is using Qt::QueuedConnection. This is error prone too because when it come executed model may go to far state.

Design is very similar to this example.

Is it possible for example update QStadardItemModel linked with QML from render thread?

Upvotes: 2

Views: 1423

Answers (2)

You could get inspiration from Calling Qt Functions From Unix Signal Handlers

You might have some central data structure, e.g. some std::deque containing lambda expressions; let's call it your todo list. That todo list also manages a pipe(7) (so two file descriptors).

You would protect that todo list with appropriate std::mutex and use std::condition_variable

You would synchronize between the render thread and the main Qt thread by using some pipe(7) or fifo(7), then use (in the main thread) some QSocketNotifier for synchronization. The render thread would also write(2) some byte to that fifo or pipe when adding a closure to your todolist, and the main thread would use QSocketNotifier to read(2) it, then fetch (pop) a closure from your todo list and run it.

Upvotes: 2

GrecKo
GrecKo

Reputation: 7160

It is possible, you can do it with the QMetaObject::invokeMethod(QObject *context, Functor function, Qt::ConnectionType type = Qt::AutoConnection, FunctorReturnType *ret = nullptr) overload of QMetaObject::invokeMethod.

For the context you need to pass an object living on thread you want to run your function. For the GUI thread, QCoreApplication::instance() is a good candidate. For the connection type, use Qt::QueueConnection or Qt::BlockingQueuedConnection depending on your needs (weither you need the call to block or not). If you are using Qt::BlockingQueuedConnection, make sure that you are not currently in the main thread (you could do a check and pass Qt::DirectConnection if that is the case).

And for the functor, a lambda will do the trick.

In example :

qDebug() << "1 main thread" << QThread::currentThreadId();
QtConcurrent::run([] {
    qDebug() << "1 QtConcurrent thread" << QThread::currentThreadId();

    QMetaObject::invokeMethod(QCoreApplication::instance(), [] {
        qDebug() << "invokeMethod thread" << QThread::currentThreadId();
    }, Qt::BlockingQueuedConnection);

    qDebug() << "2 QtConcurrent thread" << QThread::currentThreadId();
});
qDebug() << "2 main thread" << QThread::currentThreadId();

This outputs:

1 main thread 0x1c7c
2 main thread 0x1c7c
1 QtConcurrent thread 0x19ec
invokeMethod thread 0x1c7c
2 QtConcurrent thread 0x19ec

Upvotes: 3

Related Questions