Search code examples
python-3.xpython-3.4glibdbussystemd

How to listen to D-Bus events and the IPC channel at the same time?


I have the following simplyfied code. It listens to the D-Bus and does something when a new job is created. For that to work I need to start the GLib.MainLoop().run(), as it was presented by multiple examples I found.

While doing that, I want the program to continuously listen to the IPC bus and do something when a message is received. But obviously that doesn't work since my program is stuck at GLib.MainLoop().run().

How to implement something that let's me listen to the D-Bus and to the IPC at the same time?

#!/usr/bin/env python3.4
import asgi_ipc as asgi
from gi.repository import GLib
from pydbus import SystemBus
from systemd.daemon import notify as sd_notify

def main():
    bus = SystemBus()
    systemd = bus.get(".systemd1")
    systemd.onJobNew = do_something_with_job()

    channel_layer = asgi.IPCChannelLayer(prefix="endercp")

    # Notify systemd this unit is ready
    sd_notify("READY=1")

    GLib.MainLoop().run()

    while True:
        message = channel_layer.receive(["endercp"])
        if message is not (None, None):
            do_something_with_message(message)


if __name__ == "__main__":
    # Notify systemd this unit is starting
    sd_notify("STARTING=1")

    main()

    # Notify systemd this unit is stopping
    sd_notify("STOPPING=1")

Solution

  • Since IPCChannelLayer.receive() does not block, you can run it in an idle callback. Try this:

    callback_id = GLib.idle_add(GLib.PRIORITY_DEFAULT_IDLE, poll_channel, data=channel_layer)
    GLib.MainLoop().run()
    GLib.idle_remove_by_data(channel_layer)
    
    # ...
    
    def poll_channel(channel_layer):
        message = channel_layer.receive(["endercp"])
        if message is not (None, None):
            do_something_with_message(message)
        return GLib.SOURCE_CONTINUE