From e72acd799212f4681c005479b5fee980f0fc0c15 Mon Sep 17 00:00:00 2001 From: Lukas Rusak Date: Thu, 23 Jun 2022 10:37:59 -0700 Subject: [PATCH 1/2] audio_pw.c: use pthread_setcancelstate to protect the mainloop locking --- audio_pw.c | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/audio_pw.c b/audio_pw.c index 52e6a882..b9315cff 100644 --- a/audio_pw.c +++ b/audio_pw.c @@ -178,6 +178,8 @@ static void deinit() { pw_properties_free(data.props); data.props = NULL; } + + pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL); } static int init(__attribute__((unused)) int argc, __attribute__((unused)) char **argv) { @@ -230,6 +232,7 @@ static int init(__attribute__((unused)) int argc, __attribute__((unused)) char * die("pw: pw_properties_new() failed: %m"); } + pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, NULL); pw_thread_loop_lock(data.mainloop); if (pw_thread_loop_start(data.mainloop) != 0) { @@ -256,6 +259,7 @@ static int init(__attribute__((unused)) int argc, __attribute__((unused)) char * data.sync = pw_core_sync(data.core, 0, data.sync); pw_thread_loop_unlock(data.mainloop); + pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL); return 0; } @@ -336,6 +340,8 @@ static const char *spa_format_to_str(enum spa_audio_format audio_format) { static void start(int sample_rate, int sample_format) { + pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, NULL); + pw_thread_loop_lock(data.mainloop); const struct spa_pod *params[1]; @@ -410,22 +416,28 @@ static void start(int sample_rate, int sample_format) { } pw_thread_loop_unlock(data.mainloop); + + pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL); } static void stop() { + pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, NULL); pw_thread_loop_lock(data.mainloop); pw_stream_flush(data.stream, true); pw_thread_loop_unlock(data.mainloop); + pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL); } static void flush() { + pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, NULL); pw_thread_loop_lock(data.mainloop); pw_stream_flush(data.stream, false); pw_thread_loop_unlock(data.mainloop); + pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL); } static int play(void *buf, int samples) { @@ -433,6 +445,7 @@ static int play(void *buf, int samples) { struct spa_buffer *spa_buffer; struct spa_data *spa_data; + pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, NULL); pw_thread_loop_lock(data.mainloop); if (pw_stream_get_state(data.stream, NULL) == PW_STREAM_STATE_PAUSED) @@ -468,6 +481,8 @@ static int play(void *buf, int samples) { pw_thread_loop_unlock(data.mainloop); + pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL); + return 0; } From 8f7f85ffb1cf04b9d131aa311e0f41600ead8b6a Mon Sep 17 00:00:00 2001 From: Lukas Rusak Date: Thu, 23 Jun 2022 10:52:03 -0700 Subject: [PATCH 2/2] audio_pw.c: use pw_thread_loop_timed_wait_full to avoid lockups --- audio_pw.c | 26 ++++++++++++++++++++++++-- 1 file changed, 24 insertions(+), 2 deletions(-) diff --git a/audio_pw.c b/audio_pw.c index b9315cff..016df41d 100644 --- a/audio_pw.c +++ b/audio_pw.c @@ -35,6 +35,10 @@ #include +#define PW_TIMEOUT_S 5 +#define SECONDS_TO_NANOSECONDS 1000000000L +#define PW_TIMEOUT_NS (PW_TIMEOUT_S * SECONDS_TO_NANOSECONDS) + struct pw_data { struct pw_thread_loop *mainloop; struct pw_context *context; @@ -412,7 +416,15 @@ static void start(int sample_rate, int sample_format) { if (stream_state == PW_STREAM_STATE_PAUSED) break; - pw_thread_loop_wait(data.mainloop); + struct timespec abstime; + + pw_thread_loop_get_time(data.mainloop, &abstime, PW_TIMEOUT_NS); + + ret = pw_thread_loop_timed_wait_full(data.mainloop, &abstime); + if (ret == -ETIMEDOUT) { + deinit(); + die("pw: pw_thread_loop_timed_wait_full timed out: %s", strerror(ret)); + } } pw_thread_loop_unlock(data.mainloop); @@ -444,6 +456,7 @@ static int play(void *buf, int samples) { struct pw_buffer *pw_buffer; struct spa_buffer *spa_buffer; struct spa_data *spa_data; + int ret; pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, NULL); pw_thread_loop_lock(data.mainloop); @@ -456,7 +469,16 @@ static int play(void *buf, int samples) { if (pw_buffer) break; - pw_thread_loop_wait(data.mainloop); + struct timespec abstime; + + pw_thread_loop_get_time(data.mainloop, &abstime, PW_TIMEOUT_NS); + + ret = pw_thread_loop_timed_wait_full(data.mainloop, &abstime); + if (ret == -ETIMEDOUT) { + pw_thread_loop_unlock(data.mainloop); + pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL); + return ret; + } } spa_buffer = pw_buffer->buffer;