diff options
author | Asbjørn Sloth Tønnesen <ast@2e8.dk> | 2023-07-08 23:34:01 +0000 |
---|---|---|
committer | Asbjørn Sloth Tønnesen <ast@2e8.dk> | 2023-07-10 21:18:49 +0000 |
commit | 5f79d6da760e025b6af757242f70f4910901e053 (patch) | |
tree | 74081ed7448e8501cefbd35dd1fb06e0c444f347 /src | |
parent | 834780c4c6512e054c6f7c78b647dc7919cbcab6 (diff) | |
download | qlprint-5f79d6da760e025b6af757242f70f4910901e053.tar.gz qlprint-5f79d6da760e025b6af757242f70f4910901e053.tar.xz qlprint-5f79d6da760e025b6af757242f70f4910901e053.zip |
ql: rewrite to non-blocking I/O
Diffstat (limited to 'src')
-rw-r--r-- | src/ql.c | 101 |
1 files changed, 77 insertions, 24 deletions
@@ -11,6 +11,7 @@ #include <fcntl.h> #include <stdlib.h> #include <string.h> +#include <time.h> 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; } |