From 938963c1d1c6a9b929d28a90030332d4f397ca61 Mon Sep 17 00:00:00 2001 From: defanor Date: Tue, 2 May 2023 13:05:26 +0300 Subject: Use timespec and monotonic clock for timers --- emacs/xml_interface.c | 16 ++++++++++++---- examples/basic.c | 17 +++++++++++++---- src/rexmpp.c | 30 ++++++++++++++--------------- src/rexmpp.h | 12 ++++++------ src/rexmpp_dns.c | 22 ++++++++++++++++++---- src/rexmpp_dns.h | 6 +++--- src/rexmpp_jingle.c | 12 ++++++------ src/rexmpp_jingle.h | 6 +++--- src/rexmpp_tcp.c | 52 +++++++++++++++++++++++++-------------------------- src/rexmpp_tcp.h | 14 +++++++------- 10 files changed, 109 insertions(+), 78 deletions(-) diff --git a/emacs/xml_interface.c b/emacs/xml_interface.c index 7db2c63..2f4ebba 100644 --- a/emacs/xml_interface.c +++ b/emacs/xml_interface.c @@ -329,8 +329,10 @@ int main (int argc, char **argv) { fd_set read_fds, write_fds; int nfds; - struct timeval tv; - struct timeval *mtv; + struct timespec tv; + struct timespec *mtv; + struct timeval tv_ms; + struct timeval *mtv_ms; int n = 0; do { @@ -367,7 +369,13 @@ int main (int argc, char **argv) { FD_ZERO(&read_fds); FD_ZERO(&write_fds); nfds = rexmpp_fds(&s, &read_fds, &write_fds); - mtv = rexmpp_timeout(&s, NULL, (struct timeval*)&tv); + mtv = rexmpp_timeout(&s, NULL, &tv); + mtv_ms = NULL; + if (mtv != NULL) { + tv_ms.tv_sec = mtv->tv_sec; + tv_ms.tv_usec = mtv->tv_nsec / 1000; + mtv_ms = &tv_ms; + } /* Add other file descriptors we are interested in, particularly stdin for user input. */ @@ -375,7 +383,7 @@ int main (int argc, char **argv) { /* Run select(2) with all those file descriptors and timeouts, waiting for either user input or some rexmpp event to occur. */ - n = select(nfds, &read_fds, &write_fds, NULL, mtv); + n = select(nfds, &read_fds, &write_fds, NULL, mtv_ms); if (n == -1) { printf("select error: %s\n", strerror(errno)); break; diff --git a/examples/basic.c b/examples/basic.c index 6e195bd..982106a 100644 --- a/examples/basic.c +++ b/examples/basic.c @@ -11,6 +11,7 @@ #include #include #include +#include #include #include @@ -155,8 +156,10 @@ int main (int argc, char **argv) { fd_set read_fds, write_fds; int nfds; - struct timeval tv; - struct timeval *mtv; + struct timespec tv; + struct timespec *mtv; + struct timeval tv_ms; + struct timeval *mtv_ms; int n = 0; do { @@ -217,7 +220,13 @@ int main (int argc, char **argv) { FD_ZERO(&read_fds); FD_ZERO(&write_fds); nfds = rexmpp_fds(&s, &read_fds, &write_fds); - mtv = rexmpp_timeout(&s, NULL, (struct timeval*)&tv); + mtv = rexmpp_timeout(&s, NULL, &tv); + mtv_ms = NULL; + if (mtv != NULL) { + tv_ms.tv_sec = mtv->tv_sec; + tv_ms.tv_usec = mtv->tv_nsec / 1000; + mtv_ms = &tv_ms; + } /* Add other file descriptors we are interested in, particularly stdin for user input. */ @@ -225,7 +234,7 @@ int main (int argc, char **argv) { /* Run select(2) with all those file descriptors and timeouts, waiting for either user input or some rexmpp event to occur. */ - n = select(nfds, &read_fds, &write_fds, NULL, mtv); + n = select(nfds, &read_fds, &write_fds, NULL, mtv_ms); if (n == -1) { printf("select error: %s\n", strerror(errno)); break; diff --git a/src/rexmpp.c b/src/rexmpp.c index e494a14..c29574e 100644 --- a/src/rexmpp.c +++ b/src/rexmpp.c @@ -578,7 +578,7 @@ rexmpp_err_t rexmpp_init (rexmpp_t *s, s->iq_cache = NULL; s->reconnect_number = 0; s->next_reconnect_time.tv_sec = 0; - s->next_reconnect_time.tv_usec = 0; + s->next_reconnect_time.tv_nsec = 0; s->initial_jid.full[0] = '\0'; s->assigned_jid.full[0] = '\0'; s->stanza_queue_size = 1024; @@ -867,7 +867,7 @@ void rexmpp_schedule_reconnect (rexmpp_t *s) { if (seconds > 3600) { seconds = 3600; } - gettimeofday(&(s->next_reconnect_time), NULL); + clock_gettime(CLOCK_MONOTONIC, &(s->next_reconnect_time)); s->next_reconnect_time.tv_sec += seconds; rexmpp_log(s, LOG_DEBUG, "Scheduled reconnect number %d, in %d seconds", s->reconnect_number, @@ -2495,8 +2495,8 @@ rexmpp_err_t rexmpp_stop (rexmpp_t *s) { } rexmpp_err_t rexmpp_run (rexmpp_t *s, fd_set *read_fds, fd_set *write_fds) { - struct timeval now; - if (gettimeofday(&now, NULL) != 0) { + struct timespec now; + if (clock_gettime(CLOCK_MONOTONIC, &now) != 0) { rexmpp_log(s, LOG_ERR, "Failed to get time: %s", strerror(errno)); return REXMPP_E_OTHER; } @@ -2772,11 +2772,11 @@ int rexmpp_fds (rexmpp_t *s, fd_set *read_fds, fd_set *write_fds) { return max_fd; } -struct timeval *rexmpp_timeout (rexmpp_t *s, - struct timeval *max_tv, - struct timeval *tv) +struct timespec *rexmpp_timeout (rexmpp_t *s, + struct timespec *max_tv, + struct timespec *tv) { - struct timeval *ret = max_tv; + struct timespec *ret = max_tv; if (s->resolver_state != REXMPP_RESOLVER_NONE && s->resolver_state != REXMPP_RESOLVER_READY) { @@ -2787,14 +2787,14 @@ struct timeval *rexmpp_timeout (rexmpp_t *s, ret = rexmpp_jingle_timeout(s, ret, tv); - struct timeval now; - gettimeofday(&now, NULL); + struct timespec now; + clock_gettime(CLOCK_MONOTONIC, &now); if (s->reconnect_number > 0 && s->next_reconnect_time.tv_sec > now.tv_sec && (ret == NULL || s->next_reconnect_time.tv_sec - now.tv_sec < ret->tv_sec)) { tv->tv_sec = s->next_reconnect_time.tv_sec - now.tv_sec; - tv->tv_usec = 0; + tv->tv_nsec = 0; ret = tv; } @@ -2803,20 +2803,20 @@ struct timeval *rexmpp_timeout (rexmpp_t *s, time_t next_ping = s->last_network_activity + s->ping_delay - now.tv_sec; if (ret == NULL || next_ping < ret->tv_sec) { tv->tv_sec = next_ping; - tv->tv_usec = 0; + tv->tv_nsec = 0; ret = tv; } } #ifdef HAVE_CURL - long curl_timeout; + long curl_timeout; /* in milliseconds */ curl_multi_timeout(s->curl_multi, &curl_timeout); if (curl_timeout >= 0 && (curl_timeout / 1000 < ret->tv_sec || (curl_timeout / 1000 == ret->tv_sec && - (curl_timeout % 1000) * 1000 < ret->tv_usec))) { + (curl_timeout % 1000) * 1000000 < ret->tv_nsec))) { tv->tv_sec = curl_timeout / 1000; - tv->tv_usec = (curl_timeout % 1000) * 1000; + tv->tv_nsec = (curl_timeout % 1000) * 1000000; ret = tv; } #endif diff --git a/src/rexmpp.h b/src/rexmpp.h index 71edacb..13b6c08 100644 --- a/src/rexmpp.h +++ b/src/rexmpp.h @@ -322,7 +322,7 @@ struct rexmpp /* Connection and stream management. */ unsigned int reconnect_number; time_t reconnect_seconds; - struct timeval next_reconnect_time; + struct timespec next_reconnect_time; xmlNodePtr stanza_queue; uint32_t stanzas_out_count; uint32_t stanzas_out_acknowledged; @@ -481,13 +481,13 @@ void rexmpp_iq_reply (rexmpp_t *s, @param[in] s ::rexmpp @param[in] max_tv An existing timeout (can be NULL), to return if there's no more urgent timeouts. - @param[in,out] tv An allocated timeval structure, to store the time - in. + @param[in,out] tv An allocated timespec structure, to store the + time in. @returns A pointer to either max_tv or tv. */ -struct timeval *rexmpp_timeout (rexmpp_t *s, - struct timeval *max_tv, - struct timeval *tv); +struct timespec *rexmpp_timeout (rexmpp_t *s, + struct timespec *max_tv, + struct timespec *tv); /** @brief Sets file descriptors to watch. diff --git a/src/rexmpp_dns.c b/src/rexmpp_dns.c index 5c3f4f6..1894481 100644 --- a/src/rexmpp_dns.c +++ b/src/rexmpp_dns.c @@ -181,16 +181,30 @@ int rexmpp_dns_fds (rexmpp_t *s, fd_set *read_fds, fd_set *write_fds) { #endif } -struct timeval * rexmpp_dns_timeout (rexmpp_t *s, - struct timeval *max_tv, - struct timeval *tv) +struct timespec * rexmpp_dns_timeout (rexmpp_t *s, + struct timespec *max_tv, + struct timespec *tv) { #if defined(USE_UNBOUND) (void)s; (void)tv; return max_tv; #elif defined(USE_CARES) - return ares_timeout(s->resolver.channel, max_tv, tv); + struct timeval tv_ms; + struct timeval *max_tv_ms = NULL; + if (max_tv != NULL) { + max_tv_ms = &tv_ms; + tv_ms.tv_sec = tv->tv_sec; + tv_ms.tv_usec = tv->tv_nsec / 1000; + } + struct timeval *ret_ms = ares_timeout(s->resolver.channel, max_tv_ms, &tv_ms); + if (ret_ms == max_tv_ms) { + return max_tv; + } else { + tv->tv_sec = tv_ms.tv_sec; + tv->tv_nsec = tv_ms.tv_usec * 1000; + return tv; + } #else (void)s; (void)max_tv; diff --git a/src/rexmpp_dns.h b/src/rexmpp_dns.h index 6641238..abfe6b7 100644 --- a/src/rexmpp_dns.h +++ b/src/rexmpp_dns.h @@ -103,9 +103,9 @@ int rexmpp_dns_fds (rexmpp_t *s, fd_set *read_fds, fd_set *write_fds); /** @brief Reports timeouts. */ -struct timeval * rexmpp_dns_timeout (rexmpp_t *s, - struct timeval *max_tv, - struct timeval *tv); +struct timespec * rexmpp_dns_timeout (rexmpp_t *s, + struct timespec *max_tv, + struct timespec *tv); typedef void (*dns_query_cb_t) (rexmpp_t *s, void *ptr, rexmpp_dns_result_t *result); diff --git a/src/rexmpp_jingle.c b/src/rexmpp_jingle.c index 3a72a95..8499dc8 100644 --- a/src/rexmpp_jingle.c +++ b/src/rexmpp_jingle.c @@ -1779,9 +1779,9 @@ int rexmpp_jingle_fds(rexmpp_t *s, fd_set *read_fds, fd_set *write_fds) { return (nfds + 1); } -struct timeval * rexmpp_jingle_timeout (rexmpp_t *s, - struct timeval *max_tv, - struct timeval *tv) { +struct timespec * rexmpp_jingle_timeout (rexmpp_t *s, + struct timespec *max_tv, + struct timespec *tv) { #ifdef ENABLE_CALLS gint poll_timeout; GPollFD poll_fds[10]; @@ -1811,12 +1811,12 @@ struct timeval * rexmpp_jingle_timeout (rexmpp_t *s, if (poll_timeout >= 0) { int sec = poll_timeout / 1000; - int usec = (poll_timeout % 1000) * 1000; + int nsec = (poll_timeout % 1000) * 1000000; if (max_tv == NULL || (max_tv->tv_sec > sec || - (max_tv->tv_sec == sec && max_tv->tv_usec > usec))) { + (max_tv->tv_sec == sec && max_tv->tv_nsec > nsec))) { tv->tv_sec = sec; - tv->tv_usec = usec; + tv->tv_nsec = nsec; max_tv = tv; } } diff --git a/src/rexmpp_jingle.h b/src/rexmpp_jingle.h index cd23efb..d3e11eb 100644 --- a/src/rexmpp_jingle.h +++ b/src/rexmpp_jingle.h @@ -121,9 +121,9 @@ struct rexmpp_jingle_ctx { int rexmpp_jingle_init (rexmpp_t *s); rexmpp_err_t rexmpp_jingle_run (rexmpp_t *s, fd_set *read_fds, fd_set *write_fds); -struct timeval * rexmpp_jingle_timeout (rexmpp_t *s, - struct timeval *max_tv, - struct timeval *tv); +struct timespec * rexmpp_jingle_timeout (rexmpp_t *s, + struct timespec *max_tv, + struct timespec *tv); int rexmpp_jingle_fds(rexmpp_t *s, fd_set *read_fds, fd_set *write_fds); rexmpp_err_t diff --git a/src/rexmpp_tcp.c b/src/rexmpp_tcp.c index a248fec..a9c5547 100644 --- a/src/rexmpp_tcp.c +++ b/src/rexmpp_tcp.c @@ -50,10 +50,10 @@ void rexmpp_tcp_dns_a_cb (rexmpp_t *s, conn->addr_cur_v4 = -1; if (conn->resolution_v6 == REXMPP_CONN_RESOLUTION_WAITING) { /* Wait for 50 ms for IPv6. */ - gettimeofday(&(conn->next_connection_time), NULL); - conn->next_connection_time.tv_usec += REXMPP_TCP_IPV6_DELAY_MS * 1000; - if (conn->next_connection_time.tv_usec >= 1000000) { - conn->next_connection_time.tv_usec -= 1000000; + clock_gettime(CLOCK_MONOTONIC, &(conn->next_connection_time)); + conn->next_connection_time.tv_nsec += REXMPP_TCP_IPV6_DELAY_MS * 1000000; + if (conn->next_connection_time.tv_nsec >= 1000000000) { + conn->next_connection_time.tv_nsec -= 1000000000; conn->next_connection_time.tv_sec++; } } @@ -132,7 +132,7 @@ rexmpp_tcp_conn_init (rexmpp_t *s, conn->fd = -1; conn->dns_secure = 0; conn->next_connection_time.tv_sec = 0; - conn->next_connection_time.tv_usec = 0; + conn->next_connection_time.tv_nsec = 0; conn->resolution_v4 = REXMPP_CONN_RESOLUTION_INACTIVE; conn->resolution_v6 = REXMPP_CONN_RESOLUTION_INACTIVE; @@ -210,7 +210,7 @@ rexmpp_tcp_conn_proceed (rexmpp_t *s, fd_set *write_fds) { (void)read_fds; /* Not checking any read FDs at the moment. */ - struct timeval now; + struct timespec now; int i; /* Check for successful connections. */ @@ -250,10 +250,10 @@ rexmpp_tcp_conn_proceed (rexmpp_t *s, if (conn->connection_attempts < REXMPP_TCP_MAX_CONNECTION_ATTEMPTS && (rexmpp_tcp_conn_ipv4_available(conn) || rexmpp_tcp_conn_ipv6_available(conn))) { - gettimeofday(&now, NULL); + clock_gettime(CLOCK_MONOTONIC, &now); if (now.tv_sec > conn->next_connection_time.tv_sec || (now.tv_sec == conn->next_connection_time.tv_sec && - now.tv_usec >= conn->next_connection_time.tv_usec)) { + now.tv_nsec >= conn->next_connection_time.tv_nsec)) { /* Time to attempt a new connection. */ int use_ipv6 = 0; if (rexmpp_tcp_conn_ipv4_available(conn) && @@ -307,10 +307,10 @@ rexmpp_tcp_conn_proceed (rexmpp_t *s, conn->sockets[conn->connection_attempts] = rexmpp_tcp_socket(s, domain); if (connect(conn->sockets[conn->connection_attempts], addr, addrlen)) { if (errno == EINPROGRESS) { - gettimeofday(&(conn->next_connection_time), NULL); - conn->next_connection_time.tv_usec += REXMPP_TCP_CONN_DELAY_MS * 1000; - if (conn->next_connection_time.tv_usec >= 1000000) { - conn->next_connection_time.tv_usec -= 1000000; + clock_gettime(CLOCK_MONOTONIC, &(conn->next_connection_time)); + conn->next_connection_time.tv_nsec += REXMPP_TCP_CONN_DELAY_MS * 1000000; + if (conn->next_connection_time.tv_nsec >= 1000000000) { + conn->next_connection_time.tv_nsec -= 1000000000; conn->next_connection_time.tv_sec++; } conn->connection_attempts++; @@ -339,14 +339,14 @@ rexmpp_tcp_conn_proceed (rexmpp_t *s, } } - gettimeofday(&now, NULL); + clock_gettime(CLOCK_MONOTONIC, &now); if (active_connections || conn->resolution_v4 == REXMPP_CONN_RESOLUTION_WAITING || conn->resolution_v6 == REXMPP_CONN_RESOLUTION_WAITING || (conn->next_connection_time.tv_sec > now.tv_sec || (conn->next_connection_time.tv_sec == now.tv_sec && - conn->next_connection_time.tv_usec > now.tv_usec))) { + conn->next_connection_time.tv_nsec > now.tv_nsec))) { return REXMPP_CONN_IN_PROGRESS; } else { return REXMPP_CONN_FAILURE; @@ -374,13 +374,13 @@ int rexmpp_tcp_conn_fds (rexmpp_t *s, return max_fd; } -struct timeval *rexmpp_tcp_conn_timeout (rexmpp_t *s, - rexmpp_tcp_conn_t *conn, - struct timeval *max_tv, - struct timeval *tv) +struct timespec *rexmpp_tcp_conn_timeout (rexmpp_t *s, + rexmpp_tcp_conn_t *conn, + struct timespec *max_tv, + struct timespec *tv) { - struct timeval now; - struct timeval *ret = max_tv; + struct timespec now; + struct timespec *ret = max_tv; if (conn->resolution_v4 == REXMPP_CONN_RESOLUTION_WAITING || conn->resolution_v6 == REXMPP_CONN_RESOLUTION_WAITING) { ret = rexmpp_dns_timeout(s, max_tv, tv); @@ -389,20 +389,20 @@ struct timeval *rexmpp_tcp_conn_timeout (rexmpp_t *s, conn->resolution_v6 == REXMPP_CONN_RESOLUTION_SUCCESS || (conn->resolution_v4 == REXMPP_CONN_RESOLUTION_INACTIVE && conn->resolution_v6 == REXMPP_CONN_RESOLUTION_INACTIVE)) { - gettimeofday(&now, NULL); + clock_gettime(CLOCK_MONOTONIC, &now); if (now.tv_sec < conn->next_connection_time.tv_sec || (now.tv_sec == conn->next_connection_time.tv_sec && - now.tv_usec <= conn->next_connection_time.tv_usec)) { + now.tv_nsec <= conn->next_connection_time.tv_nsec)) { if (ret == NULL || ret->tv_sec > conn->next_connection_time.tv_sec - now.tv_sec || (ret->tv_sec == conn->next_connection_time.tv_sec - now.tv_sec && - ret->tv_usec > conn->next_connection_time.tv_usec - now.tv_usec)) { + ret->tv_nsec > conn->next_connection_time.tv_nsec - now.tv_nsec)) { ret = tv; tv->tv_sec = conn->next_connection_time.tv_sec - now.tv_sec; - if (conn->next_connection_time.tv_usec > now.tv_usec) { - tv->tv_usec = conn->next_connection_time.tv_usec - now.tv_usec; + if (conn->next_connection_time.tv_nsec > now.tv_nsec) { + tv->tv_nsec = conn->next_connection_time.tv_nsec - now.tv_nsec; } else { - tv->tv_usec = conn->next_connection_time.tv_usec + 1000000 - now.tv_usec; + tv->tv_nsec = conn->next_connection_time.tv_nsec + 1000000000 - now.tv_nsec; tv->tv_sec--; } } diff --git a/src/rexmpp_tcp.h b/src/rexmpp_tcp.h index 5a296cc..1440fa1 100644 --- a/src/rexmpp_tcp.h +++ b/src/rexmpp_tcp.h @@ -98,8 +98,8 @@ struct rexmpp_tcp_connection { /** @brief The number of connection attempts so far. */ int connection_attempts; - /** @brief Next scheduled connection time. */ - struct timeval next_connection_time; + /** @brief Next scheduled connection time (monotonic). */ + struct timespec next_connection_time; /** @brief File descriptor of a connected socket. */ int fd; /** @brief Whether the A or AAAA records used to establish the final @@ -175,13 +175,13 @@ int rexmpp_tcp_conn_fds (rexmpp_t *s, @param[in] s ::rexmpp @param[in] conn An active connection structure. @param[in] max_tv An existing maximum timeout. - @param[out] tv A timeval structure to store a new timeout in. + @param[out] tv A timespec structure to store a new timeout in. @returns A pointer to either max_tv or tv, depending on which one is smaller. */ -struct timeval *rexmpp_tcp_conn_timeout (rexmpp_t *s, - rexmpp_tcp_conn_t *conn, - struct timeval *max_tv, - struct timeval *tv); +struct timespec *rexmpp_tcp_conn_timeout (rexmpp_t *s, + rexmpp_tcp_conn_t *conn, + struct timespec *max_tv, + struct timespec *tv); #endif -- cgit v1.2.3