From 7ec121b337234db6e694a7140f5732df5e3aa34d Mon Sep 17 00:00:00 2001 From: Emil Mikulic Date: Mon, 11 Mar 2013 03:57:56 +1100 Subject: [PATCH] Handle write() returning EAGAIN by sleeping and retrying. Before this change, flow control on the tty caused the entire program to exit. After the change, we survive fine, and continue to write to other file descriptors while the tty is blocked. --- buftee.c | 63 ++++++++++++++++++++++++++++++++++++++------------------ 1 file changed, 43 insertions(+), 20 deletions(-) diff --git a/buftee.c b/buftee.c index 77cba3a..feb213b 100644 --- a/buftee.c +++ b/buftee.c @@ -215,31 +215,54 @@ static int xread(const int fd, char* const restrict buf, const int count) { return (int)read_ret; } +static void wait_until_writable(const int fd) { + fd_set write_fds; + FD_ZERO(&write_fds); + FD_SET(fd, &write_fds); + int select_ret = select(fd + 1, NULL, &write_fds, NULL, NULL); + if (select_ret == -1) { + if (errno == EINTR) { + assert(stopping); // that should have been SIGTERM + return; + } + err(1, "select(write fd = %d) failed", fd); + } + if (!FD_ISSET(fd, &write_fds)) + errx(1, "select() did not return writable fd = %d", fd); +} + static int xwrite(const int fd, struct buf* const buf) { ssize_t write_ret; int saved_errno; struct timespec t0; struct timespec t1; - get_mono_time(&t0); - write_ret = write(fd, buf->data, (size_t)buf->len); - saved_errno = errno; - get_mono_time(&t1); - warn_time("write()", &t0, &t1); - - errno = saved_errno; - if (write_ret == -1) - err(1, "write(fd = %d, count = %d) failed", fd, buf->len); - //FIXME: EAGAIN? - if (write_ret == 0) - return 0; - assert(write_ret >= 0); - if (write_ret < buf->len) - err(1, "write(fd = %d, count = %d) stopped short (returned %d)", - fd, buf->len, (int)write_ret); - // FIXME: handle this - assert(write_ret == buf->len); - return (int)write_ret; + for (;;) { + get_mono_time(&t0); + write_ret = write(fd, buf->data, (size_t)buf->len); + saved_errno = errno; + get_mono_time(&t1); + warn_time("write()", &t0, &t1); + + errno = saved_errno; + if (write_ret == -1) { + if (errno == EAGAIN) { + warn("write(fd = %d) got EAGAIN, sleeping and retrying", fd); + wait_until_writable(fd); + continue; + } + err(1, "write(fd = %d, count = %d) failed", fd, buf->len); + } + if (write_ret == 0) + return 0; // EOF + assert(write_ret >= 0); + if (write_ret < buf->len) + err(1, "write(fd = %d, count = %d) stopped short (returned %d)", + fd, buf->len, (int)write_ret); + // FIXME: handle this + assert(write_ret == buf->len); + return (int)write_ret; + } } static void wait_until_readable(const int fd) { @@ -252,7 +275,7 @@ static void wait_until_readable(const int fd) { assert(stopping); // that should have been SIGTERM return; } - err(1, "select() failed"); + err(1, "select(read fd = %d) failed", fd); } if (!FD_ISSET(fd, &read_fds)) errx(1, "select() did not return readable fd = %d", fd); -- 2.17.1