ALSA: pcm - Simplify snd_pcm_drain() implementation

Simplify snd_pcm_drain() implementation and avoid unneeded array-
allocation for waitqueues.  Instead, one waitqueue is used for the
first draining stream, and wait until all streams finished.

Signed-off-by: Takashi Iwai <tiwai@suse.de>
This commit is contained in:
Takashi Iwai 2009-09-17 18:46:26 +02:00
parent 87bfa1dbfb
commit d3a7dcfeeb
1 changed files with 20 additions and 53 deletions

View File

@ -1387,11 +1387,6 @@ static struct action_ops snd_pcm_action_drain_init = {
.post_action = snd_pcm_post_drain_init
};
struct drain_rec {
struct snd_pcm_substream *substream;
wait_queue_t wait;
};
static int snd_pcm_drop(struct snd_pcm_substream *substream);
/*
@ -1407,10 +1402,9 @@ static int snd_pcm_drain(struct snd_pcm_substream *substream,
struct snd_card *card;
struct snd_pcm_runtime *runtime;
struct snd_pcm_substream *s;
wait_queue_t wait;
int result = 0;
int i, num_drecs;
int nonblock = 0;
struct drain_rec *drec, drec_tmp, *d;
card = substream->pcm->card;
runtime = substream->runtime;
@ -1433,38 +1427,10 @@ static int snd_pcm_drain(struct snd_pcm_substream *substream,
} else if (substream->f_flags & O_NONBLOCK)
nonblock = 1;
if (nonblock)
goto lock; /* no need to allocate waitqueues */
/* allocate temporary record for drain sync */
down_read(&snd_pcm_link_rwsem);
if (snd_pcm_stream_linked(substream)) {
drec = kmalloc(substream->group->count * sizeof(*drec), GFP_KERNEL);
if (! drec) {
up_read(&snd_pcm_link_rwsem);
snd_power_unlock(card);
return -ENOMEM;
}
} else
drec = &drec_tmp;
/* count only playback streams */
num_drecs = 0;
snd_pcm_group_for_each_entry(s, substream) {
runtime = s->runtime;
if (s->stream == SNDRV_PCM_STREAM_PLAYBACK) {
d = &drec[num_drecs++];
d->substream = s;
init_waitqueue_entry(&d->wait, current);
add_wait_queue(&runtime->sleep, &d->wait);
}
}
up_read(&snd_pcm_link_rwsem);
lock:
snd_pcm_stream_lock_irq(substream);
/* resume pause */
if (substream->runtime->status->state == SNDRV_PCM_STATE_PAUSED)
if (runtime->status->state == SNDRV_PCM_STATE_PAUSED)
snd_pcm_pause(substream, 0);
/* pre-start/stop - all running streams are changed to DRAINING state */
@ -1479,25 +1445,35 @@ static int snd_pcm_drain(struct snd_pcm_substream *substream,
for (;;) {
long tout;
struct snd_pcm_runtime *to_check;
if (signal_pending(current)) {
result = -ERESTARTSYS;
break;
}
/* all finished? */
for (i = 0; i < num_drecs; i++) {
runtime = drec[i].substream->runtime;
if (runtime->status->state == SNDRV_PCM_STATE_DRAINING)
/* find a substream to drain */
to_check = NULL;
snd_pcm_group_for_each_entry(s, substream) {
if (s->stream != SNDRV_PCM_STREAM_PLAYBACK)
continue;
runtime = s->runtime;
if (runtime->status->state == SNDRV_PCM_STATE_DRAINING) {
to_check = runtime;
break;
}
}
if (i == num_drecs)
break; /* yes, all drained */
if (!to_check)
break; /* all drained */
init_waitqueue_entry(&wait, current);
add_wait_queue(&to_check->sleep, &wait);
set_current_state(TASK_INTERRUPTIBLE);
snd_pcm_stream_unlock_irq(substream);
up_read(&snd_pcm_link_rwsem);
snd_power_unlock(card);
tout = schedule_timeout(10 * HZ);
snd_power_lock(card);
down_read(&snd_pcm_link_rwsem);
snd_pcm_stream_lock_irq(substream);
remove_wait_queue(&to_check->sleep, &wait);
if (tout == 0) {
if (substream->runtime->status->state == SNDRV_PCM_STATE_SUSPENDED)
result = -ESTRPIPE;
@ -1512,16 +1488,7 @@ static int snd_pcm_drain(struct snd_pcm_substream *substream,
unlock:
snd_pcm_stream_unlock_irq(substream);
if (!nonblock) {
for (i = 0; i < num_drecs; i++) {
d = &drec[i];
runtime = d->substream->runtime;
remove_wait_queue(&runtime->sleep, &d->wait);
}
if (drec != &drec_tmp)
kfree(drec);
}
up_read(&snd_pcm_link_rwsem);
snd_power_unlock(card);
return result;