Created attachment 17629 [details] reproducer I have a D-BUS service (Jockey, https://launchpad.net/jockey in my case) with some methods which require to consult PackageKit. This is a bit tricky, since PackageKit is fully asynchrous and only delivers actual results over D-BUS signals. Thus, for actually delivering an answer, my D-BUS service method has to do the PackageKit call, set up a private gobject.MainLoop() and a signal handler (for "Finished"), and have that signal handler quit the main loop. The problem is that while the inner main loop is running, signals are never received. They are transported over the bus (they appear in dbus-monitor), but my program never catches them. Interestingly, when I press Control-C, the inner MainLoop reacts, and all signals are flushed and their handlers called. I don't think that using two main loops is the problem. I tried re-using the MainLoop of the D-BUS service itself, and used a "while not flag_set_by_signal_handler: do a main loop iteration" loop, with the same result: the iteration just blocks forever. I wrote a small reproducer to illustrate the problem, based on the official D-BUS python examples. It's best to use three consoles: con1$ python dbus-hang.py -> run the server con2$ dbus-send --dest=com.example.SampleService --type=signal /SomeObject com.example.SampleInterface.Poke -> this signal is properly caught, and the signal handler triggers; this happens while no service method is running, i. e. in the outside main loop. con3$ dbus-send --dest=com.example.SampleService --print-reply /SomeObject com.example.SampleInterface.HelloWorld string:moo -> call HelloWorld(), which will wait for a "Poke" signal in order to finish con2$ dbus-send --dest=com.example.SampleService --type=signal /SomeObject com.example.SampleInterface.Poke -> Signal is sent, but never received by dbus-hang.py; press Control-C, and it is received This only seems to affect signals. I can call D-BUS methods just fine while being in a D-BUS service method (e. g. I can get a PackageKit transaction ID, or query PolicyKit). If this is not the right way to do it, I'd appreciate some guidance how to solve this problem differently. I have wrapped my head around this problem for days, and was just banging my head on the table. Many thanks in advance for any help! Martin
In case it is important: dbus-python 0.82.4 dbus 1.2.1 Ubuntu Intrepid (development trunk) tested on i386 and x86_64 (on the latter it is not even Control-C'able)
I also had a look into libpackagekit. When you use it in synchronous mode, it uses exactly the same approach: set up a private main loop, run() it until the Finished signal arrives (which quits the main loop). So I hope I'm not completely on crack here. :-)
This is a bug in dbus-glib, <https://bugs.freedesktop.org/show_bug.cgi?id=14581>. We've worked around it in telepathy-glib, but I hadn't realised it would affect dbus-python too. In your case, however, you don't need to recurse the main loop. Implement your service methods using the asynchronous API (see examples/example-async-service.py). Your method will be passed some callbacks (well, closures really) as arguments. Save them somewhere and start the PK call, then return nothing from the method implementation; then when PK gives you a result back, call the success or error callback you previously saved.
Thank you, that's helpful! Indeed the 'asynchronous return' option sounds like being ideal here. Now I only need to actually find example-async-server.py, which unfortunately isn't on http://gitweb.freedesktop.org/?p=dbus/dbus-python.git;a=tree;h=851389d6cb22969f5b0579742994813e98d5ce1c;hb=dff98456995c37d964eb32a7de7ca718fc3d48d7;f=examples nor in the release tarball, nor anywhere in Google...
Hm, on second look that would be pretty inconvenient, since it means that I need to use two completely different implementations depending on whether I use the PackageKitClient object on the client or server side, and I have to rip apart the entire implementation of the server side. I'll follow up in bug 14581, though, that makes more sense.
Ah, please disregard my last comment. I discovered how to use the async_callbacks decorator keyword now, and changing my attached test case to use it works perfectly. Thank you for the hint!
Argh, no, that doesn't work. Comment 5 fully applies, of course. I want to keep the packaging bits in a library, I can't just copy&paste it around directly into the D-BUS service methods (there is a lot of other code in between the call and the layer where it touches packages). So while the async_callbacks interface is certainly elegant, it is mostly useless for using in libraries which have to work in both D-BUS service backends as well as "normal" programs.
Simon, thanks for our discussion here at the LPC. I just tried the hack you suggested with idle_add() and indeed that works as well. However, just like using an async implementation (which is much cleaner, too), its principal problem is that it only works when the function is actually called through D-BUS, and not directly in a normal program. So it seems that as long as we have that bug, there cannot be a general PackageKit client which can be used both in normal programs and in D-BUS backends.
I still don't think recursing into the main loop is a good solution for asynchronous operations (you had a problem, you solved it by recursing into the main loop, now you have two problems :-) but fixing Bug #14581 might be sufficient to fix this too.
-- GitLab Migration Automatic Message -- This bug has been migrated to freedesktop.org's GitLab instance and has been closed from further activity. You can subscribe and participate further through the new bug through this link to our GitLab instance: https://gitlab.freedesktop.org/dbus/dbus-python/issues/20.
Use of freedesktop.org services, including Bugzilla, is subject to our Code of Conduct. How we collect and use information is described in our Privacy Policy.