ALSA: fix potential cleanup deadlock

The main ALSA poll now includes another file descriptor which we
write to when cleaning up, to ensure that the poll terminates
when soundio_outstream_destroy is invoked.

closes #36
This commit is contained in:
Andrew Kelley 2015-11-14 22:16:29 -07:00
parent 8f1e1b8752
commit f61acfd953
2 changed files with 35 additions and 7 deletions

View file

@ -36,6 +36,17 @@ static void wakeup_device_poll(struct SoundIoAlsa *sia) {
} }
} }
static void wakeup_outstream_poll(struct SoundIoOutStreamAlsa *osa) {
ssize_t amt = write(osa->poll_exit_pipe_fd[1], "a", 1);
if (amt == -1) {
assert(errno != EBADF);
assert(errno != EIO);
assert(errno != ENOSPC);
assert(errno != EPERM);
assert(errno != EPIPE);
}
}
static void destroy_alsa(struct SoundIoPrivate *si) { static void destroy_alsa(struct SoundIoPrivate *si) {
struct SoundIoAlsa *sia = &si->backend_data.alsa; struct SoundIoAlsa *sia = &si->backend_data.alsa;
@ -970,6 +981,7 @@ static void outstream_destroy_alsa(struct SoundIoPrivate *si, struct SoundIoOutS
if (osa->thread) { if (osa->thread) {
atomic_flag_clear(&osa->thread_exit_flag); atomic_flag_clear(&osa->thread_exit_flag);
wakeup_outstream_poll(osa);
soundio_os_thread_destroy(osa->thread); soundio_os_thread_destroy(osa->thread);
osa->thread = NULL; osa->thread = NULL;
} }
@ -1034,13 +1046,15 @@ static int outstream_wait_for_poll(struct SoundIoOutStreamPrivate *os) {
int err; int err;
unsigned short revents; unsigned short revents;
for (;;) { for (;;) {
if ((err = poll(osa->poll_fds, osa->poll_fd_count, -1)) < 0) { if ((err = poll(osa->poll_fds, osa->poll_fd_count_with_extra, -1)) < 0) {
return err; return SoundIoErrorStreaming;
} }
if (!atomic_flag_test_and_set(&osa->thread_exit_flag))
return SoundIoErrorInterrupted;
if ((err = snd_pcm_poll_descriptors_revents(osa->handle, if ((err = snd_pcm_poll_descriptors_revents(osa->handle,
osa->poll_fds, osa->poll_fd_count, &revents)) < 0) osa->poll_fds, osa->poll_fd_count, &revents)) < 0)
{ {
return err; return SoundIoErrorStreaming;
} }
if (revents & (POLLERR|POLLNVAL|POLLHUP)) { if (revents & (POLLERR|POLLNVAL|POLLHUP)) {
return 0; return 0;
@ -1113,10 +1127,10 @@ static void outstream_thread_run(void *arg) {
case SND_PCM_STATE_RUNNING: case SND_PCM_STATE_RUNNING:
case SND_PCM_STATE_PAUSED: case SND_PCM_STATE_PAUSED:
{ {
if ((err = outstream_wait_for_poll(os)) < 0) { if ((err = outstream_wait_for_poll(os))) {
if (!atomic_flag_test_and_set(&osa->thread_exit_flag)) if (err == SoundIoErrorInterrupted)
return; return;
outstream->error_callback(outstream, SoundIoErrorStreaming); outstream->error_callback(outstream, err);
return; return;
} }
if (!atomic_flag_test_and_set(&osa->thread_exit_flag)) if (!atomic_flag_test_and_set(&osa->thread_exit_flag))
@ -1379,7 +1393,8 @@ static int outstream_open_alsa(struct SoundIoPrivate *si, struct SoundIoOutStrea
return SoundIoErrorOpeningDevice; return SoundIoErrorOpeningDevice;
} }
osa->poll_fds = ALLOCATE(struct pollfd, osa->poll_fd_count); osa->poll_fd_count_with_extra = osa->poll_fd_count + 1;
osa->poll_fds = ALLOCATE(struct pollfd, osa->poll_fd_count_with_extra);
if (!osa->poll_fds) { if (!osa->poll_fds) {
outstream_destroy_alsa(si, os); outstream_destroy_alsa(si, os);
return SoundIoErrorNoMem; return SoundIoErrorNoMem;
@ -1390,6 +1405,17 @@ static int outstream_open_alsa(struct SoundIoPrivate *si, struct SoundIoOutStrea
return SoundIoErrorOpeningDevice; return SoundIoErrorOpeningDevice;
} }
struct pollfd *extra_fd = &osa->poll_fds[osa->poll_fd_count];
if (pipe2(osa->poll_exit_pipe_fd, O_NONBLOCK)) {
assert(errno != EFAULT);
assert(errno != EINVAL);
assert(errno == EMFILE || errno == ENFILE);
outstream_destroy_alsa(si, os);
return SoundIoErrorSystemResources;
}
extra_fd->fd = osa->poll_exit_pipe_fd[0];
extra_fd->events = POLLIN;
return 0; return 0;
} }

View file

@ -56,7 +56,9 @@ struct SoundIoOutStreamAlsa {
int sample_buffer_size; int sample_buffer_size;
char *sample_buffer; char *sample_buffer;
int poll_fd_count; int poll_fd_count;
int poll_fd_count_with_extra;
struct pollfd *poll_fds; struct pollfd *poll_fds;
int poll_exit_pipe_fd[2];
struct SoundIoOsThread *thread; struct SoundIoOsThread *thread;
atomic_flag thread_exit_flag; atomic_flag thread_exit_flag;
snd_pcm_uframes_t period_size; snd_pcm_uframes_t period_size;