简体   繁体   中英

Role of Mainloops, Event Loops in DBus service

This is the standard example of DBus service.

import dbus
import gobject
from dbus import service
# from gi._gobject import MainLoop
from dbus.mainloop.glib import DBusGMainLoop

class DBusServer(service.Object):
    def __init__(self, name, object_path):
        # super(service.Object, self).__init__(name, object_path)
        dbus.service.Object.__init__(self, name, object_path)

    @dbus.service.method("com.test", in_signature='s', out_signature="s")
    def test(self, args):
        return args + " Sent by dbus client"

    @dbus.service.method("com.test", in_signature='s', out_signature="s")
    def foo(self, args):
        return "foo"

bus_loop = DBusGMainLoop(set_as_default=True)
session_bus = dbus.SessionBus()
session_name = service.BusName("com.test", session_bus)
dbus_server = DBusServer(session_name, "/test")

loop = gobject.MainLoop()

try:
    loop.run()
except KeyboardInterrupt:
    loop.quit()

I have questions regarding two mainloops used here
1. What is the role of each mainloop or event loop in this code (if I am using the correct terminology. They both are event loops I guess)
2. If my app is not a GUI app why should I need gobject mainloop or qt mainloop since that is required to catch user generated events from X11 library (in case of Linux)
3. Why can't I use an infinite loop which does nothing instead of gobject main loop (as follows)

while True:
    pass

Below are summarized short answers, see the details at the end for more explanations.

Question 1:

There is only one mainloop being used in the code you posted, the loop = gobject.MainLoop() . Its role is to process events, eg D-Bus messages sent to your service.

Question 2:

You need a mainloop in order for your code to not block D-Bus messages coming from outside your service. The dbus-python binding does not have a mainloop implementation so you need to use a mainloop from some other library (which also needs to be one supported by dbus-python , in this case GLib).

Question 3:

An infinite loop like that will never allow for any code other than that particular loop to execute, and there would be no way of receiving D-Bus messages (or any other events).

Details:

Simply put, a mainloop typically processes other events when there is no code currently running in your program. In this case, the nature of D-Bus requires there to be a way for your code to handle events from an external source, ie D-Bus messages. This is the reason that you need to have a mainloop. You could base the event processing on a loop you write yourself, as long as you have code in the loop that can process external events. The reason people often use libraries for mainloops is because things tend to grow complex in 'real' applications or systems and it's often more robust to use something which has been tested, used, and improved by others for years (eg GLib).

The D-Bus binding dbus-python is implemented in a way that requires an external mainloop to be used, as it does not have its own mainloop implementation. A typical reason for designing a library to use an external mainloop is to let the user of a library decide what mainloop to use, since the mainloop could potentially be used for other things as well (eg process other events than D-Bus events). This increases the potential use of the library and reduces the dependencies introduced as a side-effect of using the library.

The library must however support the mainloop of choice which means that there must be some integration provided specifically for each mainloop intended to be used with the library. Typically, the internals of a library like this would be designed to use a class/type that is an abstraction of a mainloop so the code internals doesn't need to know about what mainloop has been integrated. This reduces the amount of code which needs to be mainloop implementation specific within the library.

If you examine the bus_loop object received when doing:

bus_loop = DBusGMainLoop(set_as_default=True)

... you will see that it is an object of type dbus.mainloop.NativeMainLoop which is the mainloop wrapper object in dbus-python .

Now, dbus-python currently only provides an integration for the GLib mainloop so the discussion above becomes a bit theoretical in this case. The way dbus-python integrates a GLib mainloop is to require the user to import and instantiate the GLib mainloop wrapper, like this:

from dbus.mainloop.glib import DBusGMainLoop
DBusGMainLoop(set_as_default=True)

This creates a GLib main context which is later used by a GLib mainloop to process events that dbus-python associates with that context. The last part of the mainloop integration is to require the user to start a GLib mainloop. When you import and run the gobject mainloop, like this:

loop = gobject.MainLoop()
loop.run()

The mainloop will process events from the context created previously. This is the 'link' between the D-Bus binding dbus-python and the GObject binding gobject (which gives access to the GLib mainloop).

Simplified to the extreme, one could say that the context created and set when doing:

from dbus.mainloop.glib import DBusGMainLoop
DBusGMainLoop(set_as_default=True)

is a list of events to process if they appear, and the mainloop created and started when doing:

loop = gobject.MainLoop()
loop.run()

is what makes something check that list. The fact that GLib is used for both things makes it work, again simply put.

If you want to dig further into this, read up on GLib and the GMainLoop and GMainContext and the concepts related to these. Hope this helps.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM