c++qtc++11asynchronousqdbus

Proper way to implement async function in Qt/DBus adaptor


As described in D-Bus documentation, all IPC calls considered as asynchronous. When Qt calls remote D-Bus object through QDBusAbstractInterface, there's QBusPendingCall<T> which is fully async and provide signalling when call ran to completion.

In my application design I want to implement async call on my object adaptor, but current Qt/DBus implementation assumes, that all method calls are blocking.

So, there's a question: is there proper way to implement handling D-Bus method call asynchronously?


Solution

  • This is explained pretty well in Declaring Slots in D-Bus Adaptors.

    We do this by writing a slot that stores the request data in a persistent structure, indicating to the caller using QDBusMessage::setDelayedReply(true) that the response will be sent later.

    struct RequestData
    {
        QString request;
        QString processedData;
        QDBusMessage reply;
    };
    
    QString processRequest(const QString &request, const QDBusMessage &message)
    {
        RequestData *data = new RequestData;
        data->request = request;
        message.setDelayedReply(true);
        data->reply = message.createReply();
        QDBusConnection::sessionBus().send(data->reply);
    
        appendRequest(data);
        return QString();
    }
    

    The use of QDBusConnection::sessionBus().send(data->reply) is needed to explicitly inform the caller that the response will be delayed. In this case, the return value is unimportant; we return an arbitrary value to satisfy the compiler.

    When the request is processed and a reply is available, it should be sent using the QDBusMessage object that was obtained. In our example, the reply code could be something as follows:

    void sendReply(RequestData *data)
    {
        // data->processedData has been initialized with the request's reply
        QDBusMessage &reply = &data->reply;
    
        // send the reply over D-Bus:
        reply << data->processedData;
        QDBusConnection::sessionBus().send(reply);
    
        // dispose of the transaction data
        delete data;
    }
    

    As can be seen in the example, when a delayed reply is in place, the return value(s) from the slot will be ignored by Qt D-Bus. They are used only to determine the slot's signature when communicating the adaptor's description to remote applications, or in case the code in the slot decides not to use a delayed reply.

    The delayed reply itself is requested from Qt D-Bus by calling QDBusMessage::reply() on the original message. It then becomes the responsibility of the called code to eventually send a reply to the caller.

    Warning: When a caller places a method call and waits for a reply, it will only wait for a limited amount of time. Slots intending to take a long time to complete should make that fact clear in documentation so that callers properly set higher timeouts.