1 From 23a1f554565c0cb9fade1a8f86ab9a09bf3d04e7 Mon Sep 17 00:00:00 2001
2 From: Thierry Bultel <thierry.bultel@iot.bzh>
3 Date: Wed, 26 Dec 2018 10:06:38 +0100
4 Subject: [PATCH] Fixed the SIGSEGV at PCM close
6 There is a (still not identified) race condition at PCM
7 close, producing a SIGSEGV when pthread_cancel is called.
8 Instead of proposing a trick with pthread_setcancelstate,
9 this fix introduces a cancellation pipe, on which the io_thread
10 polls. This guaranties a better, safer, exit path.
12 Signed-off-by: Thierry Bultel <thierry.bultel@iot.bzh>
14 src/asound/bluealsa-pcm.c | 82 +++++++++++++++++++++++++++++++++++++++++------
15 1 file changed, 72 insertions(+), 10 deletions(-)
17 diff --git a/src/asound/bluealsa-pcm.c b/src/asound/bluealsa-pcm.c
18 index 2136964..013aebb 100644
19 --- a/src/asound/bluealsa-pcm.c
20 +++ b/src/asound/bluealsa-pcm.c
21 @@ -67,6 +67,8 @@ struct bluealsa_pcm {
22 snd_pcm_uframes_t io_hw_boundary;
23 snd_pcm_uframes_t io_hw_ptr;
30 @@ -107,7 +109,9 @@ static void *io_thread(void *arg) {
32 asrsync_init(&asrs, io->rate);
34 - debug("Starting IO loop");
35 + const char * type = io->stream== SND_PCM_STREAM_CAPTURE?"CAPTURE":"PLAYBACK";
37 + debug("------------ Starting IO loop (%s)-----------------------", type);
41 @@ -124,6 +128,11 @@ static void *io_thread(void *arg) {
42 debug("IO thread resumed: %d", io->state);
45 + if (pcm->io_started == false) {
46 + debug("io_thread not started -> exit");
50 snd_pcm_uframes_t io_ptr = pcm->io_ptr;
51 snd_pcm_uframes_t io_buffer_size = io->buffer_size;
52 snd_pcm_uframes_t io_hw_ptr = pcm->io_hw_ptr;
53 @@ -152,21 +161,44 @@ static void *io_thread(void *arg) {
54 if (io_hw_ptr >= io_hw_boundary)
55 io_hw_ptr -= io_hw_boundary;
57 + struct pollfd fds[2];
59 + fds[0].fd = pcm->pcm_fd;
60 + fds[1].fd = pcm->cancelPipe[0];
62 + if (io->stream == SND_PCM_STREAM_CAPTURE)
63 + fds[0].events |= POLLIN;
65 + fds[0].events |= POLLOUT;
67 + fds[1].events = POLLIN | POLLHUP;
69 if (io->stream == SND_PCM_STREAM_CAPTURE) {
71 /* Read the whole period "atomically". This will assure, that frames
72 * are not fragmented, so the pointer can be correctly updated. */
73 - while (len != 0 && (ret = read(pcm->pcm_fd, head, len)) != 0) {
77 + int pollres = poll(fds, 2, -1);
79 + if ((fds[1].revents & POLLIN) != 0) {
80 + debug("io_thread: CANCELLED");
84 + ret = read(pcm->pcm_fd, head, len);
88 + if (errno == EINTR) {
91 SNDERR("PCM FIFO read error: %s", strerror(errno));
102 @@ -182,9 +214,18 @@ static void *io_thread(void *arg) {
104 /* Perform atomic write - see the explanation above. */
107 + int pollres = poll(fds, 2, -1);
109 + if ((fds[1].revents & POLLIN) != 0) {
110 + debug("io_thread: CANCELLED");
114 if ((ret = write(pcm->pcm_fd, head, len)) == -1) {
115 - if (errno == EINTR)
117 + if (errno == EINTR) {
120 SNDERR("PCM FIFO write error: %s", strerror(errno));
123 @@ -251,12 +292,24 @@ static int bluealsa_start(snd_pcm_ioplug_t *io) {
125 static int bluealsa_stop(snd_pcm_ioplug_t *io) {
126 struct bluealsa_pcm *pcm = io->private_data;
128 + debug("Stopping %p", pcm);
130 + debug("PCM ALREADY CLOSED !");
133 if (pcm->io_started) {
134 pcm->io_started = false;
135 - pthread_cancel(pcm->io_thread);
139 + if (write(pcm->cancelPipe[1], &dummy, sizeof(dummy)) < 0)
140 + debug("Failed to write to the cancellation pipe");
142 pthread_join(pcm->io_thread, NULL);
145 + debug("Stop done");
150 @@ -269,10 +322,14 @@ static snd_pcm_sframes_t bluealsa_pointer(snd_pcm_ioplug_t *io) {
152 static int bluealsa_close(snd_pcm_ioplug_t *io) {
153 struct bluealsa_pcm *pcm = io->private_data;
154 - debug("Closing plugin");
155 + debug("Closing plugin %p", pcm);
157 close(pcm->event_fd);
158 + close(pcm->cancelPipe[0]);
159 + close(pcm->cancelPipe[1]);
161 + io->private_data = NULL;
162 + debug("PCM closed");
166 @@ -649,6 +706,11 @@ SND_PCM_PLUGIN_DEFINE_FUNC(bluealsa) {
168 pcm->delay_ex = delay;
170 + if (pipe(pcm->cancelPipe) == -1) {
171 + SNDERR("BlueALSA failed to create cancellation pipe: %s", strerror(errno));
175 if ((pcm->fd = bluealsa_open(interface)) == -1) {
176 SNDERR("BlueALSA connection failed: %s", strerror(errno));