Audio pthread_join is unforgiving

On older versions of Linux the audio can get a bit garbled and then
the sound cuts out completely. I didn’t have this trouble with SDL
1.2, but I’m not sure what changed. Unfortunately, after this happens
my game usually fails to quit. It hangs on the pthread_join waiting
for the audio device thread to quit. I’m guessing the real problem is
a buggy audio backend in Linux because this does not happen on any
newer distros that I’ve tried. But pthread_join still seems a bit
extreme. Would it be reasonable to just wait a short time for the
audio thread to quit and then move on regardless?–
Terry Welsh
www.reallyslick.com

A lot has changed in the audio code from 1.2 to 2.0. The public API is
still the same, but the majority of the implementation got a rewrite.

Can you break in the debugger and see where the audio thread hung up? It
might be a simple bug in SDL we can fix.

–ryan.On 08/27/2013 12:27 PM, Terry Welsh wrote:

On older versions of Linux the audio can get a bit garbled and then
the sound cuts out completely. I didn’t have this trouble with SDL
1.2, but I’m not sure what changed.

Can you break in the debugger and see where the audio thread hung up? It
might be a simple bug in SDL we can fix.

I should have also mentioned I often get lots of these message before a hang:

ALSA lib pcm.c:7316:(snd_pcm_recover) underrun occurred

Here are my stacks when it’s hung:
thread 1:
#0 0x0083f416 in __kernel_vsyscall ()
#1 0x0079fd5e in pthread_join () from /lib/libpthread.so.0
#2 0x0027bd28 in SDL_SYS_WaitThread (thread=0x867f8c0)
at /usr/local/src/SDL/src/thread/pthread/SDL_systhread.c:204
#3 0x002174ec in SDL_WaitThread (thread=0x867f8c0, status=0x0)
at /usr/local/src/SDL/src/thread/SDL_thread.c:398
#4 0x001be46c in close_audio_device (devid=1)
at /usr/local/src/SDL/src/audio/SDL_audio.c:759
#5 SDL_CloseAudioDevice (devid=1)
at /usr/local/src/SDL/src/audio/SDL_audio.c:1169
#6 0x001bf50e in SDL_CloseAudio ()
at /usr/local/src/SDL/src/audio/SDL_audio.c:1177
#7 0x00a1989a in Mix_CloseAudio () at mixer.c:1160
#8 0x08103085 in SoundManager::close (this=0x86a5a30) at SoundManager.cpp:65
#9 0x080d8a7f in main (argc=1, argv=0xbfbfe244) at main.cpp:746

thread 2:
#0 0x00429416 in __kernel_vsyscall ()
#1 0x00daec36 in poll () from /lib/libc.so.6
#2 0x0614dbe6 in ?? () from /lib/libasound.so.2
#3 0x0614ddb3 in snd_pcm_wait () from /lib/libasound.so.2
#4 0x06154240 in ?? () from /lib/libasound.so.2
#5 0x06197f97 in ?? () from /lib/libasound.so.2
#6 0x0614caf5 in snd_pcm_writei () from /lib/libasound.so.2
#7 0x00835744 in ALSA_PlayDevice (this=0x9bd1610)
at /usr/local/src/SDL/src/audio/alsa/SDL_alsa_audio.c:308
#8 0x00796dd0 in SDL_RunAudio (devicep=0x9bd1610)
at /usr/local/src/SDL/src/audio/SDL_audio.c:502
#9 0x007f0257 in SDL_RunThread (data=0x9bd18d0)
at /usr/local/src/SDL/src/thread/SDL_thread.c:276
#10 0x00854acd in RunThread (data=0x9bd18d0)
at /usr/local/src/SDL/src/thread/pthread/SDL_systhread.c:69
#11 0x00115e99 in start_thread () from /lib/libpthread.so.0
#12 0x00db9d2e in clone () from /lib/libc.so.6

thread 3:
#0 0x00429416 in __kernel_vsyscall ()
#1 0x00daec36 in poll () from /lib/libc.so.6
#2 0x05dbd063 in ?? () from /usr/lib/libpulse.so.0
#3 0x05dabe7a in pa_mainloop_poll () from /usr/lib/libpulse.so.0
#4 0x05dac655 in pa_mainloop_iterate () from /usr/lib/libpulse.so.0
#5 0x05dac734 in pa_mainloop_run () from /usr/lib/libpulse.so.0
#6 0x05dbd004 in ?? () from /usr/lib/libpulse.so.0
#7 0x05ee7973 in ?? () from /usr/lib/libpulsecommon-0.9.21.so
#8 0x00115e99 in start_thread () from /lib/libpthread.so.0
#9 0x00db9d2e in clone () from /lib/libc.so.6

thread 3:
#3 0x05dabe7a in pa_mainloop_poll () from /usr/lib/libpulse.so.0

So is this the ALSA driver that actually talks to PulseAudio, which then
talks to the real ALSA hardware? Why didn’t SDL choose PulseAudio directly?

Anyhow, I’ve attached a patch that attempts to workaround this problem.
The gist is the audio thread sets a variable to the current time each
iteration, and when we go to wait on that thread, we make sure it’s
updated it recently (and thus doesn’t appear to be locked up).

The problem is that if the app’s audio callback is where we actually
froze, if it eventually decides to return, the audio thread will be
touching freed memory. Maybe we just leave everything in place, and just
declare it a lost cause? I’m not really sure, and doing this feels
pretty icky in any case, so I’m just leaving the patch here.

–ryan.

-------------- next part --------------

HG changeset patch

User Ryan C. Gordon <@icculus>

Date 1377663709 14400

Node ID d41971701509215261a69e437e56ffbbbb21256c

Parent 7d3fff679faefa04b429fc788b3afe220672f26b

Don’t wait on the audio thread if it appears to be stuck.

diff --git a/src/audio/SDL_audio.c b/src/audio/SDL_audio.c
— a/src/audio/SDL_audio.c
+++ b/src/audio/SDL_audio.c
@@ -384,6 +384,8 @@
*/
while (device->enabled) {

  •        device->update_ticks = SDL_GetTicks();+
           if (device->paused) {
               SDL_Delay(delay);
               continue;
    

@@ -464,6 +466,8 @@
/* Loop, filling the audio buffers */
while (device->enabled) {

  •        device->update_ticks = SDL_GetTicks();
    
  •        /* Fill the current buffer with sound */
           if (device->convert.needed) {
               if (device->convert.buf) {
    

@@ -754,8 +758,21 @@
static void
close_audio_device(SDL_AudioDevice * device)
{

  • device->enabled = 0;
  • SDL_bool should_wait_thread = SDL_FALSE;
    if (device->thread != NULL) {

  •    if (!device->enabled) {  /* maybe device failed? */
    
  •        should_wait_thread = SDL_TRUE;  /* thread should have quit */
    
  •    } else {
    
  •        device->enabled = 0;  /* tell thread to clean up and quit. */
    
  •        /* if audio thread ignored update_ticks for more than 5 seconds,
    
  •           assume it's hung up and don't wait on it. Oh well. */
    
  •        if ((SDL_GetTicks() - device->update_ticks) < 5000) {
    
  •            should_wait_thread = SDL_TRUE;
    
  •        }
    
  •    }
    
  • }

  • if (should_wait_thread) {
    SDL_WaitThread(device->thread, NULL);
    }
    if (device->mixer_lock != NULL) {
    diff --git a/src/audio/SDL_sysaudio.h b/src/audio/SDL_sysaudio.h
    — a/src/audio/SDL_sysaudio.h
    +++ b/src/audio/SDL_sysaudio.h
    @@ -104,11 +104,14 @@
    SDL_AudioStreamer streamer;

    /* Current state flags */

  • volatile int enabled;
    int iscapture;

  • int enabled;
    int paused;
    int opened;
  • /* Set to SDL_GetTicks() at start of each audio thread loop iteration. */
  • volatile Uint32 update_ticks;
  • /* Fake audio buffer for when the audio hardware is busy */
    Uint8 *fake_stream;

2013/8/28 Ryan C. Gordon

thread 3:

#3 0x05dabe7a in pa_mainloop_poll () from /usr/lib/libpulse.so.0

So is this the ALSA driver that actually talks to PulseAudio, which then
talks to the real ALSA hardware? Why didn’t SDL choose PulseAudio directly?

Anyhow, I’ve attached a patch that attempts to workaround this problem.
The gist is the audio thread sets a variable to the current time each
iteration, and when we go to wait on that thread, we make sure it’s updated
it recently (and thus doesn’t appear to be locked up).

The problem is that if the app’s audio callback is where we actually
froze, if it eventually decides to return, the audio thread will be
touching freed memory. Maybe we just leave everything in place, and just
declare it a lost cause? I’m not really sure, and doing this feels pretty
icky in any case, so I’m just leaving the patch here.

–ryan.

On my Ubuntu 13.04 system, if I force the driver to ALSA, and run an
strace, I see it ends up reading from
/usr/share/alsa/alsa.conf.d//50-pulseaudio.conf, which creates a pcm
device, which uses the PulseAudio sound server, and from then on it starts
loading PulseAudio stuff, so it seems it’s a OS configuration thing.

If you don’t force a sound driver, SDL defaults to pulseaudio though.–
Gabriel.

thread 3:
#3 0x05dabe7a in pa_mainloop_poll () from /usr/lib/libpulse.so.0

So is this the ALSA driver that actually talks to PulseAudio, which then
talks to the real ALSA hardware? Why didn’t SDL choose PulseAudio directly?

Wish I could explain this stuff. I hear it takes mystical powers to
understand Linux audio.

Anyhow, I’ve attached a patch that attempts to workaround this problem.
The gist is the audio thread sets a variable to the current time each
iteration, and when we go to wait on that thread, we make sure it’s
updated it recently (and thus doesn’t appear to be locked up).

The problem is that if the app’s audio callback is where we actually
froze, if it eventually decides to return, the audio thread will be
touching freed memory. Maybe we just leave everything in place, and just
declare it a lost cause? I’m not really sure, and doing this feels
pretty icky in any case, so I’m just leaving the patch here.

–ryan.

Thank you for the patch. Unfortunately, my audio has been unusually
well-behaved the last few days so I been able to actually test this
patch. Usually my audio fails more often. My September is completely
booked and I’ll have almost zero programming time, but I’ll keep this
on my list and try to test it properly. Probably won’t happen for 3 or
4 weeks…

  • Terry

-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1

Would it be possible to return and not free the memory in such a case?

Alternatively, what about a specific flag/hint/option which allows the
application to specify whether SDL should wait for audio on quit
(possibly forever, at the risk of freezing) or not (risking a crash,
which might be the more desirable outcome on shutdown when all data
has already been saved).On 08/28/2013 06:27 AM, Ryan C. Gordon wrote:

thread 3: #3 0x05dabe7a in pa_mainloop_poll () from
/usr/lib/libpulse.so.0

So is this the ALSA driver that actually talks to PulseAudio, which
then talks to the real ALSA hardware? Why didn’t SDL choose
PulseAudio directly?

Anyhow, I’ve attached a patch that attempts to workaround this
problem. The gist is the audio thread sets a variable to the
current time each iteration, and when we go to wait on that thread,
we make sure it’s updated it recently (and thus doesn’t appear to
be locked up).

The problem is that if the app’s audio callback is where we
actually froze, if it eventually decides to return, the audio
thread will be touching freed memory. Maybe we just leave
everything in place, and just declare it a lost cause? I’m not
really sure, and doing this feels pretty icky in any case, so I’m
just leaving the patch here.

–ryan.

_______________________________________________ SDL mailing list
SDL at lists.libsdl.org
http://lists.libsdl.org/listinfo.cgi/sdl-libsdl.org

-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.4.14 (GNU/Linux)

iQIcBAEBAgAGBQJSIojtAAoJECgTcNPC9Cd0n1MP/RTnVw39FwSlkRHVZUWlgFKJ
Dn6tPL5PcAtNg6sgi/0KFp1Dw+Wk7PGFYC4yRVaoGPfrp95fUR8qtNm1EXsMf3dk
PZvy/u99rFJPeK6HoLdh6yeW/nbzLU88k9lIjbwLtaA3F7TfqeN9Vv9ycpTFwM6A
hqo0f1hPlYSVPXoBzvfXYwlRsbsUfeClC27W2KBIAGmRaf3+TNAq2ZtXcAwkxuja
Cg71aLxLvOruilYE1AZ/QfQK/wNIQDKl4vJObgyPobHjlxlkCGui4S4+tpodiuRI
B6RCQssOiUj9yIx0UwZKRzd2GMvxu7nO8kII2mgz91IBVtRq6W0vnWygOVSlW9kw
jdNAWT8Gh+vaMhHFAxJq/nRiAAFaOcKW9nKKV9DbEkyEImT8TR/D3TKTPpyYk02c
RCmYYIRMY5FE+0zOb2xzSg5ktob3g40eqyqj3U81Ixe+RyJN49tlV7RdmfXClgbF
JYytIfuI7ptX8bAabbVkLC0z8x06Jq+5HsPgOeZ36hhzmwlIGRPQHbHBq9w45yAb
uObx6mAJ8nk1ezD3lyUnz8Hq3hykxQnFi0CV1rPGMoXgoohW/f4G8rabIaCOiPtG
ifussJk5i0v9FE/KpX7xd62IhFv/siDfE6YimxD5Q5tqnx73HGgg9QLircJqClCZ
9m/5V1/NxzL2b85/hGWZ
=cDIf
-----END PGP SIGNATURE-----

I’m finally back to programming these days. Your patch appears to work
great. My audio failed 3 times this morning, but the game terminated
properly each time.–
Terry Welsh
www.reallyslick.com

On Fri, Aug 30, 2013 at 3:45 PM, Terry Welsh <@Terry_Welsh> wrote:

thread 3:
#3 0x05dabe7a in pa_mainloop_poll () from /usr/lib/libpulse.so.0

So is this the ALSA driver that actually talks to PulseAudio, which then
talks to the real ALSA hardware? Why didn’t SDL choose PulseAudio directly?

Wish I could explain this stuff. I hear it takes mystical powers to
understand Linux audio.

Anyhow, I’ve attached a patch that attempts to workaround this problem.
The gist is the audio thread sets a variable to the current time each
iteration, and when we go to wait on that thread, we make sure it’s
updated it recently (and thus doesn’t appear to be locked up).

The problem is that if the app’s audio callback is where we actually
froze, if it eventually decides to return, the audio thread will be
touching freed memory. Maybe we just leave everything in place, and just
declare it a lost cause? I’m not really sure, and doing this feels
pretty icky in any case, so I’m just leaving the patch here.

–ryan.

Thank you for the patch. Unfortunately, my audio has been unusually
well-behaved the last few days so I been able to actually test this
patch. Usually my audio fails more often. My September is completely
booked and I’ll have almost zero programming time, but I’ll keep this
on my list and try to test it properly. Probably won’t happen for 3 or
4 weeks…

  • Terry