After an evaluation, GNOME has moved from Bugzilla to GitLab. Learn more about GitLab.
No new issues can be reported in GNOME Bugzilla anymore.
To report an issue in a GNOME project, go to GNOME GitLab.
Do not go to GNOME Gitlab for: Bluefish, Doxygen, GnuCash, GStreamer, java-gnome, LDTP, NetworkManager, Tomboy.
Bug 702230 - audioringbuffer: Don't access timestamps array if not acquired
audioringbuffer: Don't access timestamps array if not acquired
Status: RESOLVED FIXED
Product: GStreamer
Classification: Platform
Component: gst-plugins-base
git master
Other Linux
: Normal normal
: 1.3.1
Assigned To: GStreamer Maintainers
GStreamer Maintainers
Depends on:
Blocks:
 
 
Reported: 2013-06-14 10:12 UTC by Sebastian Rasmussen
Modified: 2013-10-01 20:21 UTC
See Also:
GNOME target: ---
GNOME version: ---


Attachments
Log with extra debug prints for audiobasesrc and ringbuffer. (32.66 KB, text/plain)
2013-06-14 10:12 UTC, Sebastian Rasmussen
  Details
audioringbuffer: check if acquired in set_timestamp (1.40 KB, patch)
2013-09-24 06:08 UTC, David Svensson Fors
committed Details | Review

Description Sebastian Rasmussen 2013-06-14 10:12:13 UTC
Created attachment 246788 [details]
Log with extra debug prints for audiobasesrc and ringbuffer.

The scenario is that we're trying to change a GstAlsaSrc PLAYING->READY.
When doing so I sometimes se the following error:

0:08:44.558564170 16779   0xd72030 ERROR             ringbuffer gst-plugins-base/gst-libs/gst/audio/gstaudioringbuffer.c:1990:gst_audio_ring_buffer_set_timestamp:<audiosrcringbuffer82> Could not store timestamp, no timestamps buffer

I need some help in determining the correct way of solving this, but first let
me explain my reasoning:

I put a breakpoint on the GST_ERROR in gdb and obtained the following backtraces

Thread 2 (Thread 3837)

  • #0 gst_audio_ring_buffer_set_timestamp
    at gst-plugins-base/gst-libs/gst/audio/gstaudioringbuffer.c line 1990
  • #1 audioringbuffer_thread_func
    at gst-plugins-base/gst-libs/gst/audio/gstaudiosrc.c line 254
  • #2 g_thread_proxy
    at glib/glib/gthread.c line 801
  • #3 start_thread
    at pthread_create.c line 310
  • #4 __thread_start
    from /lib/libc.so.6

Thread 1 (Thread 3645)

  • #0 pthread_join
    at pthread_join.c line 89
  • #1 g_system_thread_wait
    at glib/glib/gthread-posix.c line 1158
  • #2 g_thread_join
    at glib/glib/gthread.c line 966
  • #3 gst_audio_src_ring_buffer_release
    at gst-plugins-base/gst-libs/gst/audio/gstaudiosrc.c line 435
  • #4 gst_audio_ring_buffer_release
    at gst-plugins-base/gst-libs/gst/audio/gstaudioringbuffer.c line 678
  • #5 gst_audio_base_src_change_state
    at gst-plugins-base/gst-libs/gst/audio/gstaudiobasesrc.c line 1170
  • #6 gst_alsasrc_change_state
    at gst-plugins-base/ext/alsa/gstalsasrc.c line 257
  • #7 gst_element_change_state
    at gstreamer/gst/gstelement.c line 2605
  • #8 gst_element_set_state_func
    at gstreamer/gst/gstelement.c line 2561
  • #9 gst_bin_element_set_state
    at gstreamer/gst/gstbin.c line 2297
  • #10 gst_bin_change_state_func
    at gstreamer/gst/gstbin.c line 2599
  • #11 gst_pipeline_change_state
    at gstreamer/gst/gstpipeline.c line 471
  • #12 gst_element_change_state
    at gstreamer/gst/gstelement.c line 2605
  • #13 gst_element_change_state
    at gstreamer/gst/gstelement.c line 2649
  • #14 gst_element_set_state_func
    at gstreamer/gst/gstelement.c line 2561
  • #15 pipeline_info_unref
    at apps/monolith/monolith/src/cache.c line 342
  • #16 cache_info_free
    at apps/monolith/monolith/src/cache.c line 714
  • #17 pipeline_info_unref
    at apps/monolith/monolith/src/cache.c line 365
  • #18 cache_info_free
    at apps/monolith/monolith/src/cache.c line 714
  • #19 pipeline_info_unref
    at apps/monolith/monolith/src/cache.c line 365
  • #20 cache_info_free
    at apps/monolith/monolith/src/cache.c line 714
  • #21 weak_refs_notify
    at glib/gobject/gobject.c line 2464
  • #22 g_object_unref
    at glib/gobject/gobject.c line 2981
  • #23 gst_bin_remove_func
    at gstreamer/gst/gstbin.c line 1536
  • #24 gst_bin_dispose
    at gstreamer/gst/gstbin.c line 526
  • #25 g_object_unref
    at glib/gobject/gobject.c line 2981
  • #26 gst_rtsp_media_finalize
    at gst-rtsp-server-2e835cfd47c33f89863651debf07d42c432d97a9/gst-rtsp-server/gst/rtsp-server/rtsp-media.c line 255
  • #27 g_object_unref
    at glib/gobject/gobject.c line 3018
  • #28 gst_rtsp_client_finalize
    at gst-rtsp-server-2e835cfd47c33f89863651debf07d42c432d97a9/gst-rtsp-server/gst/rtsp-server/rtsp-client.c line 287
  • #29 g_object_unref
    at glib/gobject/gobject.c line 3018
  • #30 g_source_unref_internal
    at glib/glib/gmain.c line 1649
  • #31 g_main_dispatch
    at glib/glib/gmain.c line 2567
  • #32 g_main_context_dispatch
    at glib/glib/gmain.c line 3075
  • #33 g_main_context_iterate
    at glib/glib/gmain.c line 3146
  • #34 g_main_loop_run
    at glib/glib/gmain.c line 3340
  • #35 main
    at apps/monolith/monolith/src/main.c line 557

My analysis of what is happening is that by making GstAlsaSrc transition
PLAYING->READY then gst_audio_base_src_change_state() will be called once for
each transition PLAYING->PAUSED and PAUSED->READY:

gst_audio_base_src_change_state()
when going PLAYING->PAUSED calls
  gst_audio_ring_buffer_pause()
  which calls
    gst_audio_ring_buffer_pause_unlocked()
    which does not pause the sample-reading-thread audioringbuffer_thread_func() in gstaudiosrc.c
    because gstaudiosrc does not override the virtual pause member in GstAudioRingBuffer

gst_audio_base_src_change_state()
when going PAUSED->READY calls
  gst_audio_ring_buffer_release()
  which first calls
    gst_audio_ring_buffer_stop()
    which calls
      gst_audio_src_ring_buffer_stop()
      which does nothing with the sample-reading-thread audioringbuffer_thread_func()
  and then gst_audio_ring_buffer_release() frees buf->timestamps
  before calling
    gst_audio_src_ring_buffer_release()
    which for the sample-reading-thread audioringbuffer_thread_func() calls
      g_thread_join()

All this the sample-reading-thread is busy doing this:

audioringbuffer_thread_func()
runs a loop reading sample data into the ringbuffer and calls
  gst_audio_ring_buffer_set_timestamp()
  which makes use of buf->timestamps

The problem is that when changing states in GstAudioSrc there is nothing
telling the sample-reading-thread to stop working on the ringbuffer (or more
specifically the ringbufffers' timestamps array). There must be something in
the code path somewhere to tell that thread to using the array before it is
freed by gst_audio_ring_buffer_release().

There is some code in gst_audio_src_ring_buffer_stop() that Wim commented out
in commit 265a494 in gst-plugins-base in 2008
#if 0
  GST_DEBUG ("stop, waiting...");
  GST_AUDIO_SRC_RING_BUFFER_WAIT (buf);
  GST_DEBUG ("stoped");
#endif

With the following motivation:

+       * gst-libs/gst/audio/gstaudiosrc.c: (gst_audioringbuffer_stop):
+       Disable a code path that is now called but causes a deadlock for some
+       reason and is unneeded.

This leads me to the tentative conclusion that either GstAudioSrcRingBuffer
needs to implement GstAudioRingBuffer virtual pause() member or the WAIT Wim
commented out _is_ necessary to make the sample-reading-thread stop reading
samples from the device.

Which solution is correct?

I also obtained a more comprehensive log output by using
GST_DEBUG=2,audiobasesrc:6,ringbuffer:6, though I'm unsure if this provides any
relevant information. This is attached in log.txt
Comment 1 Sebastian Dröge (slomo) 2013-06-14 12:46:20 UTC
Wouldn't be enough to only free all these things (like the timestamp array) after shutting down the ringbuffer thread? Instead of the other way around?
Comment 2 David Svensson Fors 2013-09-24 06:08:09 UTC
Created attachment 255604 [details] [review]
audioringbuffer: check if acquired in set_timestamp

I can reproduce the GST_ERROR reported by Sebastian Rasmussen. As explained in his analysis, audioringbuffer_thread_func() in gstaudiosrc.c calls gst_audio_ring_buffer_set_timestamp() at a point where buf->timestamps has been set to NULL by gst_audio_ring_buffer_release() from another thread.

My suggestion is to report this case using a GST_DEBUG in gst_audio_ring_buffer_set_timestamp(), not as a GST_ERROR. The GstAudioRingBuffer has just been released by a concurrent thread, and to me it seems best to not require that audioringbuffer_thread_func, as a user of the GstAudioRingBuffer API, handles that case itself in a thread-safe way. It should be no problem for the GstAudioRingBuffer to not save the timestamp when the buffer has been released.

In the attached suggested patch the "acquired" flag is checked in gst_audio_ring_buffer_set_timestamp(), for handling the case where the buffer has been released. GST_OBJECT_LOCK is used for protecting object data.
Comment 3 Sebastian Dröge (slomo) 2013-10-01 20:20:27 UTC
commit 09d628f8f1a103a32b8c9d25581299f77480c4d5
Author: David Svensson Fors <davidsf@axis.com>
Date:   Mon Sep 23 11:35:43 2013 +0200

    audioringbuffer: check if acquired in set_timestamp
    
    Also use GST_OBJECT_LOCK when accessing object data in set_timestamp.
    
    https://bugzilla.gnome.org/show_bug.cgi?id=702230