More fixes for 100% CPU hang with dmix

More fixes for 100% CPU hang-up problem with dmix.

- Clear timer queues properly when XRUN happens.
- Don't check XRUN when the stream isn't really running.
- Do hwsync properly for the delayed start state.
This commit is contained in:
Takashi Iwai 2005-04-05 15:50:15 +00:00
parent e5b91844bf
commit aa89f27de4
3 changed files with 43 additions and 17 deletions

View file

@ -415,13 +415,26 @@ int snd_pcm_direct_async(snd_pcm_t *pcm, int sig, pid_t pid)
return snd_timer_async(dmix->timer, sig, pid); return snd_timer_async(dmix->timer, sig, pid);
} }
/* empty the timer read queue */
static void snd_pcm_direct_clear_timer_queue(snd_pcm_direct_t *dmix)
{
/* rbuf might be overwriten by multiple plugins */
/* we don't need the value */
snd_timer_tread_t rbuf;
while (snd_timer_read(dmix->timer, &rbuf, sizeof(rbuf)) == sizeof(rbuf))
;
}
int snd_pcm_direct_timer_stop(snd_pcm_direct_t *dmix)
{
snd_timer_stop(dmix->timer);
snd_pcm_direct_clear_timer_queue(dmix);
}
int snd_pcm_direct_poll_revents(snd_pcm_t *pcm, struct pollfd *pfds, unsigned int nfds, unsigned short *revents) int snd_pcm_direct_poll_revents(snd_pcm_t *pcm, struct pollfd *pfds, unsigned int nfds, unsigned short *revents)
{ {
snd_pcm_direct_t *dmix = pcm->private_data; snd_pcm_direct_t *dmix = pcm->private_data;
unsigned short events; unsigned short events;
/* rbuf might be overwriten by multiple plugins */
/* we don't need the value */
static snd_timer_tread_t rbuf;
assert(pfds && nfds == 1 && revents); assert(pfds && nfds == 1 && revents);
events = pfds[0].revents; events = pfds[0].revents;
@ -435,8 +448,8 @@ int snd_pcm_direct_poll_revents(snd_pcm_t *pcm, struct pollfd *pfds, unsigned in
} else { } else {
empty = snd_pcm_mmap_capture_avail(pcm) < pcm->avail_min; empty = snd_pcm_mmap_capture_avail(pcm) < pcm->avail_min;
} }
/* empty the timer read queue */ if (empty)
while (empty && snd_timer_read(dmix->timer, &rbuf, sizeof(rbuf)) == sizeof(rbuf)) ; snd_pcm_direct_clear_timer_queue(dmix);
if (empty) if (empty)
events &= ~(POLLOUT|POLLIN); events &= ~(POLLOUT|POLLIN);
} }

View file

@ -144,6 +144,7 @@ int snd_pcm_direct_sw_params(snd_pcm_t *pcm, snd_pcm_sw_params_t * params);
int snd_pcm_direct_channel_info(snd_pcm_t *pcm, snd_pcm_channel_info_t * info); int snd_pcm_direct_channel_info(snd_pcm_t *pcm, snd_pcm_channel_info_t * info);
int snd_pcm_direct_mmap(snd_pcm_t *pcm); int snd_pcm_direct_mmap(snd_pcm_t *pcm);
int snd_pcm_direct_munmap(snd_pcm_t *pcm); int snd_pcm_direct_munmap(snd_pcm_t *pcm);
int snd_pcm_direct_timer_stop(snd_pcm_direct_t *dmix);
int snd_timer_async(snd_timer_t *timer, int sig, pid_t pid); int snd_timer_async(snd_timer_t *timer, int sig, pid_t pid);
struct timespec snd_pcm_hw_fast_tstamp(snd_pcm_t *pcm); struct timespec snd_pcm_hw_fast_tstamp(snd_pcm_t *pcm);

View file

@ -270,6 +270,10 @@ static int _snd_pcm_dmix_sync_ptr(snd_pcm_t *pcm, int do_slave_sync)
diff = slave_hw_ptr - old_slave_hw_ptr; diff = slave_hw_ptr - old_slave_hw_ptr;
if (diff == 0) /* fast path */ if (diff == 0) /* fast path */
return 0; return 0;
if (dmix->state != SNDRV_PCM_STATE_RUNNING &&
dmix->state != SNDRV_PCM_STATE_DRAINING)
/* not really started yet - don't update hw_ptr */
return 0;
if (diff < 0) { if (diff < 0) {
slave_hw_ptr += dmix->shmptr->s.boundary; slave_hw_ptr += dmix->shmptr->s.boundary;
diff = slave_hw_ptr - old_slave_hw_ptr; diff = slave_hw_ptr - old_slave_hw_ptr;
@ -280,7 +284,7 @@ static int _snd_pcm_dmix_sync_ptr(snd_pcm_t *pcm, int do_slave_sync)
return 0; return 0;
if ((avail = snd_pcm_mmap_playback_avail(pcm)) >= pcm->stop_threshold) { if ((avail = snd_pcm_mmap_playback_avail(pcm)) >= pcm->stop_threshold) {
struct timeval tv; struct timeval tv;
snd_timer_stop(dmix->timer); snd_pcm_direct_timer_stop(dmix);
gettimeofday(&tv, 0); gettimeofday(&tv, 0);
dmix->trigger_tstamp.tv_sec = tv.tv_sec; dmix->trigger_tstamp.tv_sec = tv.tv_sec;
dmix->trigger_tstamp.tv_nsec = tv.tv_usec * 1000L; dmix->trigger_tstamp.tv_nsec = tv.tv_usec * 1000L;
@ -419,6 +423,19 @@ static int snd_pcm_dmix_reset(snd_pcm_t *pcm)
return 0; return 0;
} }
static int snd_pcm_dmix_start_timer(snd_pcm_direct_t *dmix)
{
int err;
snd_pcm_hwsync(dmix->spcm);
dmix->slave_appl_ptr = dmix->slave_hw_ptr = *dmix->spcm->hw.ptr;
err = snd_timer_start(dmix->timer);
if (err < 0)
return err;
dmix->state = SND_PCM_STATE_RUNNING;
return 0;
}
static int snd_pcm_dmix_start(snd_pcm_t *pcm) static int snd_pcm_dmix_start(snd_pcm_t *pcm)
{ {
snd_pcm_direct_t *dmix = pcm->private_data; snd_pcm_direct_t *dmix = pcm->private_data;
@ -428,8 +445,6 @@ static int snd_pcm_dmix_start(snd_pcm_t *pcm)
if (dmix->state != SND_PCM_STATE_PREPARED) if (dmix->state != SND_PCM_STATE_PREPARED)
return -EBADFD; return -EBADFD;
snd_pcm_hwsync(dmix->spcm);
dmix->slave_appl_ptr = dmix->slave_hw_ptr = *dmix->spcm->hw.ptr;
avail = snd_pcm_mmap_playback_hw_avail(pcm); avail = snd_pcm_mmap_playback_hw_avail(pcm);
if (avail == 0) if (avail == 0)
dmix->state = STATE_RUN_PENDING; dmix->state = STATE_RUN_PENDING;
@ -438,10 +453,8 @@ static int snd_pcm_dmix_start(snd_pcm_t *pcm)
else { else {
if (avail > (snd_pcm_sframes_t)pcm->buffer_size) if (avail > (snd_pcm_sframes_t)pcm->buffer_size)
avail = pcm->buffer_size; avail = pcm->buffer_size;
err = snd_timer_start(dmix->timer); if ((err = snd_pcm_dmix_start_timer(dmix)) < 0)
if (err < 0)
return err; return err;
dmix->state = SND_PCM_STATE_RUNNING;
snd_pcm_dmix_sync_area(pcm, avail); snd_pcm_dmix_sync_area(pcm, avail);
} }
gettimeofday(&tv, 0); gettimeofday(&tv, 0);
@ -455,7 +468,7 @@ static int snd_pcm_dmix_drop(snd_pcm_t *pcm)
snd_pcm_direct_t *dmix = pcm->private_data; snd_pcm_direct_t *dmix = pcm->private_data;
if (dmix->state == SND_PCM_STATE_OPEN) if (dmix->state == SND_PCM_STATE_OPEN)
return -EBADFD; return -EBADFD;
snd_timer_stop(dmix->timer); snd_pcm_direct_timer_stop(dmix);
dmix->state = SND_PCM_STATE_SETUP; dmix->state = SND_PCM_STATE_SETUP;
return 0; return 0;
} }
@ -491,7 +504,7 @@ static int snd_pcm_dmix_pause(snd_pcm_t *pcm, int enable)
snd_pcm_direct_t *dmix = pcm->private_data; snd_pcm_direct_t *dmix = pcm->private_data;
if (enable) { if (enable) {
if (dmix->state == SND_PCM_STATE_RUNNING) if (dmix->state == SND_PCM_STATE_RUNNING)
snd_timer_stop(dmix->timer); snd_pcm_direct_timer_stop(dmix);
else if (dmix->state != STATE_RUN_PENDING) else if (dmix->state != STATE_RUN_PENDING)
return -EBADFD; return -EBADFD;
dmix->state = SND_PCM_STATE_PAUSED; dmix->state = SND_PCM_STATE_PAUSED;
@ -500,6 +513,7 @@ static int snd_pcm_dmix_pause(snd_pcm_t *pcm, int enable)
return -EBADFD; return -EBADFD;
if (snd_pcm_mmap_playback_hw_avail(pcm) > 0) { if (snd_pcm_mmap_playback_hw_avail(pcm) > 0) {
dmix->state = SND_PCM_STATE_RUNNING; dmix->state = SND_PCM_STATE_RUNNING;
/* FIXME: sync the hwptr */
snd_timer_start(dmix->timer); snd_timer_start(dmix->timer);
} else } else
dmix->state = STATE_RUN_PENDING; dmix->state = STATE_RUN_PENDING;
@ -589,13 +603,11 @@ static snd_pcm_sframes_t snd_pcm_dmix_mmap_commit(snd_pcm_t *pcm,
default: default:
break; break;
} }
snd_pcm_mmap_appl_forward(pcm, size);
if (dmix->state == STATE_RUN_PENDING) { if (dmix->state == STATE_RUN_PENDING) {
err = snd_timer_start(dmix->timer); if ((err = snd_pcm_dmix_start_timer(dmix)) < 0)
if (err < 0)
return err; return err;
dmix->state = SND_PCM_STATE_RUNNING;
} }
snd_pcm_mmap_appl_forward(pcm, size);
if (dmix->state == SND_PCM_STATE_RUNNING) { if (dmix->state == SND_PCM_STATE_RUNNING) {
err = snd_pcm_dmix_sync_ptr(pcm); err = snd_pcm_dmix_sync_ptr(pcm);
if (err < 0) if (err < 0)