- Introducing millisecond resolution support for the timeout option. See

ares_init_options()'s ARES_OPT_TIMEOUTMS.
This commit is contained in:
Daniel Stenberg 2008-05-13 20:48:48 +00:00
parent 80afddacc8
commit 76c251513e
8 changed files with 166 additions and 71 deletions

View File

@ -1,5 +1,10 @@
Changelog for the c-ares project
* May 13 2008 (Daniel Stenberg)
- Introducing millisecond resolution support for the timeout option. See
ares_init_options()'s ARES_OPT_TIMEOUTMS.
* May 9 2008 (Yang Tse)
- Use monotonic time source if available, for private function ares__tvnow()

View File

@ -1,6 +1,7 @@
/* $Id$ */
/* Copyright 1998 by the Massachusetts Institute of Technology.
* Copyright (C) 2007 by Daniel Stenberg
*
* Permission to use, copy, modify, and distribute this
* software and its documentation for any purpose and without
@ -112,6 +113,7 @@ extern "C" {
#define ARES_OPT_SORTLIST (1 << 10)
#define ARES_OPT_SOCK_SNDBUF (1 << 11)
#define ARES_OPT_SOCK_RCVBUF (1 << 12)
#define ARES_OPT_TIMEOUTMS (1 << 13)
/* Nameinfo flag values */
#define ARES_NI_NOFQDN (1 << 0)
@ -179,7 +181,7 @@ struct apattern;
struct ares_options {
int flags;
int timeout;
int timeout; /* in seconds or milliseconds, depending on options */
int tries;
int ndots;
unsigned short udp_port;

View File

@ -1,6 +1,7 @@
.\" $Id$
.\"
.\" Copyright 1998 by the Massachusetts Institute of Technology.
.\" Copyright (C) 2007-2008 by Daniel Stenberg
.\"
.\" Permission to use, copy, modify, and distribute this
.\" software and its documentation for any purpose and without
@ -14,7 +15,7 @@
.\" this software for any purpose. It is provided "as is"
.\" without express or implied warranty.
.\"
.TH ARES_INIT 3 "7 December 2004"
.TH ARES_INIT 3 "13 May 2008"
.SH NAME
ares_init, ares_init_options \- Initialize a resolver channel
.SH SYNOPSIS
@ -49,10 +50,22 @@ description of possible flag values.
.B ARES_OPT_TIMEOUT
.B int \fItimeout\fP;
.br
The number of seconds each name server is given to respond to a query
on the first try. (After the first try, the timeout algorithm becomes
more complicated, but scales linearly with the value of
\fItimeout\fP.) The default is five seconds.
The number of seconds each name server is given to respond to a query on the
first try. (After the first try, the timeout algorithm becomes more
complicated, but scales linearly with the value of \fItimeout\fP.) The
default is five seconds. This option is being deprecated by
\fIARES_OPT_TIMEOUTMS\fP starting in c-ares 1.5.2.
.TP 18
.B ARES_OPT_TIMEOUTMS
.B int \fItimeout\fP;
.br
The number of milliseconds each name server is given to respond to a query on
the first try. (After the first try, the timeout algorithm becomes more
complicated, but scales linearly with the value of \fItimeout\fP.) The
default is five seconds. Note that this option is specified with the same
struct field as the former \fIARES_OPT_TIMEOUT\fP, it is but the option bits
that tell c-ares how to interpret the number. This option was added in c-ares
1.5.2.
.TP 18
.B ARES_OPT_TRIES
.B int \fItries\fP;

View File

@ -1,6 +1,7 @@
/* $Id$ */
/* Copyright 1998 by the Massachusetts Institute of Technology.
* Copyright (C) 2007-2008 by Daniel Stenberg
*
* Permission to use, copy, modify, and distribute this
* software and its documentation for any purpose and without
@ -254,13 +255,16 @@ int ares_save_options(ares_channel channel, struct ares_options *options,
if (!ARES_CONFIG_CHECK(channel))
return ARES_ENODATA;
(*optmask) = (ARES_OPT_FLAGS|ARES_OPT_TIMEOUT|ARES_OPT_TRIES|ARES_OPT_NDOTS|
(*optmask) = (ARES_OPT_FLAGS|ARES_OPT_TRIES|ARES_OPT_NDOTS|
ARES_OPT_UDP_PORT|ARES_OPT_TCP_PORT|ARES_OPT_SOCK_STATE_CB|
ARES_OPT_SERVERS|ARES_OPT_DOMAINS|ARES_OPT_LOOKUPS|
ARES_OPT_SORTLIST);
ARES_OPT_SORTLIST|ARES_OPT_TIMEOUTMS);
/* Copy easy stuff */
options->flags = channel->flags;
/* We return full millisecond resolution but that's only because we don't
set the ARES_OPT_TIMEOUT anymore, only the new ARES_OPT_TIMEOUTMS */
options->timeout = channel->timeout;
options->tries = channel->tries;
options->ndots = channel->ndots;
@ -328,8 +332,10 @@ static int init_by_options(ares_channel channel,
/* Easy stuff. */
if ((optmask & ARES_OPT_FLAGS) && channel->flags == -1)
channel->flags = options->flags;
if ((optmask & ARES_OPT_TIMEOUT) && channel->timeout == -1)
if ((optmask & ARES_OPT_TIMEOUTMS) && channel->timeout == -1)
channel->timeout = options->timeout;
else if ((optmask & ARES_OPT_TIMEOUT) && channel->timeout == -1)
channel->timeout = options->timeout * 1000;
if ((optmask & ARES_OPT_TRIES) && channel->tries == -1)
channel->tries = options->tries;
if ((optmask & ARES_OPT_NDOTS) && channel->ndots == -1)

View File

@ -4,6 +4,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
@ -48,7 +49,7 @@
#include <time.h>
#endif
#define DEFAULT_TIMEOUT 5
#define DEFAULT_TIMEOUT 5000 /* milliseconds */
#define DEFAULT_TRIES 4
#ifndef INADDR_NONE
#define INADDR_NONE 0xffffffff
@ -149,7 +150,7 @@ struct server_state {
struct query {
/* Query ID from qbuf, for faster lookup, and current timeout */
unsigned short qid;
time_t timeout;
struct timeval timeout;
/*
* Links for the doubly-linked lists in which we insert a query.
@ -217,7 +218,7 @@ typedef struct rc4_key
struct ares_channeldata {
/* Configuration data */
int flags;
int timeout;
int timeout; /* in milliseconds */
int tries;
int ndots;
int udp_port;
@ -242,7 +243,8 @@ struct ares_channeldata {
/* Generation number to use for the next TCP socket open/close */
int tcp_connection_generation;
/* The time at which we last called process_timeouts() */
/* The time at which we last called process_timeouts(). Uses integer seconds
just to draw the line somewhere. */
time_t last_timeout_processed;
/* Circular, doubly-linked list of queries, bucketed various ways.... */
@ -259,8 +261,18 @@ struct ares_channeldata {
void *sock_state_cb_data;
};
/* return true if now is exactly check time or later */
int ares__timedout(struct timeval *now,
struct timeval *check);
/* add the specific number of milliseconds to the time in the first argument */
int ares__timeadd(struct timeval *now,
int millisecs);
/* return time offset between now and (future) check, in milliseconds */
int ares__timeoffset(struct timeval *now,
struct timeval *check);
void ares__rc4(rc4_key* key,unsigned char *buffer_ptr, int buffer_len);
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);
void ares__close_sockets(ares_channel channel, struct server_state *server);
int ares__get_hostent(FILE *fp, int family, struct hostent **host);
int ares__read_line(FILE *fp, char **buf, int *bufsize);

View File

@ -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 <arpa/nameser_compat.h>
#endif
#ifdef HAVE_SYS_TIME_H
#include <sys/time.h>
#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;
struct timeval now = ares__tvnow();
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);
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

View File

@ -39,7 +39,7 @@ void ares_send(ares_channel channel, const unsigned char *qbuf, int qlen,
{
struct query *query;
int i;
time_t now;
struct timeval now;
/* Verify that the query is at least long enough to hold the header. */
if (qlen < HFIXEDSZ || qlen >= (1 << 16))
@ -74,7 +74,7 @@ void ares_send(ares_channel channel, const unsigned char *qbuf, int qlen,
/* Compute the query ID. Start with no timeout. */
query->qid = (unsigned short)DNS_HEADER_QID(qbuf);
query->timeout = 0;
query->timeout.tv_sec = query->timeout.tv_usec = 0;
/* Form the TCP query buffer by prepending qlen (as two
* network-order bytes) to qbuf.
@ -107,17 +107,17 @@ void ares_send(ares_channel channel, const unsigned char *qbuf, int qlen,
ares__init_list_node(&(query->queries_by_timeout), query);
ares__init_list_node(&(query->queries_to_server), query);
ares__init_list_node(&(query->all_queries), query);
/* Chain the query into the list of all queries. */
ares__insert_in_list(&(query->all_queries), &(channel->all_queries));
/* Keep track of queries bucketed by qid, so we can process DNS
* responses quickly.
*/
ares__insert_in_list(
&(query->queries_by_qid),
&(channel->queries_by_qid[query->qid % ARES_QID_TABLE_SIZE]));
&(query->queries_by_qid),
&(channel->queries_by_qid[query->qid % ARES_QID_TABLE_SIZE]));
/* Perform the first query action. */
time(&now);
ares__send_query(channel, query, now);
now = ares__tvnow();
ares__send_query(channel, query, &now);
}

View File

@ -37,16 +37,16 @@ struct timeval *ares_timeout(ares_channel channel, struct timeval *maxtv,
struct query *query;
struct list_node* list_head;
struct list_node* list_node;
time_t now;
time_t offset, min_offset; /* these use time_t since some 32 bit systems
still use 64 bit time_t! (like VS2005) */
struct timeval now;
struct timeval nextstop;
long offset, min_offset;
/* No queries, no timeout (and no fetch of the current time). */
if (ares__is_list_empty(&(channel->all_queries)))
return maxtv;
/* Find the minimum timeout for the current set of queries. */
time(&now);
now = ares__tvnow();
min_offset = -1;
list_head = &(channel->all_queries);
@ -54,23 +54,26 @@ struct timeval *ares_timeout(ares_channel channel, struct timeval *maxtv,
list_node = list_node->next)
{
query = list_node->data;
if (query->timeout == 0)
if (query->timeout.tv_sec == 0)
continue;
offset = query->timeout - now;
offset = ares__timeoffset(&now, &query->timeout);
if (offset < 0)
offset = 0;
if (min_offset == -1 || offset < min_offset)
min_offset = offset;
}
/* If we found a minimum timeout and it's sooner than the one
* specified in maxtv (if any), return it. Otherwise go with
* maxtv.
if(min_offset != -1) {
nextstop = now;
ares__timeadd(&now, min_offset);
}
/* If we found a minimum timeout and it's sooner than the one specified in
* maxtv (if any), return it. Otherwise go with maxtv.
*/
if (min_offset != -1 && (!maxtv || min_offset <= maxtv->tv_sec))
if (min_offset != -1 && (!maxtv || ares__timedout(maxtv, &nextstop)))
{
tvbuf->tv_sec = (long)min_offset;
tvbuf->tv_usec = 0;
*tvbuf = nextstop;
return tvbuf;
}
else