From: Timo Wischer twischer@de.adit-jv.com
With this changes an IO plugin should not block by it self in the its own snd_pcm_drain() implementation. It should return -EAGAIN and inform the ALSA library via the poll_descriptors that draining is done.
In non-blocking mode this implementation expects that the user blocks on the poll_descriptors whenever snd_pcm_drain() returns -EAGAIN. In addition the user has to call snd_pcm_drain() again to check if draining is really done.
This change will not harm existing IO plugins which are blocking in its snd_pcm_drain() callback.
Signed-off-by: Timo Wischer twischer@de.adit-jv.com
diff --git a/include/pcm_ioplug.h b/include/pcm_ioplug.h index c1310e3..aa08962 100644 --- a/include/pcm_ioplug.h +++ b/include/pcm_ioplug.h @@ -169,6 +169,12 @@ struct snd_pcm_ioplug_callback { int (*prepare)(snd_pcm_ioplug_t *io); /** * drain; optional + * This function should never block. It should return -EAGAIN in case of + * it is not yet done. In this case it will be called again by the ALSA + * library (in blocking mode) or by the user (in non blocking mode). + * In case of -EAGAIN the poll_descriptors have to be used to inform the + * ALSA library that draining is possibly done and this callback has to + * be called again. */ int (*drain)(snd_pcm_ioplug_t *io); /** diff --git a/src/pcm/pcm_ioplug.c b/src/pcm/pcm_ioplug.c index 8c0ed48..7682dcd 100644 --- a/src/pcm/pcm_ioplug.c +++ b/src/pcm/pcm_ioplug.c @@ -492,17 +492,37 @@ static int snd_pcm_ioplug_drop(snd_pcm_t *pcm) static int snd_pcm_ioplug_drain(snd_pcm_t *pcm) { ioplug_priv_t *io = pcm->private_data; - int err; + int err = 0;
if (io->data->state == SND_PCM_STATE_OPEN) return -EBADFD;
io->data->state = SND_PCM_STATE_DRAINING; - if (io->data->callback->drain) - io->data->callback->drain(io->data); - snd_pcm_lock(pcm); - err = snd_pcm_ioplug_drop(pcm); - snd_pcm_unlock(pcm); + if (io->data->callback->drain) { + err = io->data->callback->drain(io->data); + + /* in blocking mode wait unti draining is done or + * an issue was detected. + */ + if (!io->data->nonblock) { + while (err == -EAGAIN) { + snd_pcm_lock(pcm); + err = snd_pcm_wait_nocheck(pcm, -1); + snd_pcm_unlock(pcm); + if (err < 0) + break; + err = io->data->callback->drain(io->data); + } + } + } + + /* only drop if draining is done or there was an issue */ + if (err != -EAGAIN) { + snd_pcm_lock(pcm); + err = snd_pcm_ioplug_drop(pcm); + snd_pcm_unlock(pcm); + } + return err; }