From 46dbd9461af66b9aa11a7f9e776979df6be028ac Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Tue, 13 May 2008 20:48:48 +0000 Subject: - Introducing millisecond resolution support for the timeout option. See ares_init_options()'s ARES_OPT_TIMEOUTMS. --- ares_process.c | 132 ++++++++++++++++++++++++++++++++++++++++----------------- 1 file changed, 93 insertions(+), 39 deletions(-) (limited to 'ares_process.c') diff --git a/ares_process.c b/ares_process.c index 442e328..eca363a 100644 --- a/ares_process.c +++ b/ares_process.c @@ -1,6 +1,7 @@ /* $Id$ */ /* Copyright 1998 by the Massachusetts Institute of Technology. + * Copyright (C) 2004-2008 by Daniel Stenberg * * Permission to use, copy, modify, and distribute this * software and its documentation for any purpose and without @@ -42,6 +43,9 @@ #ifdef HAVE_ARPA_NAMESER_COMPAT_H #include #endif +#ifdef HAVE_SYS_TIME_H +#include +#endif #endif /* WIN32 && !WATT32 */ #ifdef HAVE_STRINGS_H @@ -71,21 +75,25 @@ static int try_again(int errnum); static void write_tcp_data(ares_channel channel, fd_set *write_fds, - ares_socket_t write_fd, time_t now); + ares_socket_t write_fd, struct timeval *now); static void read_tcp_data(ares_channel channel, fd_set *read_fds, - ares_socket_t read_fd, time_t now); + ares_socket_t read_fd, struct timeval *now); static void read_udp_packets(ares_channel channel, fd_set *read_fds, - ares_socket_t read_fd, time_t now); + ares_socket_t read_fd, struct timeval *now); static void advance_tcp_send_queue(ares_channel channel, int whichserver, ssize_t num_bytes); -static void process_timeouts(ares_channel channel, time_t now); -static void process_broken_connections(ares_channel channel, time_t now); +static void process_timeouts(ares_channel channel, struct timeval *now); +static void process_broken_connections(ares_channel channel, + struct timeval *now); static void process_answer(ares_channel channel, unsigned char *abuf, - int alen, int whichserver, int tcp, time_t now); -static void handle_error(ares_channel channel, int whichserver, time_t now); + int alen, int whichserver, int tcp, + struct timeval *now); +static void handle_error(ares_channel channel, int whichserver, + struct timeval *now); static void skip_server(ares_channel channel, struct query *query, int whichserver); -static void next_server(ares_channel channel, struct query *query, time_t now); +static void next_server(ares_channel channel, struct query *query, + struct timeval *now); static int configure_socket(int s, ares_channel channel); static int open_tcp_socket(ares_channel channel, struct server_state *server); static int open_udp_socket(ares_channel channel, struct server_state *server); @@ -94,19 +102,59 @@ static int same_questions(const unsigned char *qbuf, int qlen, static void end_query(ares_channel channel, struct query *query, int status, unsigned char *abuf, int alen); +/* return true if now is exactly check time or later */ +int ares__timedout(struct timeval *now, + struct timeval *check) +{ + int secs = (now->tv_sec - check->tv_sec); + + if(secs > 0) + return 1; /* yes, timed out */ + if(secs < -1) + return 0; /* nope, not timed out */ + + /* if the full seconds were identical, check the sub second parts */ + return (now->tv_usec - check->tv_usec >= 0); +} + +/* add the specific number of milliseconds to the time in the first argument */ +int ares__timeadd(struct timeval *now, + int millisecs) +{ + now->tv_sec += millisecs/1000; + now->tv_usec += (millisecs%1000)*1000; + + if(now->tv_usec >= 1000000) { + ++(now->tv_sec); + now->tv_usec -= 1000000; + } + + return 0; +} + +/* return time offset between now and (future) check, in milliseconds */ +int ares__timeoffset(struct timeval *now, + struct timeval *check) +{ + int secs = (check->tv_sec - now->tv_sec); /* this many seconds */ + int us = (check->tv_usec - now->tv_usec); /* this many microseconds */ + + return secs*1000 + us/1000; /* return them combined as milliseconds */ +} + + /* Something interesting happened on the wire, or there was a timeout. * See what's up and respond accordingly. */ void ares_process(ares_channel channel, fd_set *read_fds, fd_set *write_fds) { - time_t now; - - time(&now); - write_tcp_data(channel, write_fds, ARES_SOCKET_BAD, now); - read_tcp_data(channel, read_fds, ARES_SOCKET_BAD, now); - read_udp_packets(channel, read_fds, ARES_SOCKET_BAD, now); - process_timeouts(channel, now); - process_broken_connections(channel, now); + struct timeval now = ares__tvnow(); + + write_tcp_data(channel, write_fds, ARES_SOCKET_BAD, &now); + read_tcp_data(channel, read_fds, ARES_SOCKET_BAD, &now); + read_udp_packets(channel, read_fds, ARES_SOCKET_BAD, &now); + process_timeouts(channel, &now); + process_broken_connections(channel, &now); } /* Something interesting happened on the wire, or there was a timeout. @@ -117,13 +165,12 @@ void ares_process_fd(ares_channel channel, file descriptors */ ares_socket_t write_fd) { - time_t now; + struct timeval now = ares__tvnow(); - time(&now); - write_tcp_data(channel, NULL, write_fd, now); - read_tcp_data(channel, NULL, read_fd, now); - read_udp_packets(channel, NULL, read_fd, now); - process_timeouts(channel, now); + write_tcp_data(channel, NULL, write_fd, &now); + read_tcp_data(channel, NULL, read_fd, &now); + read_udp_packets(channel, NULL, read_fd, &now); + process_timeouts(channel, &now); } @@ -158,7 +205,7 @@ static int try_again(int errnum) static void write_tcp_data(ares_channel channel, fd_set *write_fds, ares_socket_t write_fd, - time_t now) + struct timeval *now) { struct server_state *server; struct send_request *sendreq; @@ -177,7 +224,8 @@ static void write_tcp_data(ares_channel channel, /* Make sure server has data to send and is selected in write_fds or write_fd. */ server = &channel->servers[i]; - if (!server->qhead || server->tcp_socket == ARES_SOCKET_BAD || server->is_broken) + if (!server->qhead || server->tcp_socket == ARES_SOCKET_BAD || + server->is_broken) continue; if(write_fds) { @@ -281,7 +329,7 @@ static void advance_tcp_send_queue(ares_channel channel, int whichserver, * a packet if we finish reading one. */ static void read_tcp_data(ares_channel channel, fd_set *read_fds, - ares_socket_t read_fd, time_t now) + ares_socket_t read_fd, struct timeval *now) { struct server_state *server; int i; @@ -377,7 +425,7 @@ static void read_tcp_data(ares_channel channel, fd_set *read_fds, /* If any UDP sockets select true for reading, process them. */ static void read_udp_packets(ares_channel channel, fd_set *read_fds, - ares_socket_t read_fd, time_t now) + ares_socket_t read_fd, struct timeval *now) { struct server_state *server; int i; @@ -428,7 +476,7 @@ static void read_udp_packets(ares_channel channel, fd_set *read_fds, } /* If any queries have timed out, note the timeout and move them on. */ -static void process_timeouts(ares_channel channel, time_t now) +static void process_timeouts(ares_channel channel, struct timeval *now) { time_t t; /* the time of the timeouts we're processing */ struct query *query; @@ -441,14 +489,14 @@ static void process_timeouts(ares_channel channel, time_t now) * only a handful of requests that fall into the "now" bucket, so * this should be quite quick. */ - for (t = channel->last_timeout_processed; t <= now; t++) + for (t = channel->last_timeout_processed; t <= now->tv_sec; t++) { list_head = &(channel->queries_by_timeout[t % ARES_TIMEOUT_TABLE_SIZE]); for (list_node = list_head->next; list_node != list_head; ) { query = list_node->data; list_node = list_node->next; /* in case the query gets deleted */ - if (query->timeout != 0 && now >= query->timeout) + if (query->timeout.tv_sec && ares__timedout(now, &query->timeout)) { query->error_status = ARES_ETIMEOUT; ++query->timeouts; @@ -456,12 +504,13 @@ static void process_timeouts(ares_channel channel, time_t now) } } } - channel->last_timeout_processed = now; + channel->last_timeout_processed = now->tv_sec; } /* Handle an answer from a server. */ static void process_answer(ares_channel channel, unsigned char *abuf, - int alen, int whichserver, int tcp, time_t now) + int alen, int whichserver, int tcp, + struct timeval *now) { int tc, rcode; unsigned short id; @@ -539,7 +588,8 @@ static void process_answer(ares_channel channel, unsigned char *abuf, } /* Close all the connections that are no longer usable. */ -static void process_broken_connections(ares_channel channel, time_t now) +static void process_broken_connections(ares_channel channel, + struct timeval *now) { int i; for (i = 0; i < channel->nservers; i++) @@ -552,7 +602,8 @@ static void process_broken_connections(ares_channel channel, time_t now) } } -static void handle_error(ares_channel channel, int whichserver, time_t now) +static void handle_error(ares_channel channel, int whichserver, + struct timeval *now) { struct server_state *server; struct query *query; @@ -603,7 +654,8 @@ static void skip_server(ares_channel channel, struct query *query, } } -static void next_server(ares_channel channel, struct query *query, time_t now) +static void next_server(ares_channel channel, struct query *query, + struct timeval *now) { /* Advance to the next server or try. */ query->server++; @@ -640,7 +692,8 @@ static void next_server(ares_channel channel, struct query *query, time_t now) end_query(channel, query, query->error_status, NULL, 0); } -void ares__send_query(ares_channel channel, struct query *query, time_t now) +void ares__send_query(ares_channel channel, struct query *query, + struct timeval *now) { struct send_request *sendreq; struct server_state *server; @@ -707,16 +760,17 @@ void ares__send_query(ares_channel channel, struct query *query, time_t now) return; } } - query->timeout = now - + ((query->try == 0) ? channel->timeout - : channel->timeout << query->try / channel->nservers); + query->timeout = *now; + ares__timeadd(&query->timeout, + (query->try == 0) ? channel->timeout + : channel->timeout << query->try / channel->nservers); /* Keep track of queries bucketed by timeout, so we can process * timeout events quickly. */ ares__remove_from_list(&(query->queries_by_timeout)); ares__insert_in_list( &(query->queries_by_timeout), - &(channel->queries_by_timeout[query->timeout % + &(channel->queries_by_timeout[query->timeout.tv_sec % ARES_TIMEOUT_TABLE_SIZE])); /* Keep track of queries bucketed by server, so we can process server -- cgit v1.2.3