From 16fb53b8c3d69ee7fcf62706041605c1e45fe849 Mon Sep 17 00:00:00 2001 From: gretchen Date: Mon, 18 Nov 2019 18:10:44 -0800 Subject: juggle timeouts --- norns_shell.c | 78 +++++++++++++++++++++++++++++++++++++++++++++-------------- 1 file changed, 60 insertions(+), 18 deletions(-) diff --git a/norns_shell.c b/norns_shell.c index 386596e..425e605 100644 --- a/norns_shell.c +++ b/norns_shell.c @@ -16,8 +16,10 @@ #define SENTINEL_TIMEOUT (MS(1500)) #define SENTINEL_DELAY (MS(200)) +#define WAIT (MS(200)) +#define TOTAL_WAIT (S(2)) -int timespec_diff_gt(struct timespec *before, struct timespec *after, long ns) +int time_diff_gt(struct timespec *before, struct timespec *after, long ns) { /* be careful not to overflow the long... */ long ss = ns / S(1) + 1; @@ -102,7 +104,28 @@ int main (int argc, char **argv) -- as a sentinel -- repeatedly until we get the corresponding . */ + /* Meanwhile, there are several different timeouts being checked + here: + + 1) If nn_poll times out, it means the fd was neither writable + nor readable during the specified amount of time. This means + something's seriously wrong. + 2) If we don't receive a response to the sentinel within TIMEOUT, + this means the endpoint probably isn't responding, and there + may not be a server at the endpoint at all. We'll exit in this + case instead of leaving the user hanging forever. + 3) If we send the final message (i.e., if we got EOF), we'll exit + if we don't receive anything within WAIT of the last time we + received anything. This way we exit quickly if there's no more + responses, but if there's a flurry of quick responses we get them all. + 4) However, if the lua engine just keeps printing, then we don't + want to leave the user hanging forever. So if we sent the final + message (i.e., if we got EOF) more than TOTAL_WAIT ago, we exit. + */ int received = 0; + int sent_last = 0; + struct timespec input_ended = {0}; + struct timespec last_received = {0}; struct timespec began = {0}; if (clock_gettime(CLOCK_MONOTONIC, &began)) { fprintf(stderr, "Can't use the clock? %m\n"); @@ -111,18 +134,30 @@ int main (int argc, char **argv) struct timespec sent_sentinel = {0}; /* we'll also wait for one message after EOF... */ - int sent_last = 0; struct nn_pollfd poll_s = { .fd = s, .events = NN_POLLIN | NN_POLLOUT }; while (!killed) { + struct timespec now = {0}; switch (nn_poll(&poll_s, 1, 1000)) { case -1: - fprintf(stderr, "error polling: %m\n"); + fprintf(stderr, "Error polling: %m\n"); goto error; case 0: - fprintf(stderr, "# timeout...\n"); - break; + fprintf(stderr, "# poll timeout. something is probably wrong.\n"); + goto error; case 1: + if (clock_gettime(CLOCK_MONOTONIC, &now)) { + fprintf(stderr, "Can't use the clock? %m\n"); + goto error; + } + /* If we've sent the last message and we're over the wait + since the last received message, exit. */ + if (sent_last && time_diff_gt(&last_received, &now, WAIT)) + goto finish; + /* If we've sent the last message and we're over the wait + since we ended, exit. */ + if (sent_last && time_diff_gt(&input_ended, &now, TOTAL_WAIT)) + goto finish; /* If we can read, echo out the message (unless it's the response to the sentinel, which we handle differently). */ if (poll_s.revents & NN_POLLIN) { @@ -137,29 +172,30 @@ int main (int argc, char **argv) fprintf(stderr, "# it lives!\n"); } else { /* strip final newline, if there are two... */ - if (n >= 2 && buf[n - 1] == '\n' && buf[n - 2] == '\n') + if (n >= 2 + && buf[n - 1] == '\n' + && buf[n - 2] == '\n') n -= 1; if (write(STDOUT_FILENO, buf, n) < n) { fprintf(stderr, "Couldn't write: %m\n"); goto error; } - if (sent_last) - goto finish; } + /* TODO: maybe if we get a stacktrace we should set the + exit code? */ nn_freemsg(buf); + if (clock_gettime(CLOCK_MONOTONIC, &last_received)) { + fprintf(stderr, "Can't use the clock? %m\n"); + goto error; + } received = 1; } /* If we can write, see if there's a full line. */ if (poll_s.revents & NN_POLLOUT) { if (!received) { - struct timespec now = {0}; - if (clock_gettime(CLOCK_MONOTONIC, &now)) { - fprintf(stderr, "Can't use the clock? %m\n"); - goto error; - } /* if we've been waiting on sentinel response and haven't gotten anything, time out. */ - if (timespec_diff_gt(&began, &now, SENTINEL_TIMEOUT)) { + if (time_diff_gt(&began, &now, SENTINEL_TIMEOUT)) { fprintf(stderr, "# timed out.\n" "# are you sure there's something " @@ -168,9 +204,9 @@ int main (int argc, char **argv) goto error; } /* only do this once every SENTINEL_DELAY... */ - if (!timespec_diff_gt(&sent_sentinel, - &now, - SENTINEL_DELAY)) + if (!time_diff_gt(&sent_sentinel, + &now, + SENTINEL_DELAY)) break; memcpy(&sent_sentinel, &now, sizeof(struct timespec)); @@ -189,7 +225,13 @@ int main (int argc, char **argv) goto error; } } - if (n == 0) sent_last = 1; + if (n == 0) { + sent_last = 1; + if (clock_gettime(CLOCK_MONOTONIC, &input_ended)) { + fprintf(stderr, "Can't use the clock? %m\n"); + goto error; + } + } /* treat EAGAIN as "read 0" */ if (n < 1) n = 0; line_n += n; -- cgit v1.2.1