Commit 23f5d145 authored by Steinar H. Gunderson's avatar Steinar H. Gunderson
Browse files

Previously, processing a large batch of timeouts was O(n^2) in the number of

outstanding queries, and processing a DNS response packet was O(n) in the
number of outstanding queries. To speed things up in Google, we added a few circular,
doubly-linked lists of queries that are hash-bucketed based on
the attributes we care about, so most important operations are now O(1).

It might be that the number of buckets are higher than most people would need,
but on a quick calculation it should only be 100kB or so even on a 64-bit
system, so I've let it stay as-is.
parent b01ab652
Loading
Loading
Loading
Loading
+23 −7
Original line number Diff line number Diff line
@@ -14,6 +14,7 @@
 */

#include "setup.h"
#include <assert.h>
#include <stdlib.h>
#include "ares.h"
#include "ares_private.h"
@@ -25,18 +26,33 @@
 */
void ares_cancel(ares_channel channel)
{
  struct query *query, *next;
  struct query *query;
  struct list_node* list_head;
  struct list_node* list_node;
  int i;

  for (query = channel->queries; query; query = next)
  list_head = &(channel->all_queries);
  for (list_node = list_head->next; list_node != list_head; )
  {
    next = query->next;
    query = list_node->data;
    list_node = list_node->next;  /* since we're deleting the query */
    query->callback(query->arg, ARES_ETIMEOUT, 0, NULL, 0);
    free(query->tcpbuf);
    free(query->server_info);
    free(query);
    ares__free_query(query);
  }
  channel->queries = NULL;
#ifndef NDEBUG
  /* Freeing the query should remove it from all the lists in which it sits,
   * so all query lists should be empty now.
   */
  assert(ares__is_list_empty(&(channel->all_queries)));
  for (i = 0; i < ARES_QID_TABLE_SIZE; i++)
    {
      assert(ares__is_list_empty(&(channel->queries_by_qid[i])));
    }
  for (i = 0; i < ARES_TIMEOUT_TABLE_SIZE; i++)
    {
      assert(ares__is_list_empty(&(channel->queries_by_timeout[i])));
    }
#endif
  if (!(channel->flags & ARES_FLAG_STAYOPEN))
  {
    if (channel->servers)
+31 −12
Original line number Diff line number Diff line
@@ -16,6 +16,7 @@
 */

#include "setup.h"
#include <assert.h>
#include <stdlib.h>
#include "ares.h"
#include "ares_private.h"
@@ -37,13 +38,42 @@ void ares_destroy(ares_channel channel)
{
  int i;
  struct query *query;
  struct list_node* list_head;
  struct list_node* list_node;

  list_head = &(channel->all_queries);
  for (list_node = list_head->next; list_node != list_head; )
    {
      query = list_node->data;
      list_node = list_node->next;  /* since we're deleting the query */
      query->callback(query->arg, ARES_EDESTRUCTION, 0, NULL, 0);
      ares__free_query(query);
    }
#ifndef NDEBUG
  /* Freeing the query should remove it from all the lists in which it sits,
   * so all query lists should be empty now.
   */
  assert(ares__is_list_empty(&(channel->all_queries)));
  for (i = 0; i < ARES_QID_TABLE_SIZE; i++)
    {
      assert(ares__is_list_empty(&(channel->queries_by_qid[i])));
    }
  for (i = 0; i < ARES_TIMEOUT_TABLE_SIZE; i++)
    {
      assert(ares__is_list_empty(&(channel->queries_by_timeout[i])));
    }
#endif

  if (!channel)
    return;

  if (channel->servers) {
    for (i = 0; i < channel->nservers; i++)
      ares__close_sockets(channel, &channel->servers[i]);
      {
        struct server_state *server = &channel->servers[i];
        ares__close_sockets(channel, server);
        assert(ares__is_list_empty(&(server->queries_to_server)));
      }
    free(channel->servers);
  }

@@ -59,16 +89,5 @@ void ares_destroy(ares_channel channel)
  if (channel->lookups)
    free(channel->lookups);

  while (channel->queries) {
    query = channel->queries;
    channel->queries = query->next;
    query->callback(query->arg, ARES_EDESTRUCTION, 0, NULL, 0);
    if (query->tcpbuf)
      free(query->tcpbuf);
    if (query->server_info)
      free(query->server_info);
    free(query);
  }

  free(channel);
}
+4 −1
Original line number Diff line number Diff line
@@ -30,6 +30,9 @@ int ares_fds(ares_channel channel, fd_set *read_fds, fd_set *write_fds)
  ares_socket_t nfds;
  int i;

  /* Are there any active queries? */
  int active_queries = !ares__is_list_empty(&(channel->all_queries));

  nfds = 0;
  for (i = 0; i < channel->nservers; i++)
    {
@@ -37,7 +40,7 @@ int ares_fds(ares_channel channel, fd_set *read_fds, fd_set *write_fds)
      /* We only need to register interest in UDP sockets if we have
       * outstanding queries.
       */
      if (channel->queries && server->udp_socket != ARES_SOCKET_BAD)
      if (active_queries && server->udp_socket != ARES_SOCKET_BAD)
        {
          FD_SET(server->udp_socket, read_fds);
          if (server->udp_socket >= nfds)
+1 −1
Original line number Diff line number Diff line
@@ -35,7 +35,7 @@ int ares_getsock(ares_channel channel,
  ares_socket_t *socks = (ares_socket_t *)s;

  /* No queries, no file descriptors. */
  if (!channel->queries)
  if (ares__is_list_empty(&(channel->all_queries)))
    return 0;

  for (i = 0;
+17 −2
Original line number Diff line number Diff line
@@ -107,6 +107,7 @@ int ares_init_options(ares_channel *channelptr, struct ares_options *options,
  int i;
  int status = ARES_SUCCESS;
  struct server_state *server;
  struct timeval tv;

#ifdef CURLDEBUG
  const char *env = getenv("CARES_MEMDEBUG");
@@ -140,13 +141,26 @@ int ares_init_options(ares_channel *channelptr, struct ares_options *options,
  channel->nsort = -1;
  channel->tcp_connection_generation = 0;
  channel->lookups = NULL;
  channel->queries = NULL;
  channel->domains = NULL;
  channel->sortlist = NULL;
  channel->servers = NULL;
  channel->sock_state_cb = NULL;
  channel->sock_state_cb_data = NULL;

  gettimeofday(&tv, NULL);
  channel->last_timeout_processed = tv.tv_sec;

  /* Initialize our lists of queries */
  ares__init_list_head(&(channel->all_queries));
  for (i = 0; i < ARES_QID_TABLE_SIZE; i++)
    {
      ares__init_list_head(&(channel->queries_by_qid[i]));
    }
  for (i = 0; i < ARES_TIMEOUT_TABLE_SIZE; i++)
    {
      ares__init_list_head(&(channel->queries_by_timeout[i]));
    }

  /* Initialize configuration by each of the four sources, from highest
   * precedence to lowest.
   */
@@ -209,13 +223,14 @@ int ares_init_options(ares_channel *channelptr, struct ares_options *options,
      server->tcp_buffer = NULL;
      server->qhead = NULL;
      server->qtail = NULL;
      ares__init_list_head(&(server->queries_to_server));
      server->channel = channel;
      server->is_broken = 0;
    }

  init_id_key(&channel->id_key, ARES_ID_KEY_LEN);

  channel->next_id = ares__generate_new_id(&channel->id_key);
  channel->queries = NULL;

  *channelptr = channel;
  return ARES_SUCCESS;
Loading