quickjs-tart

quickjs-based runtime for wallet-core logic
Log | Files | Refs | README | LICENSE

asyn-thrdd.c (21160B)


      1 /***************************************************************************
      2  *                                  _   _ ____  _
      3  *  Project                     ___| | | |  _ \| |
      4  *                             / __| | | | |_) | |
      5  *                            | (__| |_| |  _ <| |___
      6  *                             \___|\___/|_| \_\_____|
      7  *
      8  * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
      9  *
     10  * This software is licensed as described in the file COPYING, which
     11  * you should have received as part of this distribution. The terms
     12  * are also available at https://curl.se/docs/copyright.html.
     13  *
     14  * You may opt to use, copy, modify, merge, publish, distribute and/or sell
     15  * copies of the Software, and permit persons to whom the Software is
     16  * furnished to do so, under the terms of the COPYING file.
     17  *
     18  * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
     19  * KIND, either express or implied.
     20  *
     21  * SPDX-License-Identifier: curl
     22  *
     23  ***************************************************************************/
     24 
     25 #include "curl_setup.h"
     26 #include "socketpair.h"
     27 
     28 /***********************************************************************
     29  * Only for threaded name resolves builds
     30  **********************************************************************/
     31 #ifdef CURLRES_THREADED
     32 
     33 #ifdef HAVE_NETINET_IN_H
     34 #include <netinet/in.h>
     35 #endif
     36 #ifdef HAVE_NETDB_H
     37 #include <netdb.h>
     38 #endif
     39 #ifdef HAVE_ARPA_INET_H
     40 #include <arpa/inet.h>
     41 #endif
     42 #ifdef __VMS
     43 #include <in.h>
     44 #include <inet.h>
     45 #endif
     46 
     47 #if defined(USE_THREADS_POSIX) && defined(HAVE_PTHREAD_H)
     48 #  include <pthread.h>
     49 #endif
     50 
     51 #ifdef HAVE_GETADDRINFO
     52 #  define RESOLVER_ENOMEM  EAI_MEMORY  /* = WSA_NOT_ENOUGH_MEMORY on Windows */
     53 #else
     54 #  define RESOLVER_ENOMEM  SOCKENOMEM
     55 #endif
     56 
     57 #include "urldata.h"
     58 #include "cfilters.h"
     59 #include "sendf.h"
     60 #include "hostip.h"
     61 #include "hash.h"
     62 #include "share.h"
     63 #include "url.h"
     64 #include "multiif.h"
     65 #include "curl_threads.h"
     66 #include "strdup.h"
     67 
     68 #ifdef USE_ARES
     69 #include <ares.h>
     70 #ifdef USE_HTTPSRR
     71 #define USE_HTTPSRR_ARES  /* the combo */
     72 #endif
     73 #endif
     74 
     75 /* The last 3 #include files should be in this order */
     76 #include "curl_printf.h"
     77 #include "curl_memory.h"
     78 #include "memdebug.h"
     79 
     80 
     81 /*
     82  * Curl_async_global_init()
     83  * Called from curl_global_init() to initialize global resolver environment.
     84  * Does nothing here.
     85  */
     86 int Curl_async_global_init(void)
     87 {
     88 #if defined(USE_ARES) && defined(CARES_HAVE_ARES_LIBRARY_INIT)
     89   if(ares_library_init(ARES_LIB_INIT_ALL)) {
     90     return CURLE_FAILED_INIT;
     91   }
     92 #endif
     93   return CURLE_OK;
     94 }
     95 
     96 /*
     97  * Curl_async_global_cleanup()
     98  * Called from curl_global_cleanup() to destroy global resolver environment.
     99  * Does nothing here.
    100  */
    101 void Curl_async_global_cleanup(void)
    102 {
    103 #if defined(USE_ARES) && defined(CARES_HAVE_ARES_LIBRARY_INIT)
    104   ares_library_cleanup();
    105 #endif
    106 }
    107 
    108 static void async_thrdd_destroy(struct Curl_easy *);
    109 
    110 CURLcode Curl_async_get_impl(struct Curl_easy *data, void **impl)
    111 {
    112   (void)data;
    113   *impl = NULL;
    114   return CURLE_OK;
    115 }
    116 
    117 /* Destroy context of threaded resolver */
    118 static void addr_ctx_destroy(struct async_thrdd_addr_ctx *addr_ctx)
    119 {
    120   if(addr_ctx) {
    121     DEBUGASSERT(!addr_ctx->ref_count);
    122     Curl_mutex_destroy(&addr_ctx->mutx);
    123     free(addr_ctx->hostname);
    124     if(addr_ctx->res)
    125       Curl_freeaddrinfo(addr_ctx->res);
    126 #ifndef CURL_DISABLE_SOCKETPAIR
    127   /*
    128    * close one end of the socket pair (may be done in resolver thread);
    129    * the other end (for reading) is always closed in the parent thread.
    130    */
    131 #ifndef USE_EVENTFD
    132   if(addr_ctx->sock_pair[1] != CURL_SOCKET_BAD) {
    133     wakeup_close(addr_ctx->sock_pair[1]);
    134   }
    135 #endif
    136 #endif
    137     free(addr_ctx);
    138   }
    139 }
    140 
    141 /* Initialize context for threaded resolver */
    142 static struct async_thrdd_addr_ctx *
    143 addr_ctx_create(const char *hostname, int port,
    144                 const struct addrinfo *hints)
    145 {
    146   struct async_thrdd_addr_ctx *addr_ctx = calloc(1, sizeof(*addr_ctx));
    147   if(!addr_ctx)
    148     return NULL;
    149 
    150   addr_ctx->thread_hnd = curl_thread_t_null;
    151   addr_ctx->port = port;
    152 #ifndef CURL_DISABLE_SOCKETPAIR
    153   addr_ctx->sock_pair[0] = CURL_SOCKET_BAD;
    154   addr_ctx->sock_pair[1] = CURL_SOCKET_BAD;
    155 #endif
    156   addr_ctx->ref_count = 0;
    157 
    158 #ifdef HAVE_GETADDRINFO
    159   DEBUGASSERT(hints);
    160   addr_ctx->hints = *hints;
    161 #else
    162   (void) hints;
    163 #endif
    164 
    165   Curl_mutex_init(&addr_ctx->mutx);
    166 
    167 #ifndef CURL_DISABLE_SOCKETPAIR
    168   /* create socket pair or pipe */
    169   if(wakeup_create(addr_ctx->sock_pair, FALSE) < 0) {
    170     addr_ctx->sock_pair[0] = CURL_SOCKET_BAD;
    171     addr_ctx->sock_pair[1] = CURL_SOCKET_BAD;
    172     goto err_exit;
    173   }
    174 #endif
    175   addr_ctx->sock_error = CURL_ASYNC_SUCCESS;
    176 
    177   /* Copying hostname string because original can be destroyed by parent
    178    * thread during gethostbyname execution.
    179    */
    180   addr_ctx->hostname = strdup(hostname);
    181   if(!addr_ctx->hostname)
    182     goto err_exit;
    183 
    184   addr_ctx->ref_count = 1;
    185   return addr_ctx;
    186 
    187 err_exit:
    188 #ifndef CURL_DISABLE_SOCKETPAIR
    189   if(addr_ctx->sock_pair[0] != CURL_SOCKET_BAD) {
    190     wakeup_close(addr_ctx->sock_pair[0]);
    191     addr_ctx->sock_pair[0] = CURL_SOCKET_BAD;
    192   }
    193 #endif
    194   addr_ctx_destroy(addr_ctx);
    195   return NULL;
    196 }
    197 
    198 #ifdef HAVE_GETADDRINFO
    199 
    200 /*
    201  * getaddrinfo_thread() resolves a name and then exits.
    202  *
    203  * For builds without ARES, but with USE_IPV6, create a resolver thread
    204  * and wait on it.
    205  */
    206 static
    207 #if defined(CURL_WINDOWS_UWP) || defined(UNDER_CE)
    208 DWORD
    209 #else
    210 unsigned int
    211 #endif
    212 CURL_STDCALL getaddrinfo_thread(void *arg)
    213 {
    214   struct async_thrdd_addr_ctx *addr_ctx = arg;
    215   char service[12];
    216   int rc;
    217   bool all_gone;
    218 
    219   msnprintf(service, sizeof(service), "%d", addr_ctx->port);
    220 
    221   rc = Curl_getaddrinfo_ex(addr_ctx->hostname, service,
    222                            &addr_ctx->hints, &addr_ctx->res);
    223 
    224   if(rc) {
    225     addr_ctx->sock_error = SOCKERRNO ? SOCKERRNO : rc;
    226     if(addr_ctx->sock_error == 0)
    227       addr_ctx->sock_error = RESOLVER_ENOMEM;
    228   }
    229   else {
    230     Curl_addrinfo_set_port(addr_ctx->res, addr_ctx->port);
    231   }
    232 
    233   Curl_mutex_acquire(&addr_ctx->mutx);
    234   if(addr_ctx->ref_count > 1) {
    235     /* Someone still waiting on our results. */
    236 #ifndef CURL_DISABLE_SOCKETPAIR
    237     if(addr_ctx->sock_pair[1] != CURL_SOCKET_BAD) {
    238 #ifdef USE_EVENTFD
    239       const uint64_t buf[1] = { 1 };
    240 #else
    241       const char buf[1] = { 1 };
    242 #endif
    243       /* DNS has been resolved, signal client task */
    244       if(wakeup_write(addr_ctx->sock_pair[1], buf, sizeof(buf)) < 0) {
    245         /* update sock_erro to errno */
    246         addr_ctx->sock_error = SOCKERRNO;
    247       }
    248     }
    249 #endif
    250   }
    251   /* thread gives up its reference to the shared data now. */
    252   --addr_ctx->ref_count;
    253   all_gone = !addr_ctx->ref_count;
    254   Curl_mutex_release(&addr_ctx->mutx);
    255   if(all_gone)
    256     addr_ctx_destroy(addr_ctx);
    257 
    258   return 0;
    259 }
    260 
    261 #else /* HAVE_GETADDRINFO */
    262 
    263 /*
    264  * gethostbyname_thread() resolves a name and then exits.
    265  */
    266 static
    267 #if defined(CURL_WINDOWS_UWP) || defined(UNDER_CE)
    268 DWORD
    269 #else
    270 unsigned int
    271 #endif
    272 CURL_STDCALL gethostbyname_thread(void *arg)
    273 {
    274   struct async_thrdd_addr_ctx *addr_ctx = arg;
    275   bool all_gone;
    276 
    277   addr_ctx->res = Curl_ipv4_resolve_r(addr_ctx->hostname, addr_ctx->port);
    278 
    279   if(!addr_ctx->res) {
    280     addr_ctx->sock_error = SOCKERRNO;
    281     if(addr_ctx->sock_error == 0)
    282       addr_ctx->sock_error = RESOLVER_ENOMEM;
    283   }
    284 
    285   Curl_mutex_acquire(&addr_ctx->mutx);
    286   /* thread gives up its reference to the shared data now. */
    287   --addr_ctx->ref_count;
    288   all_gone = !addr_ctx->ref_count;;
    289   Curl_mutex_release(&addr_ctx->mutx);
    290   if(all_gone)
    291     addr_ctx_destroy(addr_ctx);
    292 
    293   return 0;
    294 }
    295 
    296 #endif /* HAVE_GETADDRINFO */
    297 
    298 /*
    299  * async_thrdd_destroy() cleans up async resolver data and thread handle.
    300  */
    301 static void async_thrdd_destroy(struct Curl_easy *data)
    302 {
    303   struct async_thrdd_ctx *thrdd = &data->state.async.thrdd;
    304   struct async_thrdd_addr_ctx *addr = thrdd->addr;
    305 #ifdef USE_HTTPSRR_ARES
    306   if(thrdd->rr.channel) {
    307     ares_destroy(thrdd->rr.channel);
    308     thrdd->rr.channel = NULL;
    309   }
    310   Curl_httpsrr_cleanup(&thrdd->rr.hinfo);
    311 #endif
    312 
    313   if(addr) {
    314 #ifndef CURL_DISABLE_SOCKETPAIR
    315     curl_socket_t sock_rd = addr->sock_pair[0];
    316 #endif
    317     bool done;
    318 
    319     /* Release our reference to the data shared with the thread. */
    320     Curl_mutex_acquire(&addr->mutx);
    321     --addr->ref_count;
    322     CURL_TRC_DNS(data, "resolve, destroy async data, shared ref=%d",
    323                  addr->ref_count);
    324     done = !addr->ref_count;
    325     /* we give up our reference to `addr`, so NULL our pointer.
    326      * coverity analyses this as being a potential unsynched write,
    327      * assuming two calls to this function could be invoked concurrently.
    328      * Which they never are, as the transfer's side runs single-threaded. */
    329     thrdd->addr = NULL;
    330     if(!done) {
    331       /* thread is still running. Detach the thread while mutexed, it will
    332        * trigger the cleanup when it releases its reference. */
    333       Curl_thread_destroy(&addr->thread_hnd);
    334     }
    335     Curl_mutex_release(&addr->mutx);
    336 
    337     if(done) {
    338       /* thread has released its reference, join it and
    339        * release the memory we shared with it. */
    340       if(addr->thread_hnd != curl_thread_t_null)
    341         Curl_thread_join(&addr->thread_hnd);
    342       addr_ctx_destroy(addr);
    343     }
    344 #ifndef CURL_DISABLE_SOCKETPAIR
    345     /*
    346      * ensure CURLMOPT_SOCKETFUNCTION fires CURL_POLL_REMOVE
    347      * before the FD is invalidated to avoid EBADF on EPOLL_CTL_DEL
    348      */
    349     Curl_multi_will_close(data, sock_rd);
    350     wakeup_close(sock_rd);
    351 #endif
    352   }
    353 }
    354 
    355 #ifdef USE_HTTPSRR_ARES
    356 
    357 static void async_thrdd_rr_done(void *user_data, ares_status_t status,
    358                                 size_t timeouts,
    359                                 const ares_dns_record_t *dnsrec)
    360 {
    361   struct Curl_easy *data = user_data;
    362   struct async_thrdd_ctx *thrdd = &data->state.async.thrdd;
    363 
    364   (void)timeouts;
    365   thrdd->rr.done = TRUE;
    366   if((ARES_SUCCESS != status) || !dnsrec)
    367     return;
    368   thrdd->rr.result = Curl_httpsrr_from_ares(data, dnsrec, &thrdd->rr.hinfo);
    369 }
    370 
    371 static CURLcode async_rr_start(struct Curl_easy *data)
    372 {
    373   struct async_thrdd_ctx *thrdd = &data->state.async.thrdd;
    374   int status;
    375 
    376   DEBUGASSERT(!thrdd->rr.channel);
    377   status = ares_init_options(&thrdd->rr.channel, NULL, 0);
    378   if(status != ARES_SUCCESS) {
    379     thrdd->rr.channel = NULL;
    380     return CURLE_FAILED_INIT;
    381   }
    382 
    383   memset(&thrdd->rr.hinfo, 0, sizeof(thrdd->rr.hinfo));
    384   thrdd->rr.hinfo.port = -1;
    385   ares_query_dnsrec(thrdd->rr.channel,
    386                     data->conn->host.name, ARES_CLASS_IN,
    387                     ARES_REC_TYPE_HTTPS,
    388                     async_thrdd_rr_done, data, NULL);
    389   return CURLE_OK;
    390 }
    391 #endif
    392 
    393 /*
    394  * async_thrdd_init() starts a new thread that performs the actual
    395  * resolve. This function returns before the resolve is done.
    396  *
    397  * Returns FALSE in case of failure, otherwise TRUE.
    398  */
    399 static bool async_thrdd_init(struct Curl_easy *data,
    400                              const char *hostname, int port, int ip_version,
    401                              const struct addrinfo *hints)
    402 {
    403   struct async_thrdd_ctx *thrdd = &data->state.async.thrdd;
    404   struct async_thrdd_addr_ctx *addr_ctx;
    405 
    406   /* !checksrc! disable ERRNOVAR 1 */
    407   int err = ENOMEM;
    408 
    409   if(thrdd->addr
    410 #ifdef USE_HTTPSRR_ARES
    411      || thrdd->rr.channel
    412 #endif
    413      ) {
    414     CURL_TRC_DNS(data, "starting new resolve, with previous not cleaned up");
    415     async_thrdd_destroy(data);
    416     DEBUGASSERT(!thrdd->addr);
    417 #ifdef USE_HTTPSRR_ARES
    418     DEBUGASSERT(!thrdd->rr.channel);
    419 #endif
    420   }
    421 
    422   data->state.async.dns = NULL;
    423   data->state.async.done = FALSE;
    424   data->state.async.port = port;
    425   data->state.async.ip_version = ip_version;
    426   free(data->state.async.hostname);
    427   data->state.async.hostname = strdup(hostname);
    428   if(!data->state.async.hostname)
    429     goto err_exit;
    430 
    431   addr_ctx = addr_ctx_create(hostname, port, hints);
    432   if(!addr_ctx)
    433     goto err_exit;
    434   thrdd->addr = addr_ctx;
    435 
    436   Curl_mutex_acquire(&addr_ctx->mutx);
    437   DEBUGASSERT(addr_ctx->ref_count == 1);
    438   /* passing addr_ctx to the thread adds a reference */
    439   addr_ctx->start = curlx_now();
    440   ++addr_ctx->ref_count;
    441 #ifdef HAVE_GETADDRINFO
    442   addr_ctx->thread_hnd = Curl_thread_create(getaddrinfo_thread, addr_ctx);
    443 #else
    444   addr_ctx->thread_hnd = Curl_thread_create(gethostbyname_thread, addr_ctx);
    445 #endif
    446   if(addr_ctx->thread_hnd == curl_thread_t_null) {
    447     /* The thread never started, remove its reference that never happened. */
    448     --addr_ctx->ref_count;
    449     err = errno;
    450     Curl_mutex_release(&addr_ctx->mutx);
    451     goto err_exit;
    452   }
    453   Curl_mutex_release(&addr_ctx->mutx);
    454 
    455 #ifdef USE_HTTPSRR_ARES
    456   if(async_rr_start(data))
    457     infof(data, "Failed HTTPS RR operation");
    458 #endif
    459   CURL_TRC_DNS(data, "resolve thread started for of %s:%d", hostname, port);
    460   return TRUE;
    461 
    462 err_exit:
    463   CURL_TRC_DNS(data, "resolve thread failed init: %d", err);
    464   async_thrdd_destroy(data);
    465   CURL_SETERRNO(err);
    466   return FALSE;
    467 }
    468 
    469 /*
    470  * 'entry' may be NULL and then no data is returned
    471  */
    472 static CURLcode asyn_thrdd_await(struct Curl_easy *data,
    473                                  struct async_thrdd_addr_ctx *addr_ctx,
    474                                  struct Curl_dns_entry **entry)
    475 {
    476   CURLcode result = CURLE_OK;
    477 
    478   DEBUGASSERT(addr_ctx->thread_hnd != curl_thread_t_null);
    479 
    480   CURL_TRC_DNS(data, "resolve, wait for thread to finish");
    481   /* wait for the thread to resolve the name */
    482   if(Curl_thread_join(&addr_ctx->thread_hnd)) {
    483     if(entry)
    484       result = Curl_async_is_resolved(data, entry);
    485   }
    486   else
    487     DEBUGASSERT(0);
    488 
    489   data->state.async.done = TRUE;
    490   if(entry)
    491     *entry = data->state.async.dns;
    492 
    493   async_thrdd_destroy(data);
    494   return result;
    495 }
    496 
    497 
    498 /*
    499  * Until we gain a way to signal the resolver threads to stop early, we must
    500  * simply wait for them and ignore their results.
    501  */
    502 void Curl_async_thrdd_shutdown(struct Curl_easy *data)
    503 {
    504   struct async_thrdd_ctx *thrdd = &data->state.async.thrdd;
    505 
    506   /* If we are still resolving, we must wait for the threads to fully clean up,
    507      unfortunately. Otherwise, we can simply cancel to clean up any resolver
    508      data. */
    509   if(thrdd->addr && (thrdd->addr->thread_hnd != curl_thread_t_null) &&
    510      !data->set.quick_exit)
    511     (void)asyn_thrdd_await(data, thrdd->addr, NULL);
    512   else
    513     async_thrdd_destroy(data);
    514 }
    515 
    516 void Curl_async_thrdd_destroy(struct Curl_easy *data)
    517 {
    518   Curl_async_thrdd_shutdown(data);
    519 }
    520 
    521 /*
    522  * Curl_async_await()
    523  *
    524  * Waits for a resolve to finish. This function should be avoided since using
    525  * this risk getting the multi interface to "hang".
    526  *
    527  * If 'entry' is non-NULL, make it point to the resolved dns entry
    528  *
    529  * Returns CURLE_COULDNT_RESOLVE_HOST if the host was not resolved,
    530  * CURLE_OPERATION_TIMEDOUT if a time-out occurred, or other errors.
    531  *
    532  * This is the version for resolves-in-a-thread.
    533  */
    534 CURLcode Curl_async_await(struct Curl_easy *data,
    535                           struct Curl_dns_entry **entry)
    536 {
    537   struct async_thrdd_ctx *thrdd = &data->state.async.thrdd;
    538   if(thrdd->addr)
    539     return asyn_thrdd_await(data, thrdd->addr, entry);
    540   return CURLE_FAILED_INIT;
    541 }
    542 
    543 /*
    544  * Curl_async_is_resolved() is called repeatedly to check if a previous
    545  * name resolve request has completed. It should also make sure to time-out if
    546  * the operation seems to take too long.
    547  */
    548 CURLcode Curl_async_is_resolved(struct Curl_easy *data,
    549                                 struct Curl_dns_entry **dns)
    550 {
    551   struct async_thrdd_ctx *thrdd = &data->state.async.thrdd;
    552   bool done = FALSE;
    553 
    554   DEBUGASSERT(dns);
    555   *dns = NULL;
    556 
    557   if(data->state.async.done) {
    558     *dns = data->state.async.dns;
    559     CURL_TRC_DNS(data, "threaded: is_resolved(), already done, dns=%sfound",
    560                  *dns ? "" : "not ");
    561     return CURLE_OK;
    562   }
    563 
    564 #ifdef USE_HTTPSRR_ARES
    565   /* best effort, ignore errors */
    566   if(thrdd->rr.channel)
    567     (void)Curl_ares_perform(thrdd->rr.channel, 0);
    568 #endif
    569 
    570   DEBUGASSERT(thrdd->addr);
    571   if(!thrdd->addr)
    572     return CURLE_FAILED_INIT;
    573 
    574   Curl_mutex_acquire(&thrdd->addr->mutx);
    575   done = (thrdd->addr->ref_count == 1);
    576   Curl_mutex_release(&thrdd->addr->mutx);
    577 
    578   if(done) {
    579     CURLcode result = CURLE_OK;
    580 
    581     data->state.async.done = TRUE;
    582     Curl_resolv_unlink(data, &data->state.async.dns);
    583 
    584     if(thrdd->addr->res) {
    585       data->state.async.dns =
    586         Curl_dnscache_mk_entry(data, thrdd->addr->res,
    587                                data->state.async.hostname, 0,
    588                                data->state.async.port, FALSE);
    589       thrdd->addr->res = NULL;
    590       if(!data->state.async.dns)
    591         result = CURLE_OUT_OF_MEMORY;
    592 
    593 #ifdef USE_HTTPSRR_ARES
    594       if(thrdd->rr.channel) {
    595         result = thrdd->rr.result;
    596         if(!result) {
    597           struct Curl_https_rrinfo *lhrr;
    598           lhrr = Curl_httpsrr_dup_move(&thrdd->rr.hinfo);
    599           if(!lhrr)
    600             result = CURLE_OUT_OF_MEMORY;
    601           else
    602             data->state.async.dns->hinfo = lhrr;
    603         }
    604       }
    605 #endif
    606       if(!result && data->state.async.dns)
    607         result = Curl_dnscache_add(data, data->state.async.dns);
    608     }
    609 
    610     if(!result && !data->state.async.dns)
    611       result = Curl_resolver_error(data);
    612     if(result)
    613       Curl_resolv_unlink(data, &data->state.async.dns);
    614     *dns = data->state.async.dns;
    615     CURL_TRC_DNS(data, "is_resolved() result=%d, dns=%sfound",
    616                  result, *dns ? "" : "not ");
    617     async_thrdd_destroy(data);
    618     return result;
    619   }
    620   else {
    621     /* poll for name lookup done with exponential backoff up to 250ms */
    622     /* should be fine even if this converts to 32-bit */
    623     timediff_t elapsed = curlx_timediff(curlx_now(),
    624                                        data->progress.t_startsingle);
    625     if(elapsed < 0)
    626       elapsed = 0;
    627 
    628     if(thrdd->addr->poll_interval == 0)
    629       /* Start at 1ms poll interval */
    630       thrdd->addr->poll_interval = 1;
    631     else if(elapsed >= thrdd->addr->interval_end)
    632       /* Back-off exponentially if last interval expired  */
    633       thrdd->addr->poll_interval *= 2;
    634 
    635     if(thrdd->addr->poll_interval > 250)
    636       thrdd->addr->poll_interval = 250;
    637 
    638     thrdd->addr->interval_end = elapsed + thrdd->addr->poll_interval;
    639     Curl_expire(data, thrdd->addr->poll_interval, EXPIRE_ASYNC_NAME);
    640     return CURLE_OK;
    641   }
    642 }
    643 
    644 int Curl_async_getsock(struct Curl_easy *data, curl_socket_t *socks)
    645 {
    646   struct async_thrdd_ctx *thrdd = &data->state.async.thrdd;
    647   int ret_val = 0;
    648 #if !defined(CURL_DISABLE_SOCKETPAIR) || defined(USE_HTTPSRR_ARES)
    649   int socketi = 0;
    650 #else
    651   (void)socks;
    652 #endif
    653 
    654 #ifdef USE_HTTPSRR_ARES
    655   if(thrdd->rr.channel) {
    656     ret_val = Curl_ares_getsock(data, thrdd->rr.channel, socks);
    657     for(socketi = 0; socketi < (MAX_SOCKSPEREASYHANDLE - 1); socketi++)
    658       if(!ARES_GETSOCK_READABLE(ret_val, socketi) &&
    659          !ARES_GETSOCK_WRITABLE(ret_val, socketi))
    660         break;
    661   }
    662 #endif
    663   if(!thrdd->addr)
    664     return ret_val;
    665 
    666 #ifndef CURL_DISABLE_SOCKETPAIR
    667   if(thrdd->addr) {
    668     /* return read fd to client for polling the DNS resolution status */
    669     socks[socketi] = thrdd->addr->sock_pair[0];
    670     ret_val |= GETSOCK_READSOCK(socketi);
    671   }
    672   else
    673 #endif
    674   {
    675     timediff_t milli;
    676     timediff_t ms = curlx_timediff(curlx_now(), thrdd->addr->start);
    677     if(ms < 3)
    678       milli = 0;
    679     else if(ms <= 50)
    680       milli = ms/3;
    681     else if(ms <= 250)
    682       milli = 50;
    683     else
    684       milli = 200;
    685     Curl_expire(data, milli, EXPIRE_ASYNC_NAME);
    686   }
    687 
    688   return ret_val;
    689 }
    690 
    691 #ifndef HAVE_GETADDRINFO
    692 /*
    693  * Curl_async_getaddrinfo() - for platforms without getaddrinfo
    694  */
    695 struct Curl_addrinfo *Curl_async_getaddrinfo(struct Curl_easy *data,
    696                                              const char *hostname,
    697                                              int port,
    698                                              int ip_version,
    699                                              int *waitp)
    700 {
    701   (void)ip_version;
    702   *waitp = 0; /* default to synchronous response */
    703 
    704   /* fire up a new resolver thread! */
    705   if(async_thrdd_init(data, hostname, port, ip_version, NULL)) {
    706     *waitp = 1; /* expect asynchronous response */
    707     return NULL;
    708   }
    709 
    710   failf(data, "getaddrinfo() thread failed");
    711 
    712   return NULL;
    713 }
    714 
    715 #else /* !HAVE_GETADDRINFO */
    716 
    717 /*
    718  * Curl_async_getaddrinfo() - for getaddrinfo
    719  */
    720 struct Curl_addrinfo *Curl_async_getaddrinfo(struct Curl_easy *data,
    721                                              const char *hostname,
    722                                              int port,
    723                                              int ip_version,
    724                                              int *waitp)
    725 {
    726   struct addrinfo hints;
    727   int pf = PF_INET;
    728   *waitp = 0; /* default to synchronous response */
    729 
    730   CURL_TRC_DNS(data, "init threaded resolve of %s:%d", hostname, port);
    731 #ifdef CURLRES_IPV6
    732   if((ip_version != CURL_IPRESOLVE_V4) && Curl_ipv6works(data)) {
    733     /* The stack seems to be IPv6-enabled */
    734     if(ip_version == CURL_IPRESOLVE_V6)
    735       pf = PF_INET6;
    736     else
    737       pf = PF_UNSPEC;
    738   }
    739 #else
    740   (void)ip_version;
    741 #endif /* CURLRES_IPV6 */
    742 
    743   memset(&hints, 0, sizeof(hints));
    744   hints.ai_family = pf;
    745   hints.ai_socktype =
    746     (Curl_conn_get_transport(data, data->conn) == TRNSPRT_TCP) ?
    747     SOCK_STREAM : SOCK_DGRAM;
    748 
    749   /* fire up a new resolver thread! */
    750   if(async_thrdd_init(data, hostname, port, ip_version, &hints)) {
    751     *waitp = 1; /* expect asynchronous response */
    752     return NULL;
    753   }
    754 
    755   failf(data, "getaddrinfo() thread failed to start");
    756   return NULL;
    757 
    758 }
    759 
    760 #endif /* !HAVE_GETADDRINFO */
    761 
    762 #endif /* CURLRES_THREADED */