Use microsecond resolution in timeouts

This commit is contained in:
Juhani Krekelä 2019-07-10 15:12:11 +03:00
parent ab73580c23
commit dab1565f31
1 changed files with 51 additions and 16 deletions

View File

@ -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);