How to use Qt-Dbus bindings without blocking the main thread
Asked Answered
F

1

9

My goal is to create a library using the Qt's DBus bindings.

I tried to create a Qt application without launching the QEventLoop (provided by the QCoreApplication class) in the main thread.

Here is a minimalistic application sample, working fine using QT-4.6.2 version but blocking on introspection using QT-4.8 or higher.

DBusHandler.hpp

#pragma once
#include <iostream>
#include <QtCore/QThread>
#include <QtCore/QtCore>
#include <QtDBus/QDBusInterface>

class DBusHandler : public QThread
{
    Q_OBJECT;

private:     
    void run(void)
    {
        QDBusConnection connection = QDBusConnection::sessionBus();

        connection.registerService("my.qdbus.example");
        connection.registerObject("/", this, QDBusConnection::ExportAllSlots);
        exec();
    }

public:
    DBusHandler(void) {}
    virtual ~DBusHandler(void) {}

    void stop(void)
    {
        QDBusConnection connection = QDBusConnection::sessionBus();

        connection.unregisterObject("/");
        connection.unregisterService("my.qdbus.example");
        connection.disconnectFromBus(connection.name());
        QThread::quit();
    }

public slots:
    void remoteCall(QByteArray message)
    {
        std::cout << "Message size: " << message.size() << std::endl;
    }
};

main.cpp

#include "DBusHandler.hpp"

int main(int ac, char **av)
{
    QCoreApplication app(ac, av);
    DBusHandler handler;

    handler.moveToThread(&handler);

    handler.start();
    while (not handler.isRunning());

    // app.exec();
    sleep(10); // Gives time to call using the command line: "qdbus my.qdbus.example / local.DBusHandler.remoteCall a_message"

    handler.stop();
    while (handler.isRunning());
}

As you can see in the main.cpp file, app.exec() is commented out, but makes the application working fine on QT-4.8 or higher versions (5.3.0).

My question is the following: Is it possible to use the Qt's DBus bindings calling app.exec() in an other thread than the main one, on Qt-4.8 or 5.3 ?

Flam answered 5/6, 2014 at 11:3 Comment(4)
@naab I am befuddled. The OP asks "Is it possible to use the Qt's DBus bindings without the app.exec() call in the main thread on Qt-4.8 or 5.3 ?". Shouldn't it be with, according to what he has demonstrated?Subhead
@Subhead The demonstration shows that without executing the app.exec() in the main thread, the code works with Qt 4.6.2. And the solution expected is some way to use Qt EventLoops without blocking the main thread. Isn't it possible to use Qt as a library starting at version 4.8.4 without blocking the main thread and using QEventLoops (to make signals/slots working) ?Roomer
@Roomer So you are asking a different question from OP, or should I say the OP has asked a wrong question? Because you said "Isn't it possible...without blocking the main thread" while the OP asked "Is it possible...without the app.exec() call in the main thread". Sorry I didn't mean to play on words, I just want to make sure what the problem is. Because the case without app.exec() has already been demonstrated and worked on 4.8, why should we still ask for without again?Subhead
@Roomer Nevermind, Jules Baratoux has justed edit the post. It seems that the original question is a little bit misleading.Subhead
D
10

Background: There is a private class called QDBusConnectionPrivate which inherits from QObject and handles all networking. Unfortunately, if you look at qdbusconnection.cpp:1116 you'll see that Qt hard codes the moveToThread to QCoreApplication::instance().

You should probably submit an enhancement request to allow the user to create a QDBusConnection that uses a user specified thread or event loop. See update below.

In the meantime, if you're comfortable doing some dangerous things, you can hack it in yourself by creating your own QDbusConnection subclass (I called mine SpecializedDBusConnection) that takes QThread as a third argument of where you want the QDbusConnectionPrivate instance to be moved to. Then use that class to create the connection instead of the default QDbusConnection::sessionBus().

As this is using some private classes, it requires the inclusion of some private header files (noted in the code below) which in turn will attempt to include various dbus library headers which will necessitate the modifying of INCLUDEPATH of the project to include the dbus library include path.

I've verified this works on Qt 5.3.0 and Qt 4.8.6.

Update: In Qt 5.6, QtDBus was refactored to use threads for incoming/outgoing message processing; no more blocking of the main thread!

DBusHandler.hpp

#pragma once
#include <iostream>
#include <QtCore/QThread>
#include <QtCore/QtCore>
#include <QtDBus/QDBusInterface>
#include <QtDBus/QDBusConnectionInterface>

#include "/path/to/Qt5.3.0/5.3/Src/qtbase/src/dbus/qdbusconnection_p.h"

class SpecializedDBusConnection : public QDBusConnection {
    const char *ownName;
public:
    inline SpecializedDBusConnection(BusType type, const char *name, QThread *thread)
        : QDBusConnection(connectToBus(type, QString::fromLatin1(name))), ownName(name)
    {
        if (QDBusConnectionPrivate::d(*this)) {
            QDBusConnectionPrivate::d(*this)->moveToThread(thread);
        }
    }

    inline ~SpecializedDBusConnection()
    { disconnectFromBus(QString::fromLatin1(ownName)); }
};

class DBusHandler : public QThread
{
    Q_OBJECT;

private:     
    void run(void)
    {
        QDBusConnection connection = SpecializedDBusConnection(QDBusConnection::SessionBus, "qt_default_session_bus", this);

        connection.registerService("my.qdbus.example");
        connection.registerObject("/", this, QDBusConnection::ExportAllSlots);

        exec();
    }
[snip]
Dickie answered 10/6, 2014 at 16:38 Comment(1)
Thank you a lot for spending time on it. Didn't test it out yet, but I guess it is the way to go. Still wandering why they removed this moveToThread call. if you're comfortable doing some dangerous things => Your solution is far from a dirty / dangerous way on resolving this issue imho :). Anyway good catch on it, totally worth the bounty !Roomer

© 2022 - 2024 — McMap. All rights reserved.