Bug 61328

Summary: PulseAudio Python Binding PyEval_CallFunction segfault in pa_mainloop_dispatch based on pa_threaded_mainloop
Product: PulseAudio Reporter: Leslie Zhai <xiangzhai83>
Component: clientsAssignee: pulseaudio-bugs
Status: RESOLVED NOTOURBUG QA Contact: pulseaudio-bugs
Severity: normal    
Priority: medium CC: lennart
Version: unspecified   
Hardware: x86-64 (AMD64)   
OS: Linux (All)   
Whiteboard:
i915 platform: i915 features:

Description Leslie Zhai 2013-02-23 06:24:40 UTC
Hi PulseAudio developers,

I am developing PulseAudio Python Binding in C based on pa_threaded_mainloop to handle pa_context_subscribe callback.

For example, when received PA_SUBSCRIPTION_EVENT_SINK PA_SUBSCRIPTION_EVENT_CHANGE event, I used PyEval_CallFunction to call Python (pygtk.py) callback.
Then pygtk.py testcase throw segfault, gdb DEBUG shown as below:

Program received signal SIGSEGV, Segmentation fault.
[Switching to Thread 0x7fffebb3f700 (LWP 10150)]
0x00000000004e9f20 in PyObject_Call ()
(gdb) bt
#0  0x00000000004e9f20 in PyObject_Call ()
#1  0x00000000004ea396 in PyEval_CallObjectWithKeywords ()
#2  0x0000000000478a99 in PyEval_CallFunction ()
#3  0x00007fffed1969b6 in ?? () from /usr/lib/x86_64-linux-gnu/libpulse.so.0
#4  0x00007fffecd45693 in ?? () from /usr/lib/x86_64-linux-gnu/libpulsecommon-1.1.so
#5  0x00007fffecd45a03 in pa_pdispatch_run () from /usr/lib/x86_64-linux-gnu/libpulsecommon-1.1.so
#6  0x00007fffed18cbdd in ?? () from /usr/lib/x86_64-linux-gnu/libpulse.so.0
#7  0x00007fffecd4a2a9 in ?? () from /usr/lib/x86_64-linux-gnu/libpulsecommon-1.1.so
#8  0x00007fffed19fa1e in pa_mainloop_dispatch () from /usr/lib/x86_64-linux-gnu/libpulse.so.0
#9  0x00007fffed19fde5 in pa_mainloop_iterate () from /usr/lib/x86_64-linux-gnu/libpulse.so.0
#10 0x00007fffed19fe90 in pa_mainloop_run () from /usr/lib/x86_64-linux-gnu/libpulse.so.0
#11 0x00007fffed1ae30f in ?? () from /usr/lib/x86_64-linux-gnu/libpulse.so.0
#12 0x00007fffecd57d18 in ?? () from /usr/lib/x86_64-linux-gnu/libpulsecommon-1.1.so
#13 0x00007ffff7bc4e9a in start_thread (arg=0x7fffebb3f700) at pthread_create.c:308
#14 0x00007ffff69b3cbd in clone () at ../sysdeps/unix/sysv/linux/x86_64/clone.S:112
#15 0x0000000000000000 in ?? ()

I tried to add PyGILState_Ensure() & PyGILState_Release() around PyEval_CallFunction() for locking/unlocking, it still throw another segfault shown as below:

Program received signal SIGSEGV, Segmentation fault.
[Switching to Thread 0x7fffebb3f700 (LWP 10362)]
sem_post () at ../nptl/sysdeps/unix/sysv/linux/x86_64/sem_post.S:34
(gdb) bt
#0  sem_post () at ../nptl/sysdeps/unix/sysv/linux/x86_64/sem_post.S:34
#1  0x00000000004d456e in PyGILState_Release ()
#2  0x00007fffed1969b6 in ?? () from /usr/lib/x86_64-linux-gnu/libpulse.so.0
#3  0x00007fffecd45693 in ?? () from /usr/lib/x86_64-linux-gnu/libpulsecommon-1.1.so
#4  0x00007fffecd45a03 in pa_pdispatch_run () from /usr/lib/x86_64-linux-gnu/libpulsecommon-1.1.so
#5  0x00007fffed18cbdd in ?? () from /usr/lib/x86_64-linux-gnu/libpulse.so.0
#6  0x00007fffecd4a2a9 in ?? () from /usr/lib/x86_64-linux-gnu/libpulsecommon-1.1.so
#7  0x00007fffed19fa1e in pa_mainloop_dispatch () from /usr/lib/x86_64-linux-gnu/libpulse.so.0
#8  0x00007fffed19fde5 in pa_mainloop_iterate () from /usr/lib/x86_64-linux-gnu/libpulse.so.0
#9  0x00007fffed19fe90 in pa_mainloop_run () from /usr/lib/x86_64-linux-gnu/libpulse.so.0
#10 0x00007fffed1ae30f in ?? () from /usr/lib/x86_64-linux-gnu/libpulse.so.0
#11 0x00007fffecd57d18 in ?? () from /usr/lib/x86_64-linux-gnu/libpulsecommon-1.1.so
#12 0x00007ffff7bc4e9a in start_thread (arg=0x7fffebb3f700) at pthread_create.c:308
#13 0x00007ffff69b3cbd in clone () at ../sysdeps/unix/sysv/linux/x86_64/clone.S:112
#14 0x0000000000000000 in ?? ()

However, the console.py (non-gtk & no g_main_loop) testcase worked happily without segfault, and I added cgtk.c (Gtk+-2.0 based testcase in C) worked correctly!

The source code and testcase host on github https://github.com/xiangzhai/pypulseaudio please someone give me advice, thanks a lot!
Comment 1 Tanu Kaskinen 2013-02-23 09:23:17 UTC
You could start by installing debug symbols (if your distro supports that) or rebuilding pulseaudio with debug symbols enabled.
Comment 2 Tanu Kaskinen 2013-02-23 09:29:05 UTC
Or actually the libpulse debug symbols don't seem to be necessary. The last calls in the stack are from your code, so the bug is most likely in your code, nothing pulseaudio related. You need to call PyEval_CallFunction() or PyGILState_Release with such parameters that they don't crash. I have zero knowledge about those functions, so you're on your own.

Resolving as "notourbug".
Comment 3 Leslie Zhai 2013-02-23 09:44:48 UTC
Thank Tanu`s reply :)

Yes pa_threaded_mainloop is working correctly in my cgtk.c and console.py testcases https://github.com/xiangzhai/pypulseaudio/blob/master/examples/cgtk.c

But the pygtk.py testcase is different from console.py, because there is already a GMainLoop in PyGtk (Gtk+-2.0 Python Binding), so is it possible that shared data from concurrent modifications by pa_threaded_mainloop && GMainLoop?

PyEval_CallFunction is Python C API, it acts like call function pointer, such like PyEval_CallFunction(py_callback, argv, ...)

PyGILState_Release is another Python C API, it acts like pthread_mutex_unlock, or pa_threaded_mainloop_unlock.

Anyway thanks a lot for your reply :)
Comment 4 Tanu Kaskinen 2013-02-23 10:06:22 UTC
(In reply to comment #3)
> Thank Tanu`s reply :)
> 
> Yes pa_threaded_mainloop is working correctly in my cgtk.c and console.py
> testcases
> https://github.com/xiangzhai/pypulseaudio/blob/master/examples/cgtk.c
> 
> But the pygtk.py testcase is different from console.py, because there is
> already a GMainLoop in PyGtk (Gtk+-2.0 Python Binding), so is it possible
> that shared data from concurrent modifications by pa_threaded_mainloop &&
> GMainLoop?

No, I'm pretty sure pa_threaded_mainloop and GMainloop do not access or modify any shared data.

The problem might be that PyEval_CallFunction() is called from the thread created by pa_threaded_mainloop. The GMainLoop runs in a different thread, so if PyEval_CallFunction() expects to be run in the GMainLoop thread, things will explode.

Are you sure pa_threaded_mainloop is the right choice? If you're using GMainLoop, you can use pa_glib_mainloop to integrate libpulse with the same GMainLoop that you're already using. If you specifically want to run the pulseaudio stuff in a separate thread, pa_threaded_mainloop can be used, but then you must not call PyEval_CallFunction() directly from the libpulse callbacks, but instead notify the GMainLoop thread about the events and call PyEval_CallFunction() from the GMainLoop thread.
Comment 5 Leslie Zhai 2013-02-23 10:44:41 UTC
Tanu Kaskinen you are genius! you FIXME :)

I use pa_glib_mainloop to handle the event signal for Python Binding instead of pa_threaded_mainloop https://github.com/xiangzhai/pypulseaudio/commit/7c9dd2ff7059b9e9521d1a2e93606670fd872d19

Thank you very much!

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.