diff --git a/lib/asyn-thread.c b/lib/asyn-thread.c index 31a313281..6cdc9adff 100644 --- a/lib/asyn-thread.c +++ b/lib/asyn-thread.c @@ -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); - 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; } @@ -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); - 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; } @@ -317,21 +339,37 @@ static unsigned int CURL_STDCALL gethostbyname_thread (void *arg) */ static void destroy_async_data (struct Curl_async *async) { + 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); + + destroy_thread_sync_data(&td->tsd); + + free(async->os_specific); + } + } + async->os_specific = NULL; + if(async->hostname) free(async->hostname); - if(async->os_specific) { - struct thread_data *td = (struct thread_data*) async->os_specific; - - if(td->thread_hnd != curl_thread_t_null) - Curl_thread_join(&td->thread_hnd); - - destroy_thread_sync_data(&td->tsd); - - free(async->os_specific); - } async->hostname = NULL; - async->os_specific = 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); diff --git a/lib/hostip.c b/lib/hostip.c index 61d238acd..73b3f8201 100644 --- a/lib/hostip.c +++ b/lib/hostip.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2013, Daniel Stenberg, , et al. + * Copyright (C) 1998 - 2014, Daniel Stenberg, , 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! */ diff --git a/lib/hostip.h b/lib/hostip.h index 42ed7d320..440465194 100644 --- a/lib/hostip.h +++ b/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. * diff --git a/lib/multi.c b/lib/multi.c index 1e5b3c8df..20fc372fa 100644 --- a/lib/multi.c +++ b/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,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