From 5f79d6da760e025b6af757242f70f4910901e053 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Asbj=C3=B8rn=20Sloth=20T=C3=B8nnesen?= Date: Sat, 8 Jul 2023 23:34:01 +0000 Subject: ql: rewrite to non-blocking I/O --- src/ql.c | 101 ++++++++++++++++++++++++++++++++++++++++++++++++--------------- 1 file changed, 77 insertions(+), 24 deletions(-) (limited to 'src') diff --git a/src/ql.c b/src/ql.c index e759cb0..167f7a8 100644 --- a/src/ql.c +++ b/src/ql.c @@ -11,6 +11,7 @@ #include #include #include +#include struct ql_ctx { char *printer; @@ -19,29 +20,53 @@ struct ql_ctx { #define ESC 0x1b -#define NUM_STATUS_READ_RETRIES 100 +#define STATUS_READ_TIMEOUT 5 /* seconds */ #define full_write(fd, buf) (retry_write(fd, buf, sizeof(buf)) == (ssize_t)sizeof(buf)) ssize_t retry_write(int fd, const char *buf, size_t len) { size_t written = 0; + + int retval; + fd_set wfds; + struct timeval tv; + while (written != len) { - ssize_t n = write(fd, buf + written, len - written); - if (n == -1) { - if (errno == EAGAIN || errno == EINTR) - continue; - else - break; - } else - written += n; + FD_ZERO(&wfds); + FD_SET(fd, &wfds); + + tv.tv_sec = 5; + tv.tv_usec = 0; + + retval = select(fd + 1, NULL, &wfds, NULL, &tv); + if (retval == -1) + perror("select()"); + else if (FD_ISSET(fd, &wfds)) { + ssize_t n = write(fd, buf + written, len - written); + if (n == -1) { + if (errno == EAGAIN || errno == EINTR) + continue; + else + break; + } else + written += n; + } } return written; } +static void get_mono_time(struct timespec *tp) +{ + if (clock_gettime(CLOCK_MONOTONIC, tp) == -1) { + perror("clock_gettime"); + exit(EXIT_FAILURE); + } +} + ql_ctx_t ql_open(const char *printer) { - int fd = open(printer, O_RDWR); + int fd = open(printer, O_RDWR | O_NONBLOCK); if (fd < 0) return NULL; @@ -78,20 +103,48 @@ bool ql_request_status(ql_ctx_t ctx) bool ql_read_status(ql_ctx_t ctx, ql_status_t * status) { - for (int i = 0; i < NUM_STATUS_READ_RETRIES; ++i) { - int ret = read(ctx->fd, status, sizeof(*status)); - if (ret == 32) - return true; - else if ((ret == 0) // "no data yet, too bad we just eof'd your fd, sucker" - || (ret == -1 && errno == EBADF)) // in case we messed up, somehow - { - close(ctx->fd); - ctx->fd = open(ctx->printer, O_RDWR); - if (ctx->fd < 0) - return false; - } else if (ret == -1 && (errno != EAGAIN || errno != EINTR)) - return false; // non-recoverable - } + int retval; + fd_set rfds; + struct timeval tv; + struct timespec tp_begin; + struct timespec tp_now; + + get_mono_time(&tp_begin); + + /* If select(2) selects returns early, then the outer loop calls + * usleep(3) and tries again, until the timeout is reached. */ + int i = 0; + do { + i++; + + FD_ZERO(&rfds); + FD_SET(ctx->fd, &rfds); + + if (tp_now.tv_sec == 0) { + tv.tv_sec = STATUS_READ_TIMEOUT; + } else { + tv.tv_sec = tp_begin.tv_sec + STATUS_READ_TIMEOUT - tp_now.tv_sec; + } + tv.tv_usec = 0; + + retval = select(ctx->fd + 1, &rfds, NULL, NULL, &tv); + + if (retval == -1) { + perror("select()"); + } else if (retval == 0) { + break; /* timeout */ + } else if (FD_ISSET(ctx->fd, &rfds)) { + int ret = read(ctx->fd, status, sizeof(*status)); + if (ret == 32) { + return true; + } else { + usleep(i * 5000); /* i * 5 ms */ + } + } + + get_mono_time(&tp_now); + } while (tp_now.tv_sec - tp_begin.tv_sec < STATUS_READ_TIMEOUT); + errno = ETIME; return false; } -- cgit v1.2.1