]> git.ipfire.org Git - thirdparty/shairport-sync.git/commitdiff
avoid recursive mutex acquisition in sndio backend 1362/head
authorAnton Lindqvist <anton@basename.se>
Fri, 3 Dec 2021 19:45:51 +0000 (20:45 +0100)
committerAnton Lindqvist <anton@basename.se>
Sun, 5 Dec 2021 08:08:01 +0000 (09:08 +0100)
Ending a RTSP session while running on OpenBSD using sndio backend causes the
following crash:

#0  thrkill ()
#1  0x000005208224403e in _libc_abort
#2  0x00000520821b77be in _rthread_mutex_trylock
#3  _rthread_mutex_timedlock
#4  0x0000051e0d54e2c0 in stop ()
#5  0x0000051e0d544e85 in player_thread_cleanup_handler
#6  0x0000052082243126 in _libc_pthread_exit
#7  0x000005209a158700 in sigthr_handler
#8  <signal handler called>
#9  _thread_sys_poll ()
#10 0x000005208223533e in _libc_poll_cancel
#11 0x00000520df54c9a0 in sio_psleep
#12 0x00000520df54cc1f in sio_write
#13 0x0000051e0d54e27a in play
#14 0x0000051e0d547fc0 in player_thread_func
#15 0x000005209a158cc1 in _rthread_start
#16 0x000005208223565a in __tfork_thread

The player thread is blocking inside sio_write() -> poll(2) while the thread is
being terminated. The stop routine tied to the same backend is invoked through
player_thread_cleanup_handler() which tries to acquire the mutex which it
already acquired before invoking sio_write(). Avoiding blocking writes would
require switching to async I/O which is quite an undertaking.

The fact that there's only one `struct sio_hdl *' instance in the compilation
unit sort of implies there can only be one player thread at a time. Therefore
fix the crash by only trying to acquire the mutex and continue as usual if it's
already acquired.

audio_sndio.c

index f8582c8abb06b55143505fc0a23d6e9be2214f95..6ada40adcdc6e965c5e8b8541d7e8d14a4dbb50e 100644 (file)
@@ -239,11 +239,19 @@ static int play(void *buf, int frames) {
 }
 
 static void stop() {
-  pthread_mutex_lock(&sndio_mutex);
+  int gotlock = 1;
+
+  // The player thread could already be waiting in sio_write() during
+  // termination implying that the same thread already have acquired the mutex.
+  if (pthread_mutex_trylock(&sndio_mutex))
+    gotlock = 0;
+
   if (!sio_stop(hdl))
     die("sndio: unable to stop");
   written = played = 0;
-  pthread_mutex_unlock(&sndio_mutex);
+
+  if (gotlock)
+    pthread_mutex_unlock(&sndio_mutex);
 }
 
 static void onmove_cb(__attribute__((unused)) void *arg, int delta) {