Fix #12: Implement workaround for application < foo.txt

This patch implements a workaround for issue #12, i.e. when an I/O
watcher for redirected stdin is registered and causes epoll_ctl()
to return EPERM.

The workaround registers the watcher as `active = -1` and bypasses
the epoll_wait() call in the main loop until all data has been read
from stdin and the temporary -1 watcher(s) have completed.

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
This commit is contained in:
Joachim Nilsson committed 2017-10-21 19:10:04 +02:00
1 parent 82a1018dd2
commit e0cebc33b1
2 files changed
+52 -4

No files matched your search

+1
View File
@@ -46,6 +46,7 @@ typedef struct {
int fd; /* For epoll() */
uint32_t errors;
LIST_HEAD(,uev) watchers;
uint32_t workaround; /* For workarounds, e.g. redirected stdin */
} uev_ctx_t;
/* Forward declare due to dependencys, don't try this at home kids. */
+51 -4
View File
@@ -26,6 +26,7 @@
#include <fcntl.h> /* O_CLOEXEC */
#include <string.h> /* memset() */
#include <sys/epoll.h>
#include <sys/select.h> /* for select() workaround */
#include <sys/signalfd.h> /* struct signalfd_siginfo */
#include <unistd.h> /* close(), read() */
@@ -56,6 +57,18 @@ static int is_valid_fd(int fd)
return fcntl(fd, F_GETFL) != -1 || errno != EBADF;
}
/* Used by file i/o workaround when epoll => EPERM */
static int has_data(int fd)
{
struct timeval timeout = { 0, 0 };
fd_set fds;
FD_ZERO(&fds);
FD_SET(fd, &fds);
return select(1, &fds, NULL, NULL, &timeout) > 0;
}
/* Private to libuEv, do not use directly! */
int _uev_watcher_init(uev_ctx_t *ctx, uev_t *w, uev_type_t type, uev_cb_t *cb, void *arg, int fd, int events)
{
@@ -90,10 +103,23 @@ int _uev_watcher_start(uev_t *w)
ev.events = w->events | EPOLLRDHUP;
ev.data.ptr = w;
if (epoll_ctl(w->ctx->fd, EPOLL_CTL_ADD, w->fd, &ev) < 0)
return -1;
if (epoll_ctl(w->ctx->fd, EPOLL_CTL_ADD, w->fd, &ev) < 0) {
if (errno != EPERM)
return -1;
w->active = 1;
/* Handle special case: `application < file.txt` */
if (w->type != UEV_IO_TYPE || w->events != UEV_READ)
return -1;
/* Only allow this special handling for stdin */
if (w->fd != STDIN_FILENO)
return -1;
w->ctx->workaround = 1;
w->active = -1;
} else {
w->active = 1;
}
/* Add to internal list for bookkeeping */
LIST_INSERT_HEAD(&w->ctx->watchers, w, link);
@@ -254,9 +280,30 @@ int uev_run(uev_ctx_t *ctx, int flags)
}
while (ctx->running) {
int i, nfds;
int i, nfds, rerun = 0;
struct epoll_event events[UEV_MAX_EVENTS];
/* Handle special case: `application < file.txt` */
if (ctx->workaround) {
LIST_FOREACH(w, &ctx->watchers, link) {
if (w->active != -1 || !w->cb)
continue;
if (!has_data(w->fd)) {
w->active = 0;
LIST_REMOVE(w, link);
continue;
}
rerun++;
w->cb(w, w->arg, UEV_READ);
}
}
if (rerun)
continue;
ctx->workaround = 0;
while ((nfds = epoll_wait(ctx->fd, events, UEV_MAX_EVENTS, timeout)) < 0) {
if (!ctx->running)
break;