mirror of
https://github.com/moparisthebest/curl
synced 2024-12-21 23:58:49 -05:00
resolve: cache lookup for async resolvers
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.
This commit is contained in:
parent
2434a4e88d
commit
09b5a99816
@ -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 {
|
||||
@ -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);
|
||||
@ -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;
|
||||
|
||||
@ -280,8 +285,16 @@ static unsigned int CURL_STDCALL getaddrinfo_thread (void *arg)
|
||||
}
|
||||
|
||||
Curl_mutex_acquire(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;
|
||||
}
|
||||
@ -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);
|
||||
|
||||
@ -304,8 +318,16 @@ static unsigned int CURL_STDCALL gethostbyname_thread (void *arg)
|
||||
}
|
||||
|
||||
Curl_mutex_acquire(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;
|
||||
}
|
||||
@ -317,12 +339,23 @@ 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);
|
||||
|
||||
@ -330,8 +363,13 @@ static void destroy_async_data (struct Curl_async *async)
|
||||
|
||||
free(async->os_specific);
|
||||
}
|
||||
async->hostname = NULL;
|
||||
}
|
||||
async->os_specific = NULL;
|
||||
|
||||
if(async->hostname)
|
||||
free(async->hostname);
|
||||
|
||||
async->hostname = NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
@ -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);
|
||||
|
71
lib/hostip.c
71
lib/hostip.c
@ -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
|
||||
@ -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.
|
||||
@ -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);
|
||||
dns = Curl_fetch_addr(conn, hostname, port, &stale);
|
||||
|
||||
/* free the allocated entry_id again */
|
||||
free(entry_id);
|
||||
|
||||
infof(data, "Hostname was %sfound in DNS cache\n", dns?"":"NOT ");
|
||||
|
||||
/* 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! */
|
||||
|
11
lib/hostip.h
11
lib/hostip.h
@ -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.
|
||||
*
|
||||
|
24
lib/multi.c
24
lib/multi.c
@ -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"
|
||||
@ -1084,8 +1085,31 @@ 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 */
|
||||
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
|
||||
|
Loading…
Reference in New Issue
Block a user