for alsa, POLL{IN,OUT} events mean that a block can be read or

written (not a byte as unix files expect). Use "tickets" counters that
are reset to hdl->par.round on each POLL{IN,OUT} event. Limit read()
and write() to one block to ensure poll is always called at block
boundaries.
This commit is contained in:
Alexandre Ratchov 2014-05-05 19:25:15 +02:00
parent 0441c58c62
commit 0e0511c762
1 changed files with 69 additions and 35 deletions

View File

@ -54,6 +54,7 @@ struct sio_alsa_hdl {
int running;
int events;
int ipartial, opartial;
int canread, canwrite;
char *itmpbuf, *otmpbuf;
};
@ -379,6 +380,8 @@ sio_alsa_start(struct sio_hdl *sh)
hdl->infds = 0;
hdl->onfds = 0;
hdl->running = 0;
hdl->canread = 0;
hdl->canwrite = 0;
if (hdl->sio.mode & SIO_PLAY) {
err = snd_pcm_prepare(hdl->opcm);
@ -932,7 +935,12 @@ sio_alsa_read(struct sio_hdl *sh, void *buf, size_t len)
len = hdl->ibpf;
}
}
while ((n = snd_pcm_readi(hdl->ipcm, buf, len / hdl->ibpf)) < 0) {
todo = len / hdl->ibpf;
if (todo > hdl->canread)
todo = hdl->canread;
if (todo == 0)
return 0;
while ((n = snd_pcm_readi(hdl->ipcm, buf, todo)) < 0) {
if (n == -EINTR)
continue;
if (n == -EPIPE || n == -ESTRPIPE) {
@ -950,6 +958,7 @@ sio_alsa_read(struct sio_hdl *sh, void *buf, size_t len)
hdl->sio.eof = 1;
return 0;
}
hdl->canread -= n;
hdl->idelta += n;
if (buf == hdl->itmpbuf) {
hdl->ipartial = hdl->ibpf;
@ -977,7 +986,12 @@ sio_alsa_write(struct sio_hdl *sh, const void *buf, size_t len)
len = hdl->obpf;
buf = hdl->otmpbuf;
}
while ((n = snd_pcm_writei(hdl->opcm, buf, len / hdl->obpf)) < 0) {
todo = len / hdl->obpf;
if (todo > hdl->canwrite)
todo = hdl->canwrite;
if (todo == 0)
return 0;
while ((n = snd_pcm_writei(hdl->opcm, buf, todo)) < 0) {
if (n == -EINTR)
continue;
if (n == -ESTRPIPE || n == -EPIPE) {
@ -990,6 +1004,7 @@ sio_alsa_write(struct sio_hdl *sh, const void *buf, size_t len)
}
return 0;
}
hdl->canwrite -= n;
hdl->odelta += n;
if (buf == hdl->otmpbuf) {
if (n > 0)
@ -1054,32 +1069,37 @@ sio_alsa_pollfd(struct sio_hdl *sh, struct pollfd *pfd, int events)
if (!hdl->sio.started)
hdl->events = 0;
memset(pfd, 0, sizeof(struct pollfd) * hdl->nfds);
hdl->onfds = hdl->infds = 0;
if (hdl->events & POLLOUT) {
if (!hdl->running &&
snd_pcm_state(hdl->opcm) == SND_PCM_STATE_RUNNING)
sio_alsa_onmove(hdl);
hdl->onfds = snd_pcm_poll_descriptors(hdl->opcm,
pfd, hdl->nfds);
if (hdl->onfds < 0) {
DALSA("couldn't poll play descriptors", hdl->onfds);
hdl->sio.eof = 1;
return 0;
if (hdl->canwrite == 0) {
hdl->onfds = snd_pcm_poll_descriptors(hdl->opcm,
pfd, hdl->nfds);
if (hdl->onfds < 0) {
DALSA("couldn't poll play descriptors",
hdl->onfds);
hdl->sio.eof = 1;
return 0;
}
}
} else
hdl->onfds = 0;
}
if (hdl->events & POLLIN) {
if (!hdl->running &&
snd_pcm_state(hdl->ipcm) == SND_PCM_STATE_RUNNING)
sio_alsa_onmove(hdl);
hdl->infds = snd_pcm_poll_descriptors(hdl->ipcm,
pfd + hdl->onfds, hdl->nfds - hdl->onfds);
if (hdl->infds < 0) {
DALSA("couldn't poll rec descriptors", hdl->infds);
hdl->sio.eof = 1;
return 0;
if (hdl->canread == 0) {
hdl->infds = snd_pcm_poll_descriptors(hdl->ipcm,
pfd + hdl->onfds, hdl->nfds - hdl->onfds);
if (hdl->infds < 0) {
DALSA("couldn't poll rec descriptors",
hdl->infds);
hdl->sio.eof = 1;
return 0;
}
}
} else
hdl->infds = 0;
}
DPRINTFN(4, "sio_alsa_pollfd: events = %x, nfds = %d + %d\n",
events, hdl->onfds, hdl->infds);
@ -1097,7 +1117,7 @@ sio_alsa_revents(struct sio_hdl *sh, struct pollfd *pfd)
snd_pcm_sframes_t iused, oavail, oused;
snd_pcm_state_t istate, ostate;
unsigned short revents, r;
int nfds, err, i;
int nfds, err, i, cycle;
if (hdl->sio.eof)
return POLLHUP;
@ -1106,27 +1126,41 @@ sio_alsa_revents(struct sio_hdl *sh, struct pollfd *pfd)
DPRINTFN(4, "sio_alsa_revents: pfds[%d].revents = %x\n",
i, pfd[i].revents);
}
revents = nfds = 0;
cycle = revents = nfds = 0;
if (hdl->events & POLLOUT) {
err = snd_pcm_poll_descriptors_revents(hdl->opcm,
pfd, hdl->onfds, &r);
if (err < 0) {
DALSA("couldn't get play events", err);
hdl->sio.eof = 1;
return POLLHUP;
}
if (hdl->canwrite == 0) {
err = snd_pcm_poll_descriptors_revents(hdl->opcm,
pfd, hdl->onfds, &r);
if (err < 0) {
DALSA("couldn't get play events", err);
hdl->sio.eof = 1;
return POLLHUP;
}
if (r & POLLOUT) {
hdl->canwrite = hdl->par.round;
cycle = 1;
}
} else
r = POLLOUT;
revents |= r;
nfds += hdl->onfds;
}
if (hdl->events & POLLIN) {
err = snd_pcm_poll_descriptors_revents(hdl->ipcm,
pfd + nfds, hdl->infds, &r);
if (err < 0) {
DALSA("couldn't get rec events", err);
hdl->sio.eof = 1;
return POLLHUP;
}
if (hdl->canread == 0) {
err = snd_pcm_poll_descriptors_revents(hdl->ipcm,
pfd + nfds, hdl->infds, &r);
if (err < 0) {
DALSA("couldn't get rec events", err);
hdl->sio.eof = 1;
return POLLHUP;
}
if (r & POLLIN) {
hdl->canread = hdl->par.round;
cycle = 1;
}
} else
r = POLLIN;
revents |= r;
nfds += hdl->infds;
}
@ -1179,7 +1213,7 @@ sio_alsa_revents(struct sio_hdl *sh, struct pollfd *pfd)
hdl->iused = iused;
}
}
if (revents & (POLLIN | POLLOUT))
if (cycle && hdl->running)
sio_alsa_onmove(hdl);
return revents;
}