From 5d3224e9de99d596dc8d973a75266afc603a46cd Mon Sep 17 00:00:00 2001 From: Ian Jackson Date: Sun, 24 May 2020 16:51:09 +0100 Subject: [PATCH] time handling: Support use of CLOCK_MONOTONIC This involves a new initflag. And introducing a wrapper for gettimeofday(). Signed-off-by: Ian Jackson squash! time handling: Support use of CLOCK_MONOTONIC squash! time handling: Support use of CLOCK_MONOTONIC --- src/adns.h | 43 +++++++++++++++++++++++++++++++++++++------ src/event.c | 23 +++++++++++++++++++---- src/internal.h | 2 ++ src/query.c | 2 +- src/types.c | 7 ++++--- 5 files changed, 63 insertions(+), 14 deletions(-) diff --git a/src/adns.h b/src/adns.h index 3bb63e0..899a731 100644 --- a/src/adns.h +++ b/src/adns.h @@ -100,6 +100,7 @@ typedef enum { /* In general, or together the desired flags: */ adns_if_noautosys= 0x0010,/* do not make syscalls at every opportunity */ adns_if_eintr= 0x0020,/* allow _wait and _synchronous to return EINTR */ adns_if_nosigpipe= 0x0040,/* applic has SIGPIPE ignored, do not protect */ + adns_if_monotonic= 0x0080,/* enable if you can; see adns_processtimeouts */ adns_if_checkc_entex=0x0100,/* consistency checks on entry/exit to adns fns */ adns_if_checkc_freq= 0x0300,/* consistency checks very frequently (slow!) */ @@ -485,6 +486,21 @@ typedef struct { * these approaches has optimal performance. */ +/* + * Use of time: + * + * adns needs to manipulate timeouts. For API compatibility reasons + * (adns predates clock_gettime) the default is to use wall clock time + * from gettimeofday. This will malfunction if the system clock is + * not suitably stable. To avoid this, you should set + * adns_if_monotonic + * + * If you specify adns_if_monotonic then all `now' values passed to + * adns must be from clock_gettime(CLOCK_MONOTONIC). clock_gettime + * returns a struct timespec; you must convert it to a struct timeval + * by dividing the nsec by 1000 to make usec, rounding down. + */ + int adns_init(adns_state *newstate_r, adns_initflags flags, FILE *diagfile /*0=>stderr*/); @@ -819,6 +835,8 @@ int adns_processexceptional(adns_state ads, int fd, const struct timeval *now); * use that fd and only in the manner specified, regardless of whether * adns_if_noautosys was specified. * + * now is as for adns_processtimeouts. + * * adns_processexceptional should be called when select(2) reports an * exceptional condition, or poll(2) reports POLLPRI. * @@ -834,15 +852,19 @@ void adns_processtimeouts(adns_state ads, const struct timeval *now); /* Gives adns flow-of-control so that it can process any timeouts * which might have happened. Very like _processreadable/writeable. * - * now may be 0; if it isn't, *now must be the current time, recently - * obtained from gettimeofday. + * now may be 0; if it isn't, *now must be the current time from + * gettimeofday, or iff adns_if_monotonic it must be converted + * from the results of clock_gettime(CLOCK_MONOTONIC) (with the + * timespec.tv_nsec rounded down to make timeval.tv_usec). */ void adns_firsttimeout(adns_state ads, struct timeval **tv_mod, struct timeval *tv_buf, struct timeval now); /* Asks adns when it would first like the opportunity to time - * something out. now must be the current time, from gettimeofday. + * something out. + * + * now must be the current time, as for adns_processtimeouts. * * If tv_mod points to 0 then tv_buf must be non-null, and * _firsttimeout will fill in *tv_buf with the time until the first @@ -866,8 +888,9 @@ void adns_globalsystemfailure(adns_state ads); * adns_s_systemfail, and adns will close any stream sockets it has * open. * - * This is used by adns, for example, if gettimeofday() fails. - * Without this the program's event loop might start to spin ! + * This is used by adns, for example, if gettimeofday() or + * clock_gettime fails. Without this the program's event loop might + * start to spin ! * * This call will never block. */ @@ -883,9 +906,11 @@ void adns_beforeselect(adns_state ads, int *maxfd, fd_set *readfds, /* Find out file descriptors adns is interested in, and when it would * like the opportunity to time something out. If you do not plan to * block then tv_mod may be 0. Otherwise, tv_mod and tv_buf are as - * for adns_firsttimeout. readfds, writefds, exceptfds and maxfd_io may + * for adns_processtimeouts. readfds, writefds, exceptfds and maxfd_io may * not be 0. * + * now is as for adns_processtimeouts. + * * If tv_mod is 0 on entry then this will never actually do any I/O, * or change the fds that adns is using or the timeouts it wants. In * any case it won't block, and it will set the timeout to zero if a @@ -899,6 +924,8 @@ void adns_afterselect(adns_state ads, int maxfd, const fd_set *readfds, * select. This is just a fancy way of calling adns_processreadable/ * writeable/timeouts as appropriate, as if select had returned the * data being passed. Always succeeds. + * + * now is as for adns_processtimeouts. */ /* @@ -962,6 +989,8 @@ int adns_beforepoll(adns_state ads, struct pollfd *fds, * descriptors, and use _firsttimeout is used to find out when adns * might want to time something out.) * + * now is as for adns_processtimeouts. + * * adns_beforepoll will return 0 on success, and will not fail for any * reason other than the fds buffer being too small (ERANGE). * @@ -984,6 +1013,8 @@ void adns_afterpoll(adns_state ads, const struct pollfd *fds, int nfds, /* Gives adns flow-of-control for a bit; intended for use after * poll(2). fds and nfds should be the results from poll(). pollfd * structs mentioning fds not belonging to adns will be ignored. + * + * now is as for adns_processtimeouts. */ diff --git a/src/event.c b/src/event.c index 10d64b1..c326c28 100644 --- a/src/event.c +++ b/src/event.c @@ -36,6 +36,7 @@ #include #include #include +#include #include "internal.h" #include "tvarith.h" @@ -149,6 +150,19 @@ void adns__tcp_tryconnect(adns_state ads, struct timeval now) { /* Timeout handling functions. */ +int adns__gettimeofday(adns_state ads, struct timeval *tv) { + if (!(ads->iflags & adns_if_monotonic)) + return gettimeofday(tv,0); + + struct timespec ts; + int r = clock_gettime(CLOCK_MONOTONIC,&ts); + if (r) return r; + + tv->tv_sec = ts.tv_sec; + tv->tv_usec = ts.tv_nsec / 1000; + return 0; +} + void adns__must_gettimeofday(adns_state ads, const struct timeval **now_io, struct timeval *tv_buf) { const struct timeval *now; @@ -156,8 +170,9 @@ void adns__must_gettimeofday(adns_state ads, const struct timeval **now_io, now= *now_io; if (now) return; - r= gettimeofday(tv_buf,0); if (!r) { *now_io= tv_buf; return; } - adns__diag(ads,-1,0,"gettimeofday failed: %s",strerror(errno)); + r= adns__gettimeofday(ads,tv_buf); if (!r) { *now_io= tv_buf; return; } + adns__diag(ads,-1,0,"gettimeofday/clock_gettime failed: %s", + strerror(errno)); adns_globalsystemfailure(ads); return; } @@ -670,7 +685,7 @@ int adns_processany(adns_state ads) { adns__consistency(ads,0,cc_enter); - r= gettimeofday(&now,0); + r= adns__gettimeofday(ads,&now); if (!r) adns_processtimeouts(ads,&now); /* We just use adns__fdevents to loop over the fd's trying them. @@ -760,7 +775,7 @@ int adns_check(adns_state ads, int r; adns__consistency(ads,*query_io,cc_enter); - r= gettimeofday(&now,0); + r= adns__gettimeofday(ads,&now); if (!r) adns__autosys(ads,now); r= adns__internal_check(ads,query_io,answer_r,context_r); diff --git a/src/internal.h b/src/internal.h index 121c6d8..c03a77b 100644 --- a/src/internal.h +++ b/src/internal.h @@ -895,6 +895,8 @@ adns__timeout_clear(adns_query qu) { qu->timeout_ms= 0; timerclear(&qu->timeout_started); } +int adns__gettimeofday(adns_state ads, struct timeval *tv_buf); + void adns__must_gettimeofday(adns_state ads, const struct timeval **now_io, struct timeval *tv_buf); /* Call with care - might reentrantly cause queries to be completed! */ diff --git a/src/query.c b/src/query.c index 4eba888..82adbdd 100644 --- a/src/query.c +++ b/src/query.c @@ -284,7 +284,7 @@ int adns_submit(adns_state ads, typei= adns__findtype(type); if (!typei) return ENOSYS; - r= gettimeofday(&now,0); if (r) goto x_errno; + r= adns__gettimeofday(ads,&now); if (r) goto x_errno; qu= query_alloc(ads,typei,type,flags,now); if (!qu) goto x_errno; qu->ctx.ext= context; diff --git a/src/types.c b/src/types.c index 59e9502..428dbfa 100644 --- a/src/types.c +++ b/src/types.c @@ -691,7 +691,7 @@ static void icb_addr(adns_query parent, adns_query child) { * settled on. */ adns__cancel_children(parent); - r= gettimeofday(&now, 0); if (r) goto x_gtod; + r= adns__gettimeofday(ads,&now); if (r) goto x_gtod; qf= adns__qf_addr_cname; if (!(parent->flags & adns_qf_cname_loose)) qf |= adns_qf_cname_forbid; addr_subqueries(parent, now, qf, child->vb.buf, child->vb.used); @@ -712,7 +712,7 @@ static void icb_addr(adns_query parent, adns_query child) { adns__cancel_children(parent); adns__free_interim(parent, pans->rrs.bytes); pans->rrs.bytes= 0; pans->nrrs= 0; - r= gettimeofday(&now, 0); if (r) goto x_gtod; + r= adns__gettimeofday(ads,&now); if (r) goto x_gtod; adns__search_next(ads, parent, now); return; } @@ -737,7 +737,8 @@ x_gtod: /* We have our own error handling, because adns__must_gettimeofday * handles errors by calling adns_globalsystemfailure, which would * reenter the query processing logic. */ - adns__diag(ads, -1, parent, "gettimeofday failed: %s", strerror(errno)); + adns__diag(ads, -1, parent, "gettimeofday/clock_gettime failed: %s", + strerror(errno)); err= adns_s_systemfail; goto x_err; -- 2.30.2