forked from external/yambar
module/alsa: don’t re-create the /dev/snd inotify watcher after each connect failure
When e.g. a USB soundcard is inserted, we get several CREATE events. In my experiments, we only succeed in connecting to ALSA after the last event. This means, we’ll have several CREATE events that we receive, remove the watcher, attempt to connect, fail, and then re-add the watcher. What if that “last” CREATE event occurs while our watcher has been removed? That’s right, we miss it, and will get stuck waiting forever. The solution is keep the watcher around. Now, if we’ve been successfully connected to ALSA for a long time, chances are we’ve built up events (for other cards, for example). We don’t want to trigger a storm of re-connect attempts, so drain the event queue after having been disconnected from ALSA. There *is* a small race here - if a card is removed and re-added *very* fast, we _may_ accidentally drain the CREATE event. I don’t see this happening in reality though.
This commit is contained in:
parent
5af070ee1d
commit
360e1fbada
1 changed files with 54 additions and 25 deletions
|
@ -318,38 +318,64 @@ run(struct module *mod)
|
|||
{
|
||||
int ret = 1;
|
||||
|
||||
int wd = -1;
|
||||
int ifd = inotify_init();
|
||||
int ifd = inotify_init1(IN_NONBLOCK | IN_CLOEXEC);
|
||||
if (ifd < 0) {
|
||||
LOG_ERRNO("failed to inotify");
|
||||
return 1;
|
||||
}
|
||||
|
||||
int wd = inotify_add_watch(ifd, "/dev/snd", IN_CREATE);
|
||||
if (wd < 0) {
|
||||
LOG_ERRNO("failed to create inotify watcher for /dev/snd");
|
||||
close(ifd);
|
||||
return 1;
|
||||
}
|
||||
|
||||
while (true) {
|
||||
enum run_state state = run_while_online(mod);
|
||||
|
||||
switch (state) {
|
||||
case RUN_DONE:
|
||||
ret = 0;
|
||||
break;
|
||||
goto out;
|
||||
|
||||
case RUN_ERROR:
|
||||
ret = 1;
|
||||
break;
|
||||
goto out;
|
||||
|
||||
case RUN_FAILED_CONNECT:
|
||||
break;
|
||||
|
||||
case RUN_DISCONNECTED:
|
||||
/*
|
||||
* We’ve been connected - drain the watcher
|
||||
*
|
||||
* We don’t want old, un-releated events (for other
|
||||
* soundcards, for example) to trigger a storm of
|
||||
* re-connect attempts.
|
||||
*/
|
||||
while (true) {
|
||||
uint8_t buf[1024];
|
||||
ssize_t amount = read(ifd, buf, sizeof(buf));
|
||||
if (amount < 0) {
|
||||
if (errno == EAGAIN)
|
||||
break;
|
||||
|
||||
LOG_ERRNO("failed to drain inotify watcher");
|
||||
ret = 1;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (amount == 0)
|
||||
break;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
wd = inotify_add_watch(ifd, "/dev/snd", IN_CREATE);
|
||||
if (wd < 0) {
|
||||
LOG_ERRNO("failed to create inotify watcher for /dev/snd");
|
||||
ret = 1;
|
||||
break;
|
||||
}
|
||||
bool have_create_event = false;
|
||||
|
||||
while (true) {
|
||||
while (!have_create_event) {
|
||||
struct pollfd fds[] = {{.fd = mod->abort_fd, .events = POLLIN},
|
||||
{.fd = ifd, .events = POLLIN}};
|
||||
int r = poll(fds, sizeof(fds) / sizeof(fds[0]), -1);
|
||||
|
@ -363,42 +389,45 @@ run(struct module *mod)
|
|||
goto out;
|
||||
}
|
||||
|
||||
if (fds[0].revents & POLLIN) {
|
||||
if (fds[0].revents & (POLLIN | POLLHUP)) {
|
||||
ret = 0;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (fds[1].revents & POLLIN) {
|
||||
if (fds[1].revents & POLLHUP) {
|
||||
LOG_ERR("inotify socket closed");
|
||||
ret = 1;
|
||||
goto out;
|
||||
}
|
||||
|
||||
assert(fds[1].revents & POLLIN);
|
||||
|
||||
while (true) {
|
||||
char buf[1024];
|
||||
ssize_t len = read(ifd, buf, sizeof(buf));
|
||||
|
||||
if (len < 0) {
|
||||
if (errno == EAGAIN)
|
||||
break;
|
||||
|
||||
LOG_ERRNO("failed to read inotify events");
|
||||
ret = 1;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (len == 0) {
|
||||
LOG_ERR("inotify FD closed");
|
||||
ret = 1;
|
||||
goto out;
|
||||
}
|
||||
if (len == 0)
|
||||
break;
|
||||
|
||||
/* Consume inotify data */
|
||||
bool have_create_event = false;
|
||||
for (const char *ptr = buf; ptr < buf + len; ) {
|
||||
const struct inotify_event *e = (const struct inotify_event *)ptr;
|
||||
|
||||
if (e->mask & IN_CREATE) {
|
||||
LOG_DBG("inotify: CREATED: /dev/snd/%.*s", e->len, e->name);
|
||||
have_create_event = true;
|
||||
}
|
||||
ptr += sizeof(*e) + e->len;
|
||||
}
|
||||
|
||||
if (have_create_event) {
|
||||
inotify_rm_watch(ifd, wd);
|
||||
wd = -1;
|
||||
break;
|
||||
ptr += sizeof(*e) + e->len;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue