alsasink: don't use 100% CPU

The root cause is that alsa-lib is not thread safe for the same handle.
There are two threads in the gstreamer accessing alsa-lib not serilized.
The race condition happens when one thread holds the old framebuffer app_ptr
position in the kernel, another thread advances the framebuffer app_ptr.
when the former thread is scheduled to run again, it overwrites the app_ptr
to old value by copying from kernel.Thus,the app_ptr in the upper
alsa-lib(pcm_rate) become one period size more advanced than the lower
alsa-lib(pcm_hw & kernel).

gstreamer uses noblock and poll method to communicate with the alsa-lib.
The app_ptr unsync situation as described above makes the poll return immediately because
it concludes there is enough space for the ring-buffer via the low-level alsa-lib.
The write function returns immediately because it concludes there is not enough
space for the ring-buffer from the upper-level alsa-lib. Then the loop of poll
and write runs again and again until another period size is available for
ring-buffer.This leads to the cpu 100 problem.

delay_lock  is used to avoid the race condition.

Fixes: https://bugzilla.gnome.org/show_bug.cgi?id=690937
This commit is contained in:
yanghuolin 2012-11-15 03:31:47 -05:00 committed by Wim Taymans
parent 500b864899
commit 67a7b5a993
2 changed files with 11 additions and 0 deletions

View file

@ -118,6 +118,7 @@ gst_alsasink_finalise (GObject * object)
g_free (sink->device); g_free (sink->device);
g_mutex_clear (&sink->alsa_lock); g_mutex_clear (&sink->alsa_lock);
g_mutex_clear (&sink->delay_lock);
g_mutex_lock (&output_mutex); g_mutex_lock (&output_mutex);
--output_ref; --output_ref;
@ -255,6 +256,7 @@ gst_alsasink_init (GstAlsaSink * alsasink)
alsasink->handle = NULL; alsasink->handle = NULL;
alsasink->cached_caps = NULL; alsasink->cached_caps = NULL;
g_mutex_init (&alsasink->alsa_lock); g_mutex_init (&alsasink->alsa_lock);
g_mutex_init (&alsasink->delay_lock);
g_mutex_lock (&output_mutex); g_mutex_lock (&output_mutex);
if (output_ref == 0) { if (output_ref == 0) {
@ -1011,7 +1013,9 @@ gst_alsasink_write (GstAudioSink * asink, gpointer data, guint length)
if (err < 0) { if (err < 0) {
GST_DEBUG_OBJECT (asink, "wait error, %d", err); GST_DEBUG_OBJECT (asink, "wait error, %d", err);
} else { } else {
GST_DELAY_SINK_LOCK (asink);
err = snd_pcm_writei (alsa->handle, ptr, cptr); err = snd_pcm_writei (alsa->handle, ptr, cptr);
GST_DELAY_SINK_UNLOCK (asink);
} }
GST_DEBUG_OBJECT (asink, "written %d frames out of %d", err, cptr); GST_DEBUG_OBJECT (asink, "written %d frames out of %d", err, cptr);
@ -1057,7 +1061,9 @@ gst_alsasink_delay (GstAudioSink * asink)
alsa = GST_ALSA_SINK (asink); alsa = GST_ALSA_SINK (asink);
GST_DELAY_SINK_LOCK (asink);
res = snd_pcm_delay (alsa->handle, &delay); res = snd_pcm_delay (alsa->handle, &delay);
GST_DELAY_SINK_UNLOCK (asink);
if (G_UNLIKELY (res < 0)) { if (G_UNLIKELY (res < 0)) {
/* on errors, report 0 delay */ /* on errors, report 0 delay */
GST_DEBUG_OBJECT (alsa, "snd_pcm_delay returned %d", res); GST_DEBUG_OBJECT (alsa, "snd_pcm_delay returned %d", res);

View file

@ -43,6 +43,10 @@ typedef struct _GstAlsaSinkClass GstAlsaSinkClass;
#define GST_ALSA_SINK_LOCK(obj) (g_mutex_lock (GST_ALSA_SINK_GET_LOCK (obj))) #define GST_ALSA_SINK_LOCK(obj) (g_mutex_lock (GST_ALSA_SINK_GET_LOCK (obj)))
#define GST_ALSA_SINK_UNLOCK(obj) (g_mutex_unlock (GST_ALSA_SINK_GET_LOCK (obj))) #define GST_ALSA_SINK_UNLOCK(obj) (g_mutex_unlock (GST_ALSA_SINK_GET_LOCK (obj)))
#define GST_DELAY_SINK_GET_LOCK(obj) (&GST_ALSA_SINK_CAST (obj)->delay_lock)
#define GST_DELAY_SINK_LOCK(obj) (g_mutex_lock (GST_DELAY_SINK_GET_LOCK (obj)))
#define GST_DELAY_SINK_UNLOCK(obj) (g_mutex_unlock (GST_DELAY_SINK_GET_LOCK (obj)))
/** /**
* GstAlsaSink: * GstAlsaSink:
* *
@ -73,6 +77,7 @@ struct _GstAlsaSink {
GstCaps *cached_caps; GstCaps *cached_caps;
GMutex alsa_lock; GMutex alsa_lock;
GMutex delay_lock;
}; };
struct _GstAlsaSinkClass { struct _GstAlsaSinkClass {