- Introducing millisecond resolution support for the timeout option. See
authorDaniel Stenberg <daniel@haxx.se>
Tue, 13 May 2008 20:48:48 +0000 (20:48 +0000)
committerDaniel Stenberg <daniel@haxx.se>
Tue, 13 May 2008 20:48:48 +0000 (20:48 +0000)
  ares_init_options()'s ARES_OPT_TIMEOUTMS.

CHANGES
ares.h
ares_init.3
ares_init.c
ares_private.h
ares_process.c
ares_send.c
ares_timeout.c

diff --git a/CHANGES b/CHANGES
index 3d44b19..db3d035 100644 (file)
--- a/CHANGES
+++ b/CHANGES
@@ -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()
diff --git a/ares.h b/ares.h
index 6401019..3609f86 100644 (file)
--- a/ares.h
+++ b/ares.h
@@ -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;
index 026d8da..428cb03 100644 (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;
index 9b655a8..2781813 100644 (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)
index 9354e63..6ec4dc4 100644 (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);
index 442e328..eca363a 100644 (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
 
 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
index 2007321..fe921b6 100644 (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);
 }
index 12b93b0..ce98491 100644 (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