[pygtk] PyGTK deadlocks... I know, right?

Christian Becke christianbecke at web.de
Mon Jan 14 17:46:54 WST 2013


Am 12.01.2013 10:24, schrieb Niklas Koep:
> Here's one. I'm using PyGTK 2.24 with gstreamer 0.10.36. Whenever gstreamer
> reports an error on my playbin2's message bus I display the error in a
> gtk.MessageDialog. This causes the app to deadlock unless I use a context
> manager with gtk.gdk.lock. At first I suspected a threading issue, but
> according to the gstreamer docs messages on the bus are emitted from the
> main thread so I don't see how threading could be an issue. Nevertheless, I
> verified this by retrieving the thread ids with libc via ctypes and they're
> identical. Here's a minimal example of an app that demonstrates the issue:
> http://pastebin.com/b4VTGy9q.
>
> Any ideas of an obvious issue I'm overlooking? I'd greatly appreciate any
> input. Cheers!

Calling gtk.Dialog.run() starts a second mainloop, which in a 
non-threaded application will block the original mainloop until it 
exits. This is useful for modal dialogs, but this is not what you want 
here. I don't know in detail what is happening in your example, but my 
guess would be that calling gtk.Dialog.run() also blocks the 
glib.MainLoop driving gstreamer, causing havoc once it continues.
Working example below.

Cheers,
Christian

#!/usr/bin/env python2

"""
$ python2 --version
Python 2.7.3

$ pkg-config pygtk-2.0 --modversion
2.24.0

$ pkg-config --modversion gstreamer-0.10
0.10.36
"""

import gst
import gtk
gtk.gdk.threads_init()
import gobject
gobject.threads_init()


def main():
     bin_ = gst.element_factory_make("playbin2")
     bus = bin_.get_bus()
     bus.add_signal_watch()
     bus.connect("message", on_message)

     window = gtk.Window(gtk.WINDOW_TOPLEVEL)
     window.set_size_request(300, 180)
     window.connect("delete-event", quit, bin_)

     vbox = gtk.VBox()
     window.add(vbox)

     button = gtk.Button("play")
     button.connect("clicked", on_clicked, bin_)
     vbox.pack_start(button, False)

     button = gtk.Button("message")
     button.connect("clicked", on_message_clicked, bin_)
     vbox.pack_start(button, False)

     window.show_all()

     gtk.main()

def quit(window, event, bin_):
     bin_.set_state(gst.STATE_NULL)
     gtk.main_quit()
     return False

def on_message_clicked (button, bin_):
    err = gst.GError ("test", gst.CORE_ERROR_FAILED, "test")
    msg = gst.message_new_error (bin_, err, "test error handling")
    bin_.post_message (msg)

def on_clicked(button, bin_):
     bin_.set_property("uri", "file:///tmp/test.mp3")
     bin_.set_state(gst.STATE_PLAYING)

def on_message(bus, message):
     if message.type == gst.MESSAGE_ERROR:
         err, debug = message.parse_error()

         # FIXME: without the context manager destroying the dialog causes a
         #        deadlock, but shouldn't this lock protection be redundant?
         #with gtk.gdk.lock: error_dialog("Error", "%s" % err)
         error_dialog("Error", "%s" % err)

def on_response(diag, res):
    diag.destroy()

def error_dialog(text, secondary_text=""):
     diag = gtk.MessageDialog(
             flags=gtk.DIALOG_DESTROY_WITH_PARENT|gtk.DIALOG_MODAL,
             type=gtk.MESSAGE_ERROR, buttons=gtk.BUTTONS_OK
     )
     diag.set_property("text", text)
     diag.set_property("secondary-text", secondary_text)
     diag.connect("response", on_response)
     diag.show()

if __name__ == "__main__":
     main()



More information about the pygtk mailing list