Как использовать привязки Qt-Dbus без блокировки основного потока

Моя цель — создать библиотеку, используя привязки Qt к DBus.

Я попытался создать приложение Qt, не запуская QEventLoop (предоставляемый классом QCoreApplication) в основном потоке.

Вот минималистичный пример приложения, которое отлично работает с версией QT-4.6.2, но блокируется при самоанализе с использованием QT-4.8 или выше.

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());
}

Как вы можете видеть в файле main.cpp, app.exec() закомментировано, но позволяет приложению нормально работать на QT-4.8 или более поздних версиях (5.3.0).

У меня следующий вопрос: возможно ли использовать привязки DBus Qt, вызывающие app.exec() в другом потоке, отличном от основного, на Qt-4.8 или 5.3?


person ben-du_a    schedule 05.06.2014    source источник
comment
@naab Я сбит с толку. ОП спрашивает, можно ли использовать привязки Qt DBus без вызова app.exec() в основном потоке на Qt-4.8 или 5.3? Разве это не должно быть с, согласно тому, что он продемонстрировал?   -  person Tay2510    schedule 10.06.2014
comment
@ Tay2510 Демонстрация показывает, что без выполнения app.exec() в основном потоке код работает с Qt 4.6.2. И ожидаемое решение - это какой-то способ использовать Qt EventLoops, не блокируя основной поток. Разве нельзя использовать Qt как библиотеку, начиная с версии 4.8.4, без блокировки основного потока и использования QEventLoops (для работы сигналов/слотов)?   -  person naab    schedule 10.06.2014
comment
@naab Итак, вы задаете вопрос, отличный от ОП, или я должен сказать, что ОП задал неправильный вопрос? Потому что вы сказали Возможно ли это... без блокировки основного потока, в то время как OP спрашивал Возможно ли это... без вызова app.exec() в основном потоке. Извините, я не хотел играть словами, я просто хочу убедиться, в чем проблема. Поскольку случай без app.exec() уже был продемонстрирован и работал на версии 4.8, почему мы должны снова запрашивать без?   -  person Tay2510    schedule 10.06.2014
comment
@naab Неважно, Жюль Барату только что отредактировал пост. Кажется, что исходный вопрос немного вводит в заблуждение.   -  person Tay2510    schedule 10.06.2014


Ответы (1)


Предыстория: существует частный класс с именем QDBusConnectionPrivate, который наследуется от QObject и обрабатывает всю сеть. К сожалению, если вы посмотрите на qdbusconnection.cpp:1116 вы увидите, что Qt жестко кодирует от moveToThread до QCoreApplication::instance().

Возможно, вам следует отправить запрос на расширение, чтобы позволить пользователю создать QDBusConnection, который использует указанный пользователем поток или цикл событий. См. обновление ниже.

Тем временем, если вам удобно делать некоторые опасные вещи, вы можете взломать это в себе, создав свой собственный подкласс QDbusConnection (я назвал свой SpecializedDBusConnection), который принимает QThread в качестве третьего аргумента того, где вы хотите экземпляр QDbusConnectionPrivate для перемещения. Затем используйте этот класс для создания соединения вместо стандартного QDbusConnection::sessionBus().

Поскольку при этом используются некоторые частные классы, требуется включение некоторых файлов частных заголовков (отмеченных в коде ниже), которые, в свою очередь, попытаются включить различные заголовки библиотеки dbus, что потребует изменения INCLUDEPATH файла проект для включения библиотеки dbus включает путь.

Я убедился, что это работает на Qt 5.3.0 и Qt 4.8.6.

Обновление: в Qt 5.6, QtDBus был переработан для использования потоков для обработки входящих/исходящих сообщений; больше нет блокировки основного потока!

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]
person Linville    schedule 10.06.2014
comment
Большое спасибо, что потратили на это время. Еще не проверял, но я думаю, что это путь. Все еще блуждаю, почему они удалили этот вызов moveToThread. if you're comfortable doing some dangerous things => Ваше решение далеко не грязный/опасный способ решения этой проблемы имхо :). В любом случае, хороший улов на нем полностью стоит награды! - person naab; 10.06.2014
comment
@Linville Большое спасибо за ответ! я проверю ваше решение - person ben-du_a; 11.06.2014