summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAsbjørn Sloth Tønnesen <ast@2e8.dk>2023-07-08 23:34:01 +0000
committerAsbjørn Sloth Tønnesen <ast@2e8.dk>2023-07-10 21:18:49 +0000
commit5f79d6da760e025b6af757242f70f4910901e053 (patch)
tree74081ed7448e8501cefbd35dd1fb06e0c444f347
parent834780c4c6512e054c6f7c78b647dc7919cbcab6 (diff)
downloadqlprint-5f79d6da760e025b6af757242f70f4910901e053.tar.gz
qlprint-5f79d6da760e025b6af757242f70f4910901e053.tar.xz
qlprint-5f79d6da760e025b6af757242f70f4910901e053.zip
ql: rewrite to non-blocking I/O
-rw-r--r--src/ql.c101
1 files changed, 77 insertions, 24 deletions
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 <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;
}