Bug 62529 - pa_threaded_mainloop_stop() blocks on pa_mutex_lock(m->mutex)
Summary: pa_threaded_mainloop_stop() blocks on pa_mutex_lock(m->mutex)
Status: RESOLVED MOVED
Alias: None
Product: PulseAudio
Classification: Unclassified
Component: clients (show other bugs)
Version: unspecified
Hardware: ARM Linux (All)
: high major
Assignee: pulseaudio-bugs
QA Contact: pulseaudio-bugs
URL:
Whiteboard:
Keywords:
Depends on:
Blocks:
 
Reported: 2013-03-19 15:23 UTC by Yupeng Chang
Modified: 2018-07-30 10:27 UTC (History)
2 users (show)

See Also:
i915 platform:
i915 features:


Attachments

Description Yupeng Chang 2013-03-19 15:23:22 UTC
I recently switched my pulseaudio code to pulseaudio-3.0 and used pa_threaded_mainloop to use pulseaudio library in multi-threaded application.
I find in some cases, pa_threaded_mainloop_stop() will just block on pa_mutex_lock(m->mutex) and never quit.

I have read the source code of pa_threaded_mainloop_stop() pa_threaded_mainloop_run(), it seems that there is one possibility that makes this happen.
In pa_threaded_mainloop_run(), a thread is created and pa_mainloop_run is called and protected by pa_mutex_lock(m->mutex) and pa_mutex_unlock(m->mutex) in the thread;

in pa_threaded_mainloop_stop(), pa_mainloop_quit is also protected by pa_mutex_lock(m->mutex) and pa_mutex_unlock(m->mutex);

So, here is the problem, in pa_threaded_mainloop_stop(), m->mutex must be aquired before pa_mainloop_quit() can be called, and in pa_threaded_mainloop_run(), when pa_mainloop_run() exits, m->mutex can be unlocked. If pa_mainloop_run() is blocked, the only way to make pa_mainloop_run() exit is to call pa_mainloop_quit(), but pa_mainloop_quit() can only be called after pa_mainloop_run() is quit.

This is the deadlock.

I noticed that the mutex is created with flag RECURSIVE, which only makes this mutex can be locked recursively in the same thread. In my case, pa_threaded_mainloop_stop() is called from another thread, so the mutex still can block pa_threaded_mainloop_stop().

This is easy to reproduce, use pa_threaded_mainloop() to write a recording program, and start/stop repeatedly, sometimes pa_threaded_mainloop_stop() is deadlocked.
Comment 1 Tanu Kaskinen 2013-03-20 08:38:39 UTC
(In reply to comment #0)
> I recently switched my pulseaudio code to pulseaudio-3.0 and used
> pa_threaded_mainloop to use pulseaudio library in multi-threaded application.
> I find in some cases, pa_threaded_mainloop_stop() will just block on
> pa_mutex_lock(m->mutex) and never quit.
> 
> I have read the source code of pa_threaded_mainloop_stop()
> pa_threaded_mainloop_run(), it seems that there is one possibility that
> makes this happen.
> In pa_threaded_mainloop_run(), a thread is created and pa_mainloop_run is
> called and protected by pa_mutex_lock(m->mutex) and
> pa_mutex_unlock(m->mutex) in the thread;
> 
> in pa_threaded_mainloop_stop(), pa_mainloop_quit is also protected by
> pa_mutex_lock(m->mutex) and pa_mutex_unlock(m->mutex);
> 
> So, here is the problem, in pa_threaded_mainloop_stop(), m->mutex must be
> aquired before pa_mainloop_quit() can be called, and in
> pa_threaded_mainloop_run(),

I guess you mean pa_threaded_mainloop_start()?

> when pa_mainloop_run() exits, m->mutex can be
> unlocked. If pa_mainloop_run() is blocked, the only way to make
> pa_mainloop_run() exit is to call pa_mainloop_quit(), but pa_mainloop_quit()
> can only be called after pa_mainloop_run() is quit.
> 
> This is the deadlock.

I don't see the deadlock.

At the beginning we have one thread, let's call it the main thread. It doesn't initially hold the lock.

The main thread calls pa_threaded_mainloop_start(). That spawns a new thread, let's call it the helper thread. The main thread doesn't lock the mutex.

When the helper thread starts, it will lock the mutex and call pa_mainloop_run().

The main thread calls pa_threaded_mainloop_stop(). This causes the main thread to lock the mutex.

This is where your program deadlocks. Some other thread is holding the lock and won't release it. Is it the helper thread? No, because the helper thread will release the lock as soon as it enters polling. It will take the lock again when the polling ends, but there is no code that can block during the time the lock is held (except the callbacks that you provide, but that's then a bug in your code). So, the helper thread will always release the lock eventually, thus there's no deadlock.

Have you checked with a debugger what other threads are blocking on the mutex, and what they are doing? Is the helper thread perhaps stuck in one of your callbacks?
Comment 2 Arun Raghavan 2013-11-22 10:05:11 UTC
Adding to what Tanu said, the output of 'thread apply all bt' would likely help resolve where the deadlock is.
Comment 3 GitLab Migration User 2018-07-30 10:27:44 UTC
-- 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/pulseaudio/pulseaudio/issues/435.


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.