Search code examples
c++qtqt4signals-slotsqt4.8

Invoke Qt slot safe from non-qt thread


I want to invoke a slot of MyWidget

class MyWidget : public QWidget {
Q_OBJECT

public slots:
void onFooBar(const std::string&);/*std::string& could also be replaced
    by a QString for easier meta system handling*/
};

But because in my case boost::asio use, with threads I don't want to have to do anything with Qt, I want to invoke this slot from a thread different from the main thread but a random thread I don't control. (On of the threads I let run boost::asio of course)

How can I do this? QCoreApplication::postEvent seems to be a nice choice, but the docs don't point out a nice way, on how to create the necessary QEvent. QMetaObject::invokeMethod with Qt::QueuedConnection seems nice too, but isn't documented as thread safe.

So how can I safely invoke a qt slot from a non qt managed thread?

(Although the title of Boost asio with Qt suggests this could be a duplicate, the question seems completely different to me, this questions is not necessarily connected to boost::asio)


Solution

  • Turns out QMetaObject::invokeMethod with Qt::QueuedConnection actually uses QCoreApplication::postEvent in its implementation (Thanks @peppe!). However the guarantee that it is thread safe, when

    1. used with Qt::QueuedConnection
    2. Lifetime of recipient managed by Qt (or AFAIK at least until after completed invocation)
    3. No other actions on recipient other than like this, from non main qt thread
    4. Lifetime of parameters managed by Qt (should be fine when using Q_ARS or call by value)

    is not documented, yet. But I have created a bug report and qt forum discussion, and it seems it was intended to be so, and a documentation change ticket is already been created.

    What I used in the end is the common pattern

    class MyWidget : public QWidget {
    Q_OBJECT
    
    public slots:
    void onFooBar(QString);
    };
    
    void asio_handler(const std::string& string, MyWidget* my_widget) {
    QMetaObject::invokeMethod(
                            my_widget, "onFooBar", Qt::QueuedConnection,
                            Q_ARG(QString, QString::fromStdString(string))
                            );
    }