Patch for 7.21.3 is applied. All available tests for asynch resolvers are passed on my host (Linux Ubuntu), threaded as well as c-ares. Now the only few files depend on c-ares, and all of them are really depend on it, like version.c f.e. ... except easy.c, which contains never-evaluated #ifdef. Anyway, resolver customizing can now be isolated in curl_config.h, setup.h, and host....c files.

Probably more intensive memory-loss and early-stage-request-cancelation tests are required ...

Regards,
Vsevolod

10.01.2011 11:24, Daniel Stenberg пишет:
On Mon, 10 Jan 2011, Vsevolod Novikov wrote:

Suggestions.

Thank you!

[cut]

Yes, by making a (cleaner) internal API for async resolvers we could indeed make it much easier for people such as you to add custom ones. Your suggestion seems fair and even if I didn't review all the details, I think I agree with the general concept.

Of course, this is just a theory exercise until somebody actually steps up and produces code that adapts what we have to this/a new internal API. We did a cleanup of the SSL backend situation a while back in a similar spirit and that cleaned up a lot.

We don't have any outstanding bugs in our resolver code, for any of the backends, and we have very few users wanting to use a custom one so there's not a large push to make this happen.

Feel free to start working on this overhaul, I will certainly assist and review code and whatever as much as I can.

diff -r -u curl-7.21.3.orig/lib/easy.c curl-7.21.3/lib/easy.c
--- curl-7.21.3.orig/lib/easy.c	2010-12-07 16:52:44.000000000 +0300
+++ curl-7.21.3/lib/easy.c	2011-01-22 17:38:30.000000000 +0300
@@ -692,13 +692,6 @@
     outcurl->change.referer_alloc = TRUE;
   }
 
-#ifdef USE_ARES
-  /* If we use ares, we clone the ares channel for the new handle */
-  if(ARES_SUCCESS != ares_dup(&outcurl->state.areschannel,
-                              data->state.areschannel))
-    goto fail;
-#endif
-
 #if defined(CURL_DOES_CONVERSIONS) && defined(HAVE_ICONV)
   outcurl->inbound_cd = iconv_open(CURL_ICONV_CODESET_OF_HOST,
                                    CURL_ICONV_CODESET_OF_NETWORK);
diff -r -u curl-7.21.3.orig/lib/hostares.c curl-7.21.3/lib/hostares.c
--- curl-7.21.3.orig/lib/hostares.c	2010-12-06 01:17:39.000000000 +0300
+++ curl-7.21.3/lib/hostares.c	2011-01-22 17:45:44.000000000 +0300
@@ -86,6 +86,55 @@
 
 #ifdef CURLRES_ARES
 
+#include <ares.h>
+
+
+#if ARES_VERSION >= 0x010500
+/* c-ares 1.5.0 or later, the callback proto is modified */
+#define HAVE_CARES_CALLBACK_TIMEOUTS 1
+#endif
+
+struct ares_data {
+  ares_channel areschannel;
+};
+
+static struct ares_data * conn_ares_data(struct connectdata *conn)
+{
+  return (struct ares_data *)conn->async.os_specific;
+}
+
+static ares_channel conn_ares_channel(struct connectdata *conn)
+{
+  return conn_ares_data(conn)->areschannel;
+}
+
+/* Destroy ares data */
+static
+void destroy_ares_data(struct ares_data * tsd)
+{
+  if (tsd->areschannel)
+    ares_destroy(tsd->areschannel);
+
+  memset(tsd,0,sizeof(*tsd));
+}
+
+/* Initialize ares resolver data */
+static
+int init_ares_data(struct ares_data * tsd)
+{
+  int status;
+  memset(tsd, 0, sizeof(*tsd));
+  if((status = ares_init(&tsd->areschannel)) != ARES_SUCCESS) {
+    DEBUGF(fprintf(stderr, "Error: ares_init failed\n"));
+    if(status == ARES_ENOMEM)
+      return CURLE_OUT_OF_MEMORY;
+    else
+      return CURLE_FAILED_INIT;
+  }
+
+  return CURLE_OK;
+}
+
 /*
  * Curl_resolv_fdset() is called when someone from the outside world (using
  * curl_multi_fdset()) wants to get our fd_set setup and we're talking with
@@ -103,14 +152,14 @@
   struct timeval maxtime;
   struct timeval timebuf;
   struct timeval *timeout;
-  int max = ares_getsock(conn->data->state.areschannel,
+  int max = ares_getsock(conn_ares_channel(conn),
                          (ares_socket_t *)socks, numsocks);
 
 
   maxtime.tv_sec = CURL_TIMEOUT_RESOLVE;
   maxtime.tv_usec = 0;
 
-  timeout = ares_timeout(conn->data->state.areschannel, &maxtime, &timebuf);
+  timeout = ares_timeout(conn_ares_channel(conn), &maxtime, &timebuf);
 
   Curl_expire(conn->data,
               (timeout->tv_sec * 1000) + (timeout->tv_usec/1000));
@@ -130,7 +179,6 @@
 
 static int waitperform(struct connectdata *conn, int timeout_ms)
 {
-  struct SessionHandle *data = conn->data;
   int nfds;
   int bitmask;
   ares_socket_t socks[ARES_GETSOCK_MAXNUM];
@@ -138,7 +186,7 @@
   int i;
   int num = 0;
 
-  bitmask = ares_getsock(data->state.areschannel, socks, ARES_GETSOCK_MAXNUM);
+  bitmask = ares_getsock(conn_ares_channel(conn), socks, ARES_GETSOCK_MAXNUM);
 
   for(i=0; i < ARES_GETSOCK_MAXNUM; i++) {
     pfd[i].events = 0;
@@ -165,11 +213,11 @@
   if(!nfds)
     /* Call ares_process() unconditonally here, even if we simply timed out
        above, as otherwise the ares name resolve won't timeout! */
-    ares_process_fd(data->state.areschannel, ARES_SOCKET_BAD, ARES_SOCKET_BAD);
+    ares_process_fd(conn_ares_channel(conn), ARES_SOCKET_BAD, ARES_SOCKET_BAD);
   else {
     /* move through the descriptors and ask for processing on them */
     for(i=0; i < num; i++)
-      ares_process_fd(data->state.areschannel,
+      ares_process_fd(conn_ares_channel(conn),
                       pfd[i].revents & (POLLRDNORM|POLLIN)?
                       pfd[i].fd:ARES_SOCKET_BAD,
                       pfd[i].revents & (POLLWRNORM|POLLOUT)?
@@ -196,6 +244,7 @@
 
   if(conn->async.done) {
     /* we're done, kill the ares handle */
+    Curl_destroy_async_data(&conn->async);
     if(!conn->async.dns) {
       failf(data, "Could not resolve host: %s (%s)", conn->host.dispname,
             ares_strerror(conn->async.status));
@@ -208,6 +257,24 @@
 }
 
 /*
+ * Curl_destroy_async_data() cleans up async resolver data and thread handle.
+ * Complementary of ares_destroy.
+ */
+void Curl_destroy_async_data (struct Curl_async *async)
+{
+  if(async->hostname)
+    free(async->hostname);
+
+  if(async->os_specific) {
+    destroy_ares_data(async->os_specific);
+    free(async->os_specific);
+  }
+
+  async->hostname = NULL;
+  async->os_specific = NULL;
+}
+
+/*
  * Curl_wait_for_resolv() waits for a resolve to finish. This function should
  * be avoided since using this risk getting the multi interface to "hang".
  *
@@ -240,7 +307,7 @@
     store.tv_sec = itimeout/1000;
     store.tv_usec = (itimeout%1000)*1000;
 
-    tvp = ares_timeout(data->state.areschannel, &store, &tv);
+    tvp = ares_timeout(conn_ares_channel(conn), &store, &tv);
 
     /* use the timeout period ares returned to us above if less than one
        second is left, otherwise just use 1000ms to make sure the progress
@@ -267,11 +334,13 @@
     }
     if(timeout < 0) {
       /* our timeout, so we cancel the ares operation */
-      ares_cancel(data->state.areschannel);
+      ares_cancel(conn_ares_channel(conn));
       break;
     }
   }
 
+  Curl_destroy_async_data(&conn->async);
+
   /* Operation complete, if the lookup was successful we now have the entry
      in the cache. */
 
@@ -353,7 +422,6 @@
                                 int *waitp)
 {
   char *bufp;
-  struct SessionHandle *data = conn->data;
   struct in_addr in;
   int family = PF_INET;
 #ifdef ENABLE_IPV6 /* CURLRES_IPV6 */
@@ -402,8 +470,10 @@
     conn->async.status = 0;   /* clear */
     conn->async.dns = NULL;   /* clear */
 
-    /* areschannel is already setup in the Curl_open() function */
-    ares_gethostbyname(data->state.areschannel, hostname, family,
+    /* areschannel is NOT setup in the Curl_open() function NOW !!! */
+    conn->async.os_specific = calloc(1,sizeof(struct ares_data));
+    init_ares_data(conn->async.os_specific);
+    ares_gethostbyname(conn_ares_channel(conn), hostname, family,
                        (ares_host_callback)ares_query_completed_cb, conn);
 
     *waitp = 1; /* expect asynchronous response */
diff -r -u curl-7.21.3.orig/lib/hostip.h curl-7.21.3/lib/hostip.h
--- curl-7.21.3.orig/lib/hostip.h	2010-11-11 17:16:21.000000000 +0300
+++ curl-7.21.3/lib/hostip.h	2011-01-22 17:38:58.000000000 +0300
@@ -39,10 +39,6 @@
  * Comfortable CURLRES_* definitions are included from setup.h
  */
 
-#ifdef USE_ARES
-#include <ares_version.h>
-#endif
-
 /* Allocate enough memory to hold the full name information structs and
  * everything. OSF1 is known to require at least 8872 bytes. The buffer
  * required for storing all possible aliases and IP numbers is according to
@@ -53,16 +49,8 @@
 #define CURL_TIMEOUT_RESOLVE 300 /* when using asynch methods, we allow this
                                     many seconds for a name resolve */
 
-#ifdef CURLRES_ARES
-#define CURL_ASYNC_SUCCESS ARES_SUCCESS
-#if ARES_VERSION >= 0x010500
-/* c-ares 1.5.0 or later, the callback proto is modified */
-#define HAVE_CARES_CALLBACK_TIMEOUTS 1
-#endif
-#else
+#ifdef CURLRES_ASYNCH
 #define CURL_ASYNC_SUCCESS CURLE_OK
-#define ares_cancel(x) do {} while(0)
-#define ares_destroy(x) do {} while(0)
 #endif
 
 struct addrinfo;
@@ -195,11 +183,10 @@
                 const char *hostname, int port);
 
 /*
- * Curl_destroy_thread_data() cleans up async resolver data.
- * Complementary of ares_destroy.
+ * Curl_destroy_async_data() cleans up async resolver data.
  */
 struct Curl_async; /* forward-declaration */
-void Curl_destroy_thread_data(struct Curl_async *async);
+void Curl_destroy_async_data(struct Curl_async *async);
 
 #ifndef INADDR_NONE
 #define CURL_INADDR_NONE (in_addr_t) ~0
diff -r -u curl-7.21.3.orig/lib/hostthre.c curl-7.21.3/lib/hostthre.c
--- curl-7.21.3.orig/lib/hostthre.c	2010-11-11 17:16:21.000000000 +0300
+++ curl-7.21.3/lib/hostthre.c	2011-01-22 13:21:23.000000000 +0300
@@ -253,10 +253,10 @@
 #endif /* HAVE_GETADDRINFO */
 
 /*
- * Curl_destroy_thread_data() cleans up async resolver data and thread handle.
+ * Curl_destroy_async_data() cleans up async resolver data and thread handle.
  * Complementary of ares_destroy.
  */
-void Curl_destroy_thread_data (struct Curl_async *async)
+void Curl_destroy_async_data (struct Curl_async *async)
 {
   if(async->hostname)
     free(async->hostname);
@@ -336,7 +336,7 @@
   return TRUE;
 
  err_exit:
-  Curl_destroy_thread_data(&conn->async);
+  Curl_destroy_async_data(&conn->async);
 
   SET_ERRNO(err);
 
@@ -386,7 +386,7 @@
     }
   }
 
-  Curl_destroy_thread_data(&conn->async);
+  Curl_destroy_async_data(&conn->async);
 
   if(!conn->async.dns)
     conn->bits.close = TRUE;
@@ -419,7 +419,7 @@
 
   if (done) {
     getaddrinfo_complete(conn);
-    Curl_destroy_thread_data(&conn->async);
+    Curl_destroy_async_data(&conn->async);
 
     if(!conn->async.dns) {
       failf(data, "Could not resolve host: %s; %s",
diff -r -u curl-7.21.3.orig/lib/url.c curl-7.21.3/lib/url.c
--- curl-7.21.3.orig/lib/url.c	2010-12-15 12:04:43.000000000 +0300
+++ curl-7.21.3/lib/url.c	2011-01-22 17:52:04.000000000 +0300
@@ -555,9 +555,6 @@
   Curl_safefree(data->info.contenttype);
   Curl_safefree(data->info.wouldredirect);
 
-  /* this destroys the channel and we cannot use it anymore after this */
-  ares_destroy(data->state.areschannel);
-
 #if defined(CURL_DOES_CONVERSIONS) && defined(HAVE_ICONV)
   /* close iconv conversion descriptors */
   if(data->inbound_cd != (iconv_t)-1) {
@@ -804,9 +801,6 @@
 {
   CURLcode res = CURLE_OK;
   struct SessionHandle *data;
-#ifdef USE_ARES
-  int status;
-#endif
 
   /* Very simple start-up: alloc the struct, init it with zeroes and return */
   data = calloc(1, sizeof(struct SessionHandle));
@@ -818,19 +812,6 @@
 
   data->magic = CURLEASY_MAGIC_NUMBER;
 
-#ifdef USE_ARES
-  if((status = ares_init(&data->state.areschannel)) != ARES_SUCCESS) {
-    DEBUGF(fprintf(stderr, "Error: ares_init failed\n"));
-    free(data);
-    if(status == ARES_ENOMEM)
-      return CURLE_OUT_OF_MEMORY;
-    else
-      return CURLE_FAILED_INIT;
-  }
-  /* make sure that all other returns from this function should destroy the
-     ares channel before returning error! */
-#endif
-
   /* We do some initial setup here, all those fields that can't be just 0 */
 
   data->state.headerbuff = malloc(HEADERSIZE);
@@ -866,7 +847,6 @@
   }
 
   if(res) {
-    ares_destroy(data->state.areschannel);
     if(data->state.headerbuff)
       free(data->state.headerbuff);
     Curl_freeset(data);
@@ -2575,11 +2555,8 @@
   Curl_llist_destroy(conn->done_pipe, NULL);
 
   /* possible left-overs from the async name resolvers */
-#if defined(CURLRES_THREADED)
-  Curl_destroy_thread_data(&conn->async);
-#elif defined(CURLRES_ASYNCH)
-  Curl_safefree(conn->async.hostname);
-  Curl_safefree(conn->async.os_specific);
+#if defined(CURLRES_ASYNCH)
+  Curl_destroy_async_data(&conn->async);
 #endif
 
   Curl_free_ssl_config(&conn->ssl_config);
@@ -5210,10 +5187,6 @@
     data->state.tempwrite = NULL;
   }
 
-  /* for ares-using, make sure all possible outstanding requests are properly
-     cancelled before we proceed */
-  ares_cancel(data->state.areschannel);
-
   /* if data->set.reuse_forbid is TRUE, it means the libcurl client has
      forced us to close this no matter what we think.
 
diff -r -u curl-7.21.3.orig/lib/urldata.h curl-7.21.3/lib/urldata.h
--- curl-7.21.3.orig/lib/urldata.h	2010-12-15 16:49:10.000000000 +0300
+++ curl-7.21.3/lib/urldata.h	2011-01-22 17:41:15.000000000 +0300
@@ -134,14 +134,6 @@
 #endif
 #endif
 
-#ifdef USE_ARES
-#  if defined(CURL_STATICLIB) && !defined(CARES_STATICLIB) && \
-     (defined(WIN32) || defined(_WIN32) || defined(__SYMBIAN32__))
-#    define CARES_STATICLIB
-#  endif
-#  include <ares.h>
-#endif
-
 #include <curl/curl.h>
 
 #include "http_chunks.h" /* for the structs and enum stuff */
@@ -1115,10 +1107,6 @@
 
   bool authproblem; /* TRUE if there's some problem authenticating */
 
-#ifdef USE_ARES
-  ares_channel areschannel; /* for name resolves */
-#endif
-
 #if defined(USE_SSLEAY) && defined(HAVE_OPENSSL_ENGINE_H)
   ENGINE *engine;
 #endif /* USE_SSLEAY */
-------------------------------------------------------------------
List admin: http://cool.haxx.se/list/listinfo/curl-library
Etiquette:  http://curl.haxx.se/mail/etiquette.html

Reply via email to