Skip to content

Commit

Permalink
resolve: cache lookup for async resolvers
Browse files Browse the repository at this point in the history
While waiting for a host resolve, check if the host cache may have
gotten the name already (by someone else), for when the same name is
resolved by several simultanoues requests.

The resolver thread occasionally gets stuck in getaddrinfo() when the
DNS or anything else is crappy or slow, so when a host is found in the
DNS cache, leave the thread alone and let itself cleanup the mess.
  • Loading branch information
m6w6 authored and bagder committed Aug 31, 2014
1 parent 2434a4e commit 09b5a99
Show file tree
Hide file tree
Showing 4 changed files with 137 additions and 39 deletions.
68 changes: 53 additions & 15 deletions lib/asyn-thread.c
Expand Up @@ -166,6 +166,7 @@ struct thread_sync_data {
#ifdef HAVE_GETADDRINFO
struct addrinfo hints;
#endif
struct thread_data *td; /* for thread-self cleanup */
};

struct thread_data {
Expand Down Expand Up @@ -202,13 +203,16 @@ void destroy_thread_sync_data(struct thread_sync_data * tsd)

/* Initialize resolver thread synchronization data */
static
int init_thread_sync_data(struct thread_sync_data * tsd,
int init_thread_sync_data(struct thread_data * td,
const char * hostname,
int port,
const struct addrinfo *hints)
{
struct thread_sync_data *tsd = &td->tsd;

memset(tsd, 0, sizeof(*tsd));

tsd->td = td;
tsd->port = port;
#ifdef HAVE_GETADDRINFO
DEBUGASSERT(hints);
Expand Down Expand Up @@ -266,6 +270,7 @@ static int getaddrinfo_complete(struct connectdata *conn)
static unsigned int CURL_STDCALL getaddrinfo_thread (void *arg)
{
struct thread_sync_data *tsd = (struct thread_sync_data*)arg;
struct thread_data *td = tsd->td;
char service[12];
int rc;

Expand All @@ -280,8 +285,16 @@ static unsigned int CURL_STDCALL getaddrinfo_thread (void *arg)
}

Curl_mutex_acquire(tsd->mtx);
tsd->done = 1;
Curl_mutex_release(tsd->mtx);
if(tsd->done) {
/* too late, gotta clean up the mess */
Curl_mutex_release(tsd->mtx);
destroy_thread_sync_data(tsd);
free(td);
}
else {
tsd->done = 1;
Curl_mutex_release(tsd->mtx);
}

return 0;
}
Expand All @@ -294,6 +307,7 @@ static unsigned int CURL_STDCALL getaddrinfo_thread (void *arg)
static unsigned int CURL_STDCALL gethostbyname_thread (void *arg)
{
struct thread_sync_data *tsd = (struct thread_sync_data *)arg;
struct thread_data *td = tsd->td;

tsd->res = Curl_ipv4_resolve_r(tsd->hostname, tsd->port);

Expand All @@ -304,8 +318,16 @@ static unsigned int CURL_STDCALL gethostbyname_thread (void *arg)
}

Curl_mutex_acquire(tsd->mtx);
tsd->done = 1;
Curl_mutex_release(tsd->mtx);
if(tsd->done) {
/* too late, gotta clean up the mess */
Curl_mutex_release(tsd->mtx);
destroy_thread_sync_data(tsd);
free(td);
}
else {
tsd->done = 1;
Curl_mutex_release(tsd->mtx);
}

return 0;
}
Expand All @@ -317,21 +339,37 @@ static unsigned int CURL_STDCALL gethostbyname_thread (void *arg)
*/
static void destroy_async_data (struct Curl_async *async)
{
if(async->hostname)
free(async->hostname);

if(async->os_specific) {
struct thread_data *td = (struct thread_data*) async->os_specific;
int done;

/*
* if the thread is still blocking in the resolve syscall, detach it and
* let the thread do the cleanup...
*/
Curl_mutex_acquire(td->tsd.mtx);
done = td->tsd.done;
td->tsd.done = 1;
Curl_mutex_release(td->tsd.mtx);

if(!done) {
Curl_thread_destroy(td->thread_hnd);
}
else {
if(td->thread_hnd != curl_thread_t_null)
Curl_thread_join(&td->thread_hnd);

if(td->thread_hnd != curl_thread_t_null)
Curl_thread_join(&td->thread_hnd);

destroy_thread_sync_data(&td->tsd);
destroy_thread_sync_data(&td->tsd);

free(async->os_specific);
free(async->os_specific);
}
}
async->hostname = NULL;
async->os_specific = NULL;

if(async->hostname)
free(async->hostname);

async->hostname = NULL;
}

/*
Expand All @@ -357,7 +395,7 @@ static bool init_resolve_thread (struct connectdata *conn,
conn->async.dns = NULL;
td->thread_hnd = curl_thread_t_null;

if(!init_thread_sync_data(&td->tsd, hostname, port, hints))
if(!init_thread_sync_data(td, hostname, port, hints))
goto err_exit;

Curl_safefree(conn->async.hostname);
Expand Down
71 changes: 48 additions & 23 deletions lib/hostip.c
Expand Up @@ -5,7 +5,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
* Copyright (C) 1998 - 2013, Daniel Stenberg, <daniel@haxx.se>, et al.
* Copyright (C) 1998 - 2014, Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
Expand Down Expand Up @@ -318,6 +318,48 @@ remove_entry_if_stale(struct SessionHandle *data, struct Curl_dns_entry *dns)
sigjmp_buf curl_jmpenv;
#endif

/*
* Curl_fetch_addr() fetches a 'Curl_dns_entry' already in the DNS cache.
*
* Curl_resolv() checks initially and multi_runsingle() checks each time
* it discovers the handle in the state WAITRESOLVE whether the hostname
* has already been resolved and the address has already been stored in
* the DNS cache. This short circuits waiting for a lot of pending
* lookups for the same hostname requested by different handles.
*
* Returns the Curl_dns_entry entry pointer or NULL if not in the cache.
*/
struct Curl_dns_entry *
Curl_fetch_addr(struct connectdata *conn,
const char *hostname,
int port, int *stale)
{
char *entry_id = NULL;
struct Curl_dns_entry *dns = NULL;
size_t entry_len;
struct SessionHandle *data = conn->data;

/* Create an entry id, based upon the hostname and port */
entry_id = create_hostcache_id(hostname, port);
/* If we can't create the entry id, fail */
if(!entry_id)
return dns;

entry_len = strlen(entry_id);

/* See if its already in our dns cache */
dns = Curl_hash_pick(data->dns.hostcache, entry_id, entry_len+1);

/* free the allocated entry_id again */
free(entry_id);

/* See whether the returned entry is stale. Done before we release lock */
*stale = remove_entry_if_stale(data, dns);
if(*stale)
dns = NULL; /* the memory deallocation is being handled by the hash */

return dns;
}

/*
* Curl_cache_addr() stores a 'Curl_addrinfo' struct in the DNS cache.
Expand Down Expand Up @@ -403,39 +445,22 @@ int Curl_resolv(struct connectdata *conn,
int port,
struct Curl_dns_entry **entry)
{
char *entry_id = NULL;
struct Curl_dns_entry *dns = NULL;
size_t entry_len;
struct SessionHandle *data = conn->data;
CURLcode result;
int rc = CURLRESOLV_ERROR; /* default to failure */
int stale, rc = CURLRESOLV_ERROR; /* default to failure */

*entry = NULL;

/* Create an entry id, based upon the hostname and port */
entry_id = create_hostcache_id(hostname, port);
/* If we can't create the entry id, fail */
if(!entry_id)
return rc;

entry_len = strlen(entry_id);

if(data->share)
Curl_share_lock(data, CURL_LOCK_DATA_DNS, CURL_LOCK_ACCESS_SINGLE);

/* See if its already in our dns cache */
dns = Curl_hash_pick(data->dns.hostcache, entry_id, entry_len+1);

/* free the allocated entry_id again */
free(entry_id);

infof(data, "Hostname was %sfound in DNS cache\n", dns?"":"NOT ");
dns = Curl_fetch_addr(conn, hostname, port, &stale);

/* See whether the returned entry is stale. Done before we release lock */
if(remove_entry_if_stale(data, dns)) {
infof(data, "Hostname was %sfound in DNS cache\n", dns||stale?"":"NOT ");
if(stale)
infof(data, "Hostname in DNS cache was stale, zapped\n");
dns = NULL; /* the memory deallocation is being handled by the hash */
}


if(dns) {
dns->inuse++; /* we use it! */
Expand Down
11 changes: 11 additions & 0 deletions lib/hostip.h
Expand Up @@ -171,6 +171,17 @@ CURLcode Curl_addrinfo_callback(struct connectdata *conn,
const char *Curl_printable_address(const Curl_addrinfo *ip,
char *buf, size_t bufsize);

/*
* Curl_fetch_addr() fetches a 'Curl_dns_entry' already in the DNS cache.
*
* Returns the Curl_dns_entry entry pointer or NULL if not in the cache.
*/
struct Curl_dns_entry *
Curl_fetch_addr(struct connectdata *conn,
const char *hostname,
int port,
int *stale);

/*
* Curl_cache_addr() stores a 'Curl_addrinfo' struct in the DNS cache.
*
Expand Down
26 changes: 25 additions & 1 deletion lib/multi.c
Expand Up @@ -30,6 +30,7 @@
#include "connect.h"
#include "progress.h"
#include "easyif.h"
#include "share.h"
#include "multiif.h"
#include "sendf.h"
#include "timeval.h"
Expand Down Expand Up @@ -1084,9 +1085,32 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi,
/* awaiting an asynch name resolve to complete */
{
struct Curl_dns_entry *dns = NULL;
struct connectdata *conn = data->easy_conn;
int stale;

/* check if we have the name resolved by now */
data->result = Curl_resolver_is_resolved(data->easy_conn, &dns);
if(data->share)
Curl_share_lock(data, CURL_LOCK_DATA_DNS, CURL_LOCK_ACCESS_SINGLE);

dns = Curl_fetch_addr(conn, conn->host.name, (int)conn->port, &stale);

if(dns) {
dns->inuse++; /* we use it! */
#ifdef CURLRES_ASYNCH
conn->async.dns = dns;
conn->async.done = TRUE;
#endif
data->result = CURLRESOLV_RESOLVED;
infof(data, "Hostname was found in DNS cache\n");
}
if(stale)
infof(data, "Hostname in DNS cache was stale, zapped\n");

if(data->share)
Curl_share_unlock(data, CURL_LOCK_DATA_DNS);

if(!dns)
data->result = Curl_resolver_is_resolved(data->easy_conn, &dns);

/* Update sockets here, because the socket(s) may have been
closed and the application thus needs to be told, even if it
Expand Down

0 comments on commit 09b5a99

Please sign in to comment.