glibevent-loop

Integrating sdbus-cpp with the GLib event loop


I need to integrate some code written using sdbus-cpp with my application's event loop, which is the GLib's GMainLoop.

sdbus-cpp's IConnection interface declares the getEventLoopPollData() function which, according to its documentation, should serve exactly this role. However, no examples on integrating with GMainLoop are given, and I couldn't find any projects on GitHub which use it in a GLib application.

Does anyone has some hints on how the integration should be implemented or, even better, some working code which can be shared?

The hard part is that the PollData structure which should be returned by this function does not really closely match the API of GMainContext, and I find the presence of two different file descriptor fields (fd and eventFd) particularly confusing.


Solution

  • You need to create a GSource that will periodically poll the event descriptor received from the sdbus::IConnection and attach it to the GMainContext.

    class EventSource
    {
    public:
        static EventSource *create(sdbus::IConnection &connection);
        static void destroy(EventSource *source);
    
        gboolean check() const;
        void dispatch();
    
    private:
        GSource m_base;
        GPollFD m_pollFD;
        sdbus::IConnection *m_connection;
    };
    
    
    GSourceFuncs sourceFuncs = {
        /* prepare */
        [](GSource *, int *timeout) -> gboolean {
            *timeout = -1;
            return G_SOURCE_REMOVE;
        },
        /* check */
        [](GSource *gsource) -> gboolean {
            EventSource *source = (EventSource *) gsource;
            return source->check();
        },
        /* dispatch */
        [](GSource *gsource, GSourceFunc, gpointer) -> gboolean {
            EventSource *source = (EventSource *) gsource;
    
            try {
                source->dispatch();
                return G_SOURCE_CONTINUE;
            }
            catch (const sdbus::Error &err) {
                Log(Err) << "Failed to process dbus connection events (" << err << ")";
                return G_SOURCE_REMOVE;
            }
        },
        /* finalize */
        [](GSource *) {},
        nullptr,
        nullptr,
    };
    
    EventSource *EventSource::create(sdbus::IConnection &connection)
    {
        EventSource *source = (EventSource *) g_source_new(&sourceFuncs, sizeof(EventSource));
        
        source->m_connection = &connection;
        source->m_pollFD.fd = connection.getEventLoopPollData().fd;
        source->m_pollFD.events = G_IO_IN;
    
        g_source_add_poll(&source->m_base, &source->m_pollFD);
        g_source_attach(&source->m_base, g_main_context_default());
    
        return source;
    }
    
    void EventSource::destroy(EventSource *source)
    {
        g_source_remove_poll(&source->m_base, &source->m_pollFD);
        g_source_destroy(&source->m_base);
        g_source_unref(&source->m_base);
    }
    
    gboolean EventSource::check() const
    {
        return m_pollFD.revents & G_IO_IN;
    }
    
    void EventSource::dispatch()
    {
        while (m_connection->processPendingEvent()) {};
    }
    
    int main()
    {
        std::unique_ptr<sdbus::IConnection> connection(createSessionBusConnection());
        std::unique_ptr<EventSource, void (*)(EventSource*)> source(EventSource::create(connection), &EventSource::destroy);
    
        GMainLoop *mainloop = g_main_loop_new(nullptr, false);
        g_main_loop_run(loop);
        g_main_loop_unref(loop);
    
        return 0;
    }