diff options
-rw-r--r-- | lib/asyn-ares.c | 2 | ||||
-rw-r--r-- | lib/asyn-thread.c | 70 | ||||
-rw-r--r-- | lib/connect.c | 2 | ||||
-rw-r--r-- | lib/hostip.c | 71 | ||||
-rw-r--r-- | lib/hostip.h | 11 | ||||
-rw-r--r-- | lib/multi.c | 72 | ||||
-rw-r--r-- | lib/multiif.h | 1 | ||||
-rw-r--r-- | lib/speedcheck.c | 6 |
8 files changed, 187 insertions, 48 deletions
diff --git a/lib/asyn-ares.c b/lib/asyn-ares.c index cb99a1f46..01a9c9b50 100644 --- a/lib/asyn-ares.c +++ b/lib/asyn-ares.c @@ -235,7 +235,7 @@ int Curl_resolver_getsock(struct connectdata *conn, milli = (timeout->tv_sec * 1000) + (timeout->tv_usec/1000); if(milli == 0) milli += 10; - Curl_expire(conn->data, milli); + Curl_expire_latest(conn->data, milli); return max; } diff --git a/lib/asyn-thread.c b/lib/asyn-thread.c index 31a313281..e4ad32bb7 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->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; } /* @@ -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); @@ -503,7 +541,7 @@ CURLcode Curl_resolver_is_resolved(struct connectdata *conn, td->poll_interval = 250; td->interval_end = elapsed + td->poll_interval; - Curl_expire(conn->data, td->poll_interval); + Curl_expire_latest(conn->data, td->poll_interval); } return CURLE_OK; diff --git a/lib/connect.c b/lib/connect.c index 6a79e64de..fb315fc8d 100644 --- a/lib/connect.c +++ b/lib/connect.c @@ -1054,7 +1054,7 @@ singleipconnect(struct connectdata *conn, conn->connecttime = Curl_tvnow(); if(conn->num_addr > 1) - Curl_expire(data, conn->timeoutms_per_addr); + Curl_expire_latest(data, conn->timeoutms_per_addr); /* Connect TCP sockets, bind UDP */ if(!isconnected && (conn->socktype == SOCK_STREAM)) { 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, <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); - - /* 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! */ diff --git a/lib/hostip.h b/lib/hostip.h index 42ed7d320..440465194 100644 --- a/lib/hostip.h +++ b/lib/hostip.h @@ -172,6 +172,17 @@ 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. * * Returns the Curl_dns_entry entry pointer or NULL if the storage failed. diff --git a/lib/multi.c b/lib/multi.c index 1e5b3c8df..557be06df 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 @@ -1467,7 +1491,7 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, data->set.buffer_size : BUFSIZE); timeout_ms = Curl_sleep_time(data->set.max_send_speed, data->progress.ulspeed, buffersize); - Curl_expire(data, timeout_ms); + Curl_expire_latest(data, timeout_ms); break; } @@ -1483,7 +1507,7 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, data->set.buffer_size : BUFSIZE); timeout_ms = Curl_sleep_time(data->set.max_recv_speed, data->progress.dlspeed, buffersize); - Curl_expire(data, timeout_ms); + Curl_expire_latest(data, timeout_ms); break; } @@ -1545,7 +1569,7 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, /* expire the new receiving pipeline head */ if(data->easy_conn->recv_pipe->head) - Curl_expire(data->easy_conn->recv_pipe->head->ptr, 1); + Curl_expire_latest(data->easy_conn->recv_pipe->head->ptr, 1); /* Check if we can move pending requests to send pipe */ Curl_multi_process_pending_handles(multi); @@ -2658,6 +2682,46 @@ void Curl_expire(struct SessionHandle *data, long milli) #endif } +/* + * Curl_expire_latest() + * + * This is like Curl_expire() but will only add a timeout node to the list of + * timers if there is no timeout that will expire before the given time. + * + * Use this function if the code logic risks calling this function many times + * or if there's no particular conditional wait in the code for this specific + * time-out period to expire. + * + */ +void Curl_expire_latest(struct SessionHandle *data, long milli) +{ + struct timeval *exp = &data->state.expiretime; + + struct timeval set; + + set = Curl_tvnow(); + set.tv_sec += milli/1000; + set.tv_usec += (milli%1000)*1000; + + if(set.tv_usec >= 1000000) { + set.tv_sec++; + set.tv_usec -= 1000000; + } + + if(exp->tv_sec || exp->tv_usec) { + /* This means that the struct is added as a node in the splay tree. + Compare if the new time is earlier, and only remove-old/add-new if it + is. */ + long diff = curlx_tvdiff(set, *exp); + if(diff > 0) + /* the new expire time was later than the top time, so just skip this */ + return; + } + + /* Just add the timeout like normal */ + Curl_expire(data, milli); +} + CURLMcode curl_multi_assign(CURLM *multi_handle, curl_socket_t s, void *hashp) { diff --git a/lib/multiif.h b/lib/multiif.h index 1cbd31009..c77b3ca3b 100644 --- a/lib/multiif.h +++ b/lib/multiif.h @@ -26,6 +26,7 @@ * Prototypes for library-wide functions provided by multi.c */ void Curl_expire(struct SessionHandle *data, long milli); +void Curl_expire_latest(struct SessionHandle *data, long milli); bool Curl_multi_pipeline_enabled(const struct Curl_multi* multi); void Curl_multi_handlePipeBreak(struct SessionHandle *data); diff --git a/lib/speedcheck.c b/lib/speedcheck.c index ea17a5975..ac7447c41 100644 --- a/lib/speedcheck.c +++ b/lib/speedcheck.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2011, 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 @@ -57,7 +57,7 @@ CURLcode Curl_speedcheck(struct SessionHandle *data, } else { /* wait complete low_speed_time */ - Curl_expire(data, nextcheck); + Curl_expire_latest(data, nextcheck); } } else { @@ -68,7 +68,7 @@ CURLcode Curl_speedcheck(struct SessionHandle *data, /* if there is a low speed limit enabled, we set the expire timer to make this connection's speed get checked again no later than when this time is up */ - Curl_expire(data, data->set.low_speed_time*1000); + Curl_expire_latest(data, data->set.low_speed_time*1000); } return CURLE_OK; } |