From dab1565f3127ad655c0ef6e0f74f0f78344b28fb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Juhani=20Krekel=C3=A4?= Date: Wed, 10 Jul 2019 15:12:11 +0300 Subject: [PATCH] Use microsecond resolution in timeouts --- ethermess.c | 67 ++++++++++++++++++++++++++++++++++++++++------------- 1 file changed, 51 insertions(+), 16 deletions(-) diff --git a/ethermess.c b/ethermess.c index d144b25..6c2f6e2 100644 --- a/ethermess.c +++ b/ethermess.c @@ -51,7 +51,7 @@ unsigned char broadcast_mac[6] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff}; unsigned char own_status = EMS_AVAILABLE; unsigned char own_nick[256]; unsigned char own_nick_length = 0; -time_t next_status_broadcast; +struct timespec next_status_broadcast; enum message_send_states {IDLE, QUEUED, SENDING}; enum message_send_states own_message_send_state = IDLE; @@ -111,12 +111,55 @@ unsigned char random_byte(void) { return randomness; } -time_t monotonic_time(void) { +struct timespec ms_in_future(intmax_t ms) { + struct timespec ts; + if (clock_gettime(CLOCK_MONOTONIC, &ts) == -1) { + err(1, "clock_gettime"); + } + + // We can't really do anything to check for time_t overflow, because + // there are no macros to test for its size, so let's just hope it + // won't overflow + ts.tv_sec += ms / 1000; +#if LONG_MAX < 1999999999L +#error This code does time arithmetic, and requires a long big enough to hold almost 2 seconds worth of nanoseconds +#endif + ts.tv_nsec += (ms % 1000) * 1000 * 1000; + if (ts.tv_nsec >= 1000 * 1000 * 1000) { + ts.tv_sec += -1; + ts.tv_nsec -= 1000 * 1000 * 1000; + } + + return ts; +} + +int wait_ms_until(struct timespec then) { + // This function basically returns the difference in ms between the + // current time and the given time, clamped to [0, INT_MAX] + // + // That format works for poll(2), which will immediately exit after + // checking status if timeout is 0, and otherwise wait the given + // number of ms + struct timespec now; if (clock_gettime(CLOCK_MONOTONIC, &now) == -1) { err(1, "clock_gettime"); } - return now.tv_sec; + + // Overflow not checked because fuck overflow checking in C + // TODO: Check overflow + intmax_t sec_diff = then.tv_sec - now.tv_sec; + intmax_t ns_diff = then.tv_nsec - now.tv_nsec; + intmax_t ms = sec_diff * 1000 + ns_diff / 1000 / 1000; + + // Clamp + if (ms > INT_MAX) { + ms = INT_MAX; + } else if (ms < 0) { + ms = 0; + } + + return (int)ms; } char hexify(int nybble) { @@ -648,21 +691,13 @@ void eventloop(void) { } // Figure out how many ms to wait - int wait_ms; - time_t now = monotonic_time(); - if (next_status_broadcast <= now) { + int wait_ms = wait_ms_until(next_status_broadcast); + if (wait_ms == 0) { // The time has come to send the status broadcast send_status(broadcast_mac); // Do next one in about 5 minutes - next_status_broadcast = now + 5 * 60 + random_byte() / 64; - } else { - if (INT_MAX / 1000 >= next_status_broadcast - now) { - // Wail until next status broadcast is due - wait_ms = (next_status_broadcast - now) * 1000; - } else { - // Would overflow, wait INT_MAX ms - wait_ms = INT_MAX; - } + next_status_broadcast = ms_in_future(5 * 60 * 1000 + 1000 * random_byte() / 64); + wait_ms = wait_ms_until(next_status_broadcast); } int ready = poll(pollfds, sizeof(pollfds) / sizeof(*pollfds), wait_ms); @@ -767,7 +802,7 @@ int main(int argc, char **argv) { send_status(broadcast_mac); // Schedule next broadcast of our status about 5 min in the future - next_status_broadcast = monotonic_time() + 5 * 60 + random_byte() / 64; + next_status_broadcast = ms_in_future(5 * 60 * 1000 + 1000 * random_byte() / 64); // Request status from everyone, so that we can get an idea of who is on the network send_status_request(broadcast_mac);