aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--lib/asyn-ares.c2
-rw-r--r--lib/asyn-thread.c70
-rw-r--r--lib/connect.c2
-rw-r--r--lib/hostip.c71
-rw-r--r--lib/hostip.h11
-rw-r--r--lib/multi.c72
-rw-r--r--lib/multiif.h1
-rw-r--r--lib/speedcheck.c6
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;
}