libuv 中 sd-bus 的事件循环处理

Posted

技术标签:

【中文标题】libuv 中 sd-bus 的事件循环处理【英文标题】:Event loop handling for sd-bus in libuv 【发布时间】:2017-04-13 18:12:30 【问题描述】:

我们有一个来自libuv 的事件循环来处理 unixsocket 和 TCP 套接字。该程序现在还必须处理 DBus,我们决定为此使用 sd-bus

伦纳特wrote on his blog:

Note that our APIs, including sd-bus, integrate nicely into sd-event
event loops, but do not require it, and may be integrated into other
event loops too, as long as they support watching for time and I/O events.

所以我认为,这一定是可能的。

我可以通过sd_bus_get_fd (sd_bus *bus) 获得dbus 套接字fd。 但是我找不到任何明显的方法来阻止 sd-bus 使用其bus_poll 方法在内部等待事件。

例如,当使用sd_bus_call(...) 调用will block 和ppoll 的方法时。

那么:如何处理 libuv 中的 dbus 事件?

【问题讨论】:

【参考方案1】:

我想通了,下面是一个关于如何联合 C++、libuv 和 sd-bus 的示例:

我建议您阅读http://0pointer.de/blog/the-new-sd-bus-api-of-systemd.html 以大致了解 sd-bus。

这些是我在https://github.com/TheJJ/horst实现的代码sn-ps

然后可以使用不阻塞的sd_bus_call_async 完成方法调用(与sd_bus_call 相对)。 不要忘记在sd_bus_call_async 之后调用update_events(),以便通过套接字发出调用!

/**
 * Callback function that is invoked from libuv
 * once dbus events flowed in.
 */
static void on_dbus_ready(uv_poll_t *handle, int /*status*/, int /*events*/) 
    DBusConnection *connection = (DBusConnection *)handle->data;

    sd_bus *bus = connection->get_bus();

    // let dbus handle the available events request
    while (true) 
        // this will trigger the dbus vtable-registered functions
        int r = sd_bus_process(bus, nullptr);

        if (r < 0) 
            printf("[dbus] Failed to process bus: %s", strerror(-r));
            break;
        
        else if (r > 0) 
            // try to process another request!
            continue;
        
        else 
            // no more progress, wait for the next callback.
            break;
        
    

    // update the events we watch for on the socket.
    connection->update_events();


/**
 * Convert the sdbus-returned poll flags to
 * corresponding libuv flags.
 */
int poll_to_libuv_events(int pollflags) 
    int ret = 0;
    if (pollflags & (POLLIN | POLLPRI)) 
        ret |= UV_READABLE;
    
    if (pollflags & POLLOUT) 
        ret |= UV_WRITABLE;
    

    // we also have the non-corresponding UV_DISCONNECT

    return ret;



class DBusConnection 
public:
    DBusConnection(Satellite *sat);

    virtual ~DBusConnection() = default;

    /** connect to dbus */
    int connect() 
        int r = sd_bus_open_system(&this->bus);

        if (r < 0) 
            printf("[dbus] Failed to connect to bus: %s", strerror(-r));
            goto clean_return;
        

        r = sd_bus_add_object_vtable(
            this->bus,
            &this->bus_slot,
            "/rofl/lol",      // object path
            "rofl.lol",       // interface name
            your_vtable,
            this              // this is the userdata that'll be passed
                              // to the dbus methods
        );

        if (r < 0) 
            printf("[dbus] Failed to install the horst sdbus object: %s", strerror(-r));
            goto clean_return;
        

        // register our service name
        r = sd_bus_request_name(this->bus, "moveii.horst", 0);
        if (r < 0) 
            printf("[dbus] Failed to acquire service name: %s", strerror(-r));
            goto clean_return;
        

        // register the filedescriptor from
        // sd_bus_get_fd(bus) to libuv
        uv_poll_init(this->loop, &this->connection, sd_bus_get_fd(this->bus));


        // make `this` reachable in callbacks.
        this->connection.data = this;

        // init the dbus-event-timer
        uv_timer_init(this->loop, &this->timer);
        this->timer.data = this;

        // process initial events and set up the
        // events and timers for subsequent calls
        on_dbus_ready(&this->connection, 0, 0);

        printf("[dbus] Listener initialized");
        return 0;

    clean_return:
        sd_bus_slot_unref(this->bus_slot);
        sd_bus_unref(this->bus);
        this->bus = nullptr;

        return 1;
    



    /** update the events watched for on the filedescriptor */
    void update_events() 
        sd_bus *bus = this->get_bus();

        // prepare the callback for calling us the next time.
        int new_events = poll_to_libuv_events(
            sd_bus_get_events(bus)
        );

        uint64_t usec;
        int r = sd_bus_get_timeout(bus, &usec);

        if (not r) 
            // if the timer is running already, it is stopped automatically
            // inside uv_timer_start.
            uv_timer_start(
                &this->timer,
                [] (uv_timer_t *handle) 
                    // yes, handle is not a poll_t, but
                    // we just care for its -> data member anyway.
                    on_dbus_ready((uv_poll_t *)handle, 0, 0);
                ,
                usec / 1000, // time in milliseconds, sd_bus provides µseconds
                0            // don't repeat
            );
        

        // always watch for disconnects:
        new_events |= UV_DISCONNECT;

        // activate the socket watching,
        // and if active, invoke the callback function
        uv_poll_start(&this->connection, new_events, &on_dbus_ready);
    


    /** close the connections */
    int close() 
        // TODO: maybe this memoryerrors when the loop actually
        //       does the cleanup. we have to wait for the callback.
        uv_close((uv_handle_t *) &this->timer, nullptr);

        uv_poll_stop(&this->connection);

        sd_bus_close(this->bus);
        sd_bus_slot_unref(this->bus_slot);
        sd_bus_unref(this->bus);
        return 0;
    

    /**
      * Return the bus handle.
      */
    sd_bus *get_bus() const 
        return this->bus;
    

protected:
    /**
      * loop handle
      */
    uv_loop_t *loop;

    /**
      * polling object for dbus events
      */
    uv_poll_t connection;

    /**
      * dbus also wants to be called periodically
      */
    uv_timer_t timer;

    /**
      * dbus bus handle
      */
    sd_bus *bus;

    /**
      * dbus slot handle
      */
    sd_bus_slot *bus_slot;
;

【讨论】:

我希望你有一个完整的工作示例。您显示的代码甚至没有使用 sd_bus_call_async! ://

以上是关于libuv 中 sd-bus 的事件循环处理的主要内容,如果未能解决你的问题,请参考以下文章

什么是libuv?

在nodejs中事件循环分析

异步编程之事件循环机制

浅析libuv源码-node事件轮询解析

Nodejs事件引擎libuv源码剖析之:请求(request)结构的设计剖析

周一硬核干货:通过Node.js的源码彻底理解EventLoop