From 483ff1ca75cbeabe9d0ec4548d8a4d68f8104d83 Mon Sep 17 00:00:00 2001 From: Yang Tse Date: Mon, 25 Jan 2010 23:50:13 +0000 Subject: [PATCH] Constantine Sapuntzakis threaded resolver enhancements --- lib/hostares.c | 5 +- lib/hostip.h | 9 +- lib/hostip4.c | 66 +++-- lib/hostip6.c | 6 +- lib/hostthre.c | 641 ++++++++++++++++++------------------------------- 5 files changed, 290 insertions(+), 437 deletions(-) diff --git a/lib/hostares.c b/lib/hostares.c index 94240a6ab..a7f1d1bd2 100644 --- a/lib/hostares.c +++ b/lib/hostares.c @@ -350,7 +350,8 @@ Curl_addrinfo *Curl_getaddrinfo(struct connectdata *conn, #ifdef ENABLE_IPV6 /* CURLRES_IPV6 */ struct in6_addr in6; #endif /* CURLRES_IPV6 */ - *waitp = FALSE; + + *waitp = 0; /* default to synchronous response */ /* First check if this is an IPv4 address string */ if(Curl_inet_pton(AF_INET, hostname, &in) > 0) { @@ -396,7 +397,7 @@ Curl_addrinfo *Curl_getaddrinfo(struct connectdata *conn, ares_gethostbyname(data->state.areschannel, hostname, family, (ares_host_callback)ares_query_completed_cb, conn); - *waitp = TRUE; /* please wait for the response */ + *waitp = 1; /* expect asynchronous response */ } return NULL; /* no struct yet */ } diff --git a/lib/hostip.h b/lib/hostip.h index 85425b6ce..ea2e3cd87 100644 --- a/lib/hostip.h +++ b/lib/hostip.h @@ -1,5 +1,5 @@ -#ifndef __HOSTIP_H -#define __HOSTIP_H +#ifndef HEADER_CURL_HOSTIP_H +#define HEADER_CURL_HOSTIP_H /*************************************************************************** * _ _ ____ _ * Project ___| | | | _ \| | @@ -165,6 +165,9 @@ int curl_dogetnameinfo(GETNAMEINFO_QUAL_ARG1 GETNAMEINFO_TYPE_ARG1 sa, int line, const char *source); #endif +/* IPv4 threadsafe resolve function used for synch and asynch builds */ +Curl_addrinfo *Curl_ipv4_resolve_r(const char * hostname, int port); + /* * Curl_addrinfo_callback() is used when we build with any asynch specialty. * Handles end of async request processing. Inserts ai into hostcache when @@ -214,4 +217,4 @@ void Curl_destroy_thread_data(struct Curl_async *async); extern sigjmp_buf curl_jmpenv; #endif -#endif +#endif /* HEADER_CURL_HOSTIP_H */ diff --git a/lib/hostip4.c b/lib/hostip4.c index b4f6bd9d3..b637ce6ec 100644 --- a/lib/hostip4.c +++ b/lib/hostip4.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2009, Daniel Stenberg, , et al. + * Copyright (C) 1998 - 2010, 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 @@ -87,8 +87,7 @@ bool Curl_ipvalid(struct SessionHandle *data) return TRUE; /* OK, proceed */ } -#ifdef CURLRES_SYNCH /* the functions below are for synchronous resolves */ - +#ifdef CURLRES_SYNCH /* * Curl_getaddrinfo() - the ipv4 synchronous version. * @@ -109,6 +108,33 @@ Curl_addrinfo *Curl_getaddrinfo(struct connectdata *conn, const char *hostname, int port, int *waitp) +{ + Curl_addrinfo *ai = NULL; + +#ifdef CURL_DISABLE_VERBOSE_STRINGS + (void)conn; +#endif + + *waitp = 0; /* synchronous response only */ + + ai = Curl_ipv4_resolve_r(hostname, port); + if(!ai) + infof(conn->data, "Curl_ipv4_resolve_r failed for %s\n", hostname); + + return ai; +} +#endif /* CURLRES_SYNCH */ +#endif /* CURLRES_IPV4 */ + +/* + * Curl_ipv4_resolve_r() - ipv4 threadsafe resolver function. + * + * This is used for both synchronous and asynchronous resolver builds, + * implying that only threadsafe code and function calls may be used. + * + */ +Curl_addrinfo *Curl_ipv4_resolve_r(const char *hostname, + int port) { #if defined(HAVE_GETHOSTBYNAME_R_3) int res; @@ -118,17 +144,28 @@ Curl_addrinfo *Curl_getaddrinfo(struct connectdata *conn, struct in_addr in; struct hostent *buf = NULL; -#ifdef CURL_DISABLE_VERBOSE_STRINGS - (void)conn; -#endif - - *waitp = 0; /* don't wait, we act synchronously */ - if(Curl_inet_pton(AF_INET, hostname, &in) > 0) /* This is a dotted IP address 123.123.123.123-style */ return Curl_ip2addr(AF_INET, &in, hostname, port); -#if defined(HAVE_GETHOSTBYNAME_R) +#if defined(HAVE_GETADDRINFO_THREADSAFE) + else { + struct addrinfo hints; + char sbuf[NI_MAXSERV]; + char *sbufptr = NULL; + int error; + + memset(&hints, 0, sizeof(hints)); + hints.ai_family = PF_INET; + hints.ai_socktype = SOCK_STREAM; + if (port) { + snprintf(sbuf, sizeof(sbuf), "%d", port); + sbufptr = sbuf; + } + hints.ai_flags = AI_CANONNAME; + error = Curl_getaddrinfo_ex(hostname, sbufptr, &hints, &ai); + +#elif defined(HAVE_GETHOSTBYNAME_R) /* * gethostbyname_r() is the preferred resolve function for many platforms. * Since there are three different versions of it, the following code is @@ -260,8 +297,7 @@ Curl_addrinfo *Curl_getaddrinfo(struct connectdata *conn, } else #endif /* HAVE_GETHOSTBYNAME_R_3 */ - { - infof(conn->data, "gethostbyname_r(2) failed for %s\n", hostname); + { h = NULL; /* set return code to NULL */ free(buf); } @@ -276,8 +312,6 @@ Curl_addrinfo *Curl_getaddrinfo(struct connectdata *conn, #else h = gethostbyname(hostname); #endif - if(!h) - infof(conn->data, "gethostbyname(2) failed for %s\n", hostname); #endif /*HAVE_GETHOSTBYNAME_R */ } @@ -290,7 +324,3 @@ Curl_addrinfo *Curl_getaddrinfo(struct connectdata *conn, return ai; } - -#endif /* CURLRES_SYNCH */ -#endif /* CURLRES_IPV4 */ - diff --git a/lib/hostip6.c b/lib/hostip6.c index ba50a0360..bb2c5e46d 100644 --- a/lib/hostip6.c +++ b/lib/hostip6.c @@ -126,7 +126,7 @@ bool Curl_ipvalid(struct SessionHandle *data) return TRUE; } -#if !defined(USE_THREADING_GETADDRINFO) && !defined(CURLRES_ARES) +#if defined(CURLRES_SYNCH) #ifdef DEBUG_ADDRINFO static void dump_addrinfo(struct connectdata *conn, const Curl_addrinfo *ai) @@ -170,7 +170,7 @@ Curl_addrinfo *Curl_getaddrinfo(struct connectdata *conn, int pf; struct SessionHandle *data = conn->data; - *waitp=0; /* don't wait, we have the response now */ + *waitp = 0; /* synchronous response only */ /* * Check if a limited name resolve has been requested. @@ -234,6 +234,6 @@ Curl_addrinfo *Curl_getaddrinfo(struct connectdata *conn, return res; } -#endif /* !USE_THREADING_GETADDRINFO && !CURLRES_ARES */ +#endif /* CURLRES_SYNCH */ #endif /* CURLRES_IPV6 */ diff --git a/lib/hostthre.c b/lib/hostthre.c index 32e946ccb..2205c8fdd 100644 --- a/lib/hostthre.c +++ b/lib/hostthre.c @@ -50,8 +50,14 @@ #include #endif -#ifdef HAVE_PROCESS_H -#include +#if defined(USE_THREADS_POSIX) +# ifdef HAVE_PTHREAD_H +# include +# endif +#elif defined(USE_THREADS_WIN32) +# ifdef HAVE_PROCESS_H +# include +# endif #endif #if (defined(NETWARE) && defined(__NOVELL_LIBC__)) @@ -68,12 +74,12 @@ #include "url.h" #include "multiif.h" #include "inet_pton.h" +#include "inet_ntop.h" +#include "curl_threads.h" #define _MPRINTF_REPLACE /* use our functions only */ #include -#include "inet_ntop.h" - #include "curl_memory.h" /* The last #include file should be: */ #include "memdebug.h" @@ -83,7 +89,7 @@ #endif /*********************************************************************** - * Only for Windows threaded name resolves builds + * Only for threaded name resolves builds **********************************************************************/ #ifdef CURLRES_THREADED @@ -92,251 +98,162 @@ static bool init_resolve_thread(struct connectdata *conn, const char *hostname, int port, const struct addrinfo *hints); -#ifdef CURLRES_IPV4 - #define THREAD_FUNC gethostbyname_thread - #define THREAD_NAME "gethostbyname_thread" -#else - #define THREAD_FUNC getaddrinfo_thread - #define THREAD_NAME "getaddrinfo_thread" -#endif -struct thread_data { - HANDLE thread_hnd; - unsigned thread_id; - DWORD thread_status; - curl_socket_t dummy_sock; /* dummy for Curl_resolv_fdset() */ - HANDLE mutex_waiting; /* marks that we are still waiting for a resolve */ - HANDLE event_resolved; /* marks that the thread obtained the information */ - HANDLE event_thread_started; /* marks that the thread has initialized and - started */ - HANDLE mutex_terminate; /* serializes access to flag_terminate */ - HANDLE event_terminate; /* flag for thread to terminate instead of calling - callbacks */ -#ifdef CURLRES_IPV6 +/* Data for synchronization between resolver thread and its parent */ +struct thread_sync_data { + curl_mutex_t * mtx; + int done; + + char * hostname; /* hostname to resolve, Curl_async.hostname + duplicate */ + int port; + int sock_error; + Curl_addrinfo *res; +#ifdef HAVE_GETADDRINFO struct addrinfo hints; #endif }; -/* Data for synchronization between resolver thread and its parent */ -struct thread_sync_data { - HANDLE mutex_waiting; /* thread_data.mutex_waiting duplicate */ - HANDLE mutex_terminate; /* thread_data.mutex_terminate duplicate */ - HANDLE event_terminate; /* thread_data.event_terminate duplicate */ - char * hostname; /* hostname to resolve, Curl_async.hostname - duplicate */ +struct thread_data { + curl_thread_t thread_hnd; + curl_socket_t dummy_sock; + unsigned int poll_interval; + int interval_end; + struct thread_sync_data tsd; }; +static struct thread_sync_data * conn_thread_sync_data(struct connectdata *conn) +{ + return &(((struct thread_data *)conn->async.os_specific)->tsd); +} + +#define CONN_THREAD_SYNC_DATA(conn) &(((conn)->async.os_specific)->tsd); + /* Destroy resolver thread synchronization data */ static void destroy_thread_sync_data(struct thread_sync_data * tsd) { + if (tsd->mtx) { + Curl_mutex_destroy(tsd->mtx); + free(tsd->mtx); + } + if(tsd->hostname) free(tsd->hostname); - if(tsd->event_terminate) - CloseHandle(tsd->event_terminate); - if(tsd->mutex_terminate) - CloseHandle(tsd->mutex_terminate); - if(tsd->mutex_waiting) - CloseHandle(tsd->mutex_waiting); + + if (tsd->res) + Curl_freeaddrinfo(tsd->res); + memset(tsd,0,sizeof(*tsd)); } /* Initialize resolver thread synchronization data */ static -BOOL init_thread_sync_data(struct thread_data * td, +int init_thread_sync_data(struct thread_sync_data * tsd, const char * hostname, - struct thread_sync_data * tsd) + int port, + const struct addrinfo *hints) { - HANDLE curr_proc = GetCurrentProcess(); - memset(tsd, 0, sizeof(*tsd)); - if(!DuplicateHandle(curr_proc, td->mutex_waiting, - curr_proc, &tsd->mutex_waiting, 0, FALSE, - DUPLICATE_SAME_ACCESS)) { - /* failed to duplicate the mutex, no point in continuing */ - destroy_thread_sync_data(tsd); - return FALSE; - } - if(!DuplicateHandle(curr_proc, td->mutex_terminate, - curr_proc, &tsd->mutex_terminate, 0, FALSE, - DUPLICATE_SAME_ACCESS)) { - /* failed to duplicate the mutex, no point in continuing */ - destroy_thread_sync_data(tsd); - return FALSE; - } - if(!DuplicateHandle(curr_proc, td->event_terminate, - curr_proc, &tsd->event_terminate, 0, FALSE, - DUPLICATE_SAME_ACCESS)) { - /* failed to duplicate the event, no point in continuing */ - destroy_thread_sync_data(tsd); - return FALSE; - } + + tsd->port = port; +#ifdef CURLRES_IPV6 + DEBUGASSERT(hints); + tsd->hints = *hints; +#else + (void) hints; +#endif + + tsd->mtx = malloc(sizeof(curl_mutex_t)); + if (tsd->mtx == NULL) goto err_exit; + + Curl_mutex_init(tsd->mtx); + + tsd->sock_error = CURL_ASYNC_SUCCESS; + /* Copying hostname string because original can be destroyed by parent * thread during gethostbyname execution. */ tsd->hostname = strdup(hostname); - if(!tsd->hostname) { - /* Memory allocation failed */ - destroy_thread_sync_data(tsd); - return FALSE; - } - return TRUE; + if (!tsd->hostname) goto err_exit; + + return 1; + + err_exit: + /* Memory allocation failed */ + destroy_thread_sync_data(tsd); + return 0; } -/* acquire resolver thread synchronization */ -static -BOOL acquire_thread_sync(struct thread_sync_data * tsd) -{ - /* is the thread initiator still waiting for us ? */ - if(WaitForSingleObject(tsd->mutex_waiting, 0) == WAIT_TIMEOUT) { - /* yes, it is */ - - /* Waiting access to event_terminate */ - if(WaitForSingleObject(tsd->mutex_terminate, INFINITE) != WAIT_OBJECT_0) { - /* Something went wrong - now just ignoring */ - } - else { - if(WaitForSingleObject(tsd->event_terminate, 0) != WAIT_TIMEOUT) { - /* Parent thread signaled us to terminate. - * This means that all data in conn->async is now destroyed - * and we cannot use it. - */ - } - else { - return TRUE; - } - } - } - return FALSE; -} - -/* release resolver thread synchronization */ -static -void release_thread_sync(struct thread_sync_data * tsd) -{ - ReleaseMutex(tsd->mutex_terminate); -} - -#if defined(CURLRES_IPV4) /* - * gethostbyname_thread() resolves a name, calls the Curl_addrinfo_callback - * and then exits. - * - * For builds without ARES/ENABLE_IPV6, create a resolver thread and wait on - * it. + * gethostbyname_thread() resolves a name and then exits. */ -static unsigned __stdcall gethostbyname_thread (void *arg) +static unsigned int CURL_STDCALL gethostbyname_thread (void *arg) { - struct connectdata *conn = (struct connectdata*) arg; - struct thread_data *td = (struct thread_data*) conn->async.os_specific; - struct hostent *he; - int rc = 0; + struct thread_sync_data *tsd = (struct thread_sync_data *)arg; - /* Duplicate the passed mutex and event handles. - * This allows us to use it even after the container gets destroyed - * due to a resolver timeout. - */ - struct thread_sync_data tsd = { 0,0,0,NULL }; + tsd->res = Curl_ipv4_resolve_r(tsd->hostname, tsd->port); - if(!init_thread_sync_data(td, conn->async.hostname, &tsd)) { - /* thread synchronization data initialization failed */ - return (unsigned)-1; - } + if (!tsd->res) { + tsd->sock_error = SOCKERRNO; + if (tsd->sock_error == 0) + tsd->sock_error = ENOMEM; + } - conn->async.status = NO_DATA; /* pending status */ - SET_SOCKERRNO(conn->async.status); + Curl_mutex_acquire(tsd->mtx); + tsd->done = 1; + Curl_mutex_release(tsd->mtx); - /* Signaling that we have initialized all copies of data and handles we - need */ - SetEvent(td->event_thread_started); - - he = gethostbyname (tsd.hostname); - - /* is parent thread waiting for us and are we able to access conn members? */ - if(acquire_thread_sync(&tsd)) { - Curl_addrinfo *ai = Curl_he2ai(he, conn->async.port); - - /* Mark that we have obtained the information, and that we are calling - * back with it. */ - SetEvent(td->event_resolved); - if(ai) { - rc = Curl_addrinfo_callback(conn, CURL_ASYNC_SUCCESS, ai); - } - else { - rc = Curl_addrinfo_callback(conn, SOCKERRNO, NULL); - } - release_thread_sync(&tsd); - } - - /* clean up */ - destroy_thread_sync_data(&tsd); - - return (rc); - /* An implicit _endthreadex() here */ + return 0; } -#elif defined(CURLRES_IPV6) +static int getaddrinfo_complete(struct connectdata *conn) +{ + struct thread_sync_data *tsd = conn_thread_sync_data(conn); + int rc; + + rc = Curl_addrinfo_callback(conn, tsd->sock_error, tsd->res); + /* The tsd->res structure has been copied to async.dns and perhaps the DNS cache. + Set our copy to NULL so destroy_thread_sync_data doesn't free it. + */ + tsd->res = NULL; + + return rc; +} + + +#if defined(HAVE_GETADDRINFO) /* - * getaddrinfo_thread() resolves a name, calls Curl_addrinfo_callback and then - * exits. + * getaddrinfo_thread() resolves a name and then exits. * * For builds without ARES, but with ENABLE_IPV6, create a resolver thread * and wait on it. */ -static unsigned __stdcall getaddrinfo_thread (void *arg) +static unsigned int CURL_STDCALL getaddrinfo_thread (void *arg) { - struct connectdata *conn = (struct connectdata*) arg; - struct thread_data *td = (struct thread_data*) conn->async.os_specific; - Curl_addrinfo *res; + struct thread_sync_data *tsd = (struct thread_sync_data*)arg; char service [NI_MAXSERV]; - int rc; - struct addrinfo hints = td->hints; + int rc; - /* Duplicate the passed mutex handle. - * This allows us to use it even after the container gets destroyed - * due to a resolver timeout. - */ - struct thread_sync_data tsd = { 0,0,0,NULL }; + snprintf(service, sizeof(service), "%d", tsd->port); - if(!init_thread_sync_data(td, conn->async.hostname, &tsd)) { - /* thread synchronization data initialization failed */ - return -1; + rc = Curl_getaddrinfo_ex(tsd->hostname, service, &tsd->hints, &tsd->res); + + if (rc != 0) { + tsd->sock_error = SOCKERRNO; + if (tsd->sock_error == 0) + tsd->sock_error = ENOMEM; } - itoa(conn->async.port, service, 10); + Curl_mutex_acquire(tsd->mtx); + tsd->done = 1; + Curl_mutex_release(tsd->mtx); - conn->async.status = NO_DATA; /* pending status */ - SET_SOCKERRNO(conn->async.status); - - /* Signaling that we have initialized all copies of data and handles we - need */ - SetEvent(td->event_thread_started); - - rc = Curl_getaddrinfo_ex(tsd.hostname, service, &hints, &res); - - /* is parent thread waiting for us and are we able to access conn members? */ - if(acquire_thread_sync(&tsd)) { - /* Mark that we have obtained the information, and that we are calling - back with it. */ - SetEvent(td->event_resolved); - - if(rc == 0) { - rc = Curl_addrinfo_callback(conn, CURL_ASYNC_SUCCESS, res); - } - else { - rc = Curl_addrinfo_callback(conn, SOCKERRNO, NULL); - } - release_thread_sync(&tsd); - } - - /* clean up */ - destroy_thread_sync_data(&tsd); - - return (rc); - /* An implicit _endthreadex() here */ + return 0; } -#endif + +#endif /* HAVE_GETADDRINFO */ /* * Curl_destroy_thread_data() cleans up async resolver data and thread handle. @@ -349,39 +266,15 @@ void Curl_destroy_thread_data (struct Curl_async *async) if(async->os_specific) { struct thread_data *td = (struct thread_data*) async->os_specific; - curl_socket_t sock = td->dummy_sock; - if(td->mutex_terminate && td->event_terminate) { - /* Signaling resolver thread to terminate */ - if(WaitForSingleObject(td->mutex_terminate, INFINITE) == WAIT_OBJECT_0) { - SetEvent(td->event_terminate); - ReleaseMutex(td->mutex_terminate); - } - else { - /* Something went wrong - just ignoring it */ - } - } - - if(td->mutex_terminate) - CloseHandle(td->mutex_terminate); - if(td->event_terminate) - CloseHandle(td->event_terminate); - if(td->event_thread_started) - CloseHandle(td->event_thread_started); - - if(sock != CURL_SOCKET_BAD) - sclose(sock); - - /* destroy the synchronization objects */ - if(td->mutex_waiting) - CloseHandle(td->mutex_waiting); - td->mutex_waiting = NULL; - if(td->event_resolved) - CloseHandle(td->event_resolved); - - if(td->thread_hnd) - CloseHandle(td->thread_hnd); + if (td->dummy_sock != CURL_SOCKET_BAD) + sclose(td->dummy_sock); + 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; @@ -399,114 +292,58 @@ static bool init_resolve_thread (struct connectdata *conn, const struct addrinfo *hints) { struct thread_data *td = calloc(1, sizeof(struct thread_data)); - HANDLE thread_and_event[2] = {0}; + int err = ENOMEM; - if(!td) { - SET_ERRNO(ENOMEM); - return FALSE; - } - - Curl_safefree(conn->async.hostname); - conn->async.hostname = strdup(hostname); - if(!conn->async.hostname) { - free(td); - SET_ERRNO(ENOMEM); - return FALSE; - } + conn->async.os_specific = (void*) td; + if(!td) + goto err_exit; conn->async.port = port; conn->async.done = FALSE; conn->async.status = 0; conn->async.dns = NULL; - conn->async.os_specific = (void*) td; td->dummy_sock = CURL_SOCKET_BAD; + td->thread_hnd = curl_thread_t_null; - /* Create the mutex used to inform the resolver thread that we're - * still waiting, and take initial ownership. - */ - td->mutex_waiting = CreateMutex(NULL, TRUE, NULL); - if(td->mutex_waiting == NULL) { - Curl_destroy_thread_data(&conn->async); - SET_ERRNO(EAGAIN); - return FALSE; - } + if (!init_thread_sync_data(&td->tsd, hostname, port, hints)) + goto err_exit; - /* Create the event that the thread uses to inform us that it's - * done resolving. Do not signal it. - */ - td->event_resolved = CreateEvent(NULL, TRUE, FALSE, NULL); - if(td->event_resolved == NULL) { - Curl_destroy_thread_data(&conn->async); - SET_ERRNO(EAGAIN); - return FALSE; - } - /* Create the mutex used to serialize access to event_terminated - * between us and resolver thread. - */ - td->mutex_terminate = CreateMutex(NULL, FALSE, NULL); - if(td->mutex_terminate == NULL) { - Curl_destroy_thread_data(&conn->async); - SET_ERRNO(EAGAIN); - return FALSE; - } - /* Create the event used to signal thread that it should terminate. - */ - td->event_terminate = CreateEvent(NULL, TRUE, FALSE, NULL); - if(td->event_terminate == NULL) { - Curl_destroy_thread_data(&conn->async); - SET_ERRNO(EAGAIN); - return FALSE; - } - /* Create the event used by thread to inform it has initialized its own data. - */ - td->event_thread_started = CreateEvent(NULL, TRUE, FALSE, NULL); - if(td->event_thread_started == NULL) { - Curl_destroy_thread_data(&conn->async); - SET_ERRNO(EAGAIN); - return FALSE; - } + Curl_safefree(conn->async.hostname); + conn->async.hostname = strdup(hostname); + if(!conn->async.hostname) + goto err_exit; -#ifdef _WIN32_WCE - td->thread_hnd = (HANDLE) CreateThread(NULL, 0, - (LPTHREAD_START_ROUTINE) THREAD_FUNC, - conn, 0, &td->thread_id); -#else - td->thread_hnd = (HANDLE) _beginthreadex(NULL, 0, THREAD_FUNC, - conn, 0, &td->thread_id); +#ifdef WIN32 + /* This socket is only to keep Curl_resolv_fdset() and select() happy; + * should never become signalled for read since it's unbound but + * Windows needs at least 1 socket in select(). + */ + td->dummy_sock = socket(AF_INET, SOCK_DGRAM, 0); + if (td->dummy_sock == CURL_SOCKET_BAD) + goto err_exit; #endif -#ifdef CURLRES_IPV6 - DEBUGASSERT(hints); - td->hints = *hints; +#ifdef HAVE_GETADDRINFO + td->thread_hnd = Curl_thread_create(getaddrinfo_thread, &td->tsd); #else - (void) hints; + td->thread_hnd = Curl_thread_create(gethostbyname_thread, &td->tsd); #endif if(!td->thread_hnd) { #ifndef _WIN32_WCE - SET_ERRNO(errno); + err = errno; #endif - Curl_destroy_thread_data(&conn->async); - return FALSE; + goto err_exit; } - /* Waiting until the thread will initialize its data or it will exit due errors. - */ - thread_and_event[0] = td->thread_hnd; - thread_and_event[1] = td->event_thread_started; - if(WaitForMultipleObjects(sizeof(thread_and_event) / - sizeof(thread_and_event[0]), - (const HANDLE*)thread_and_event, FALSE, - INFINITE) == WAIT_FAILED) { - /* The resolver thread has been created, - * most probably it works now - ignoring this "minor" error - */ - } - /* This socket is only to keep Curl_resolv_fdset() and select() happy; - * should never become signalled for read/write since it's unbound but - * Windows needs atleast 1 socket in select(). - */ - td->dummy_sock = socket(AF_INET, SOCK_DGRAM, 0); + return TRUE; + + err_exit: + Curl_destroy_thread_data(&conn->async); + + SET_ERRNO(err); + + return FALSE; } @@ -523,84 +360,33 @@ CURLcode Curl_wait_for_resolv(struct connectdata *conn, { struct thread_data *td = (struct thread_data*) conn->async.os_specific; struct SessionHandle *data = conn->data; - long timeout; - DWORD status; CURLcode rc; DEBUGASSERT(conn && td); - /* now, see if there's a connect timeout or a regular timeout to - use instead of the default one */ - timeout = - conn->data->set.connecttimeout ? conn->data->set.connecttimeout : - conn->data->set.timeout ? conn->data->set.timeout : - CURL_TIMEOUT_RESOLVE * 1000; /* default name resolve timeout */ - /* wait for the thread to resolve the name */ - status = WaitForSingleObject(td->event_resolved, timeout); - - /* mark that we are now done waiting */ - ReleaseMutex(td->mutex_waiting); - - /* close our handle to the mutex, no point in hanging on to it */ - CloseHandle(td->mutex_waiting); - td->mutex_waiting = NULL; - - /* close the event handle, it's useless now */ - CloseHandle(td->event_resolved); - td->event_resolved = NULL; - - /* has the resolver thread succeeded in resolving our query ? */ - if(status == WAIT_OBJECT_0) { - /* wait for the thread to exit, it's in the callback sequence */ - if(WaitForSingleObject(td->thread_hnd, 5000) == WAIT_TIMEOUT) { - TerminateThread(td->thread_hnd, 0); - conn->async.done = TRUE; - td->thread_status = (DWORD)-1; - } - else { - /* Thread finished before timeout; propagate Winsock error to this - * thread. 'conn->async.done = TRUE' is set in - * Curl_addrinfo4/6_callback(). - */ - SET_SOCKERRNO(conn->async.status); - GetExitCodeThread(td->thread_hnd, &td->thread_status); - } - } - else { - conn->async.done = TRUE; - td->thread_status = (DWORD)-1; + if (Curl_thread_join(&td->thread_hnd)) { + rc = getaddrinfo_complete(conn); + } else { + DEBUGASSERT(0); } + conn->async.done = TRUE; + if(entry) *entry = conn->async.dns; - rc = CURLE_OK; - if(!conn->async.dns) { /* a name was not resolved */ - if(td->thread_status == CURLE_OUT_OF_MEMORY) { - rc = CURLE_OUT_OF_MEMORY; - failf(data, "Could not resolve host: %s", curl_easy_strerror(rc)); + if (conn->bits.httpproxy) { + failf(data, "Could not resolve proxy: %s; %s", + conn->async.hostname, Curl_strerror(conn, conn->async.status)); + rc = CURLE_COULDNT_RESOLVE_PROXY; + } else { + failf(data, "Could not resolve host: %s; %s", + conn->async.hostname, Curl_strerror(conn, conn->async.status)); + rc = CURLE_COULDNT_RESOLVE_HOST; } - else if(conn->async.done) { - if(conn->bits.httpproxy) { - failf(data, "Could not resolve proxy: %s; %s", - conn->proxy.dispname, Curl_strerror(conn, conn->async.status)); - rc = CURLE_COULDNT_RESOLVE_PROXY; - } - else { - failf(data, "Could not resolve host: %s; %s", - conn->host.name, Curl_strerror(conn, conn->async.status)); - rc = CURLE_COULDNT_RESOLVE_HOST; - } - } - else if(td->thread_status == (DWORD)-1 || conn->async.status == NO_DATA) { - failf(data, "Resolving host timed out: %s", conn->host.name); - rc = CURLE_OPERATION_TIMEDOUT; - } - else - rc = CURLE_OPERATION_TIMEDOUT; } Curl_destroy_thread_data(&conn->async); @@ -620,19 +406,57 @@ CURLcode Curl_is_resolved(struct connectdata *conn, struct Curl_dns_entry **entry) { struct SessionHandle *data = conn->data; - + struct thread_data *td = (struct thread_data*) conn->async.os_specific; + int done = 0; + *entry = NULL; - if(conn->async.done) { - /* we're done */ + if (!td) { + DEBUGASSERT(td); + return CURLE_COULDNT_RESOLVE_HOST; + } + + Curl_mutex_acquire(td->tsd.mtx); + done = td->tsd.done; + Curl_mutex_release(td->tsd.mtx); + + if (done) { + getaddrinfo_complete(conn); + if (td->poll_interval != 0) + Curl_expire(conn->data, 0); Curl_destroy_thread_data(&conn->async); + if(!conn->async.dns) { failf(data, "Could not resolve host: %s; %s", conn->host.name, Curl_strerror(conn, conn->async.status)); return CURLE_COULDNT_RESOLVE_HOST; } *entry = conn->async.dns; + } else { + /* poll for name lookup done with exponential backoff up to 250ms */ + int elapsed; + + elapsed = Curl_tvdiff(Curl_tvnow(), data->progress.t_startsingle); + if (elapsed < 0) { + elapsed = 0; + } + + if (td->poll_interval == 0) { + /* Start at 1ms poll interval */ + td->poll_interval = 1; + } else if (elapsed >= td->interval_end) { + /* Back-off exponentially if last interval expired */ + td->poll_interval *= 2; + } + + if (td->poll_interval > 250) + td->poll_interval = 250; + + td->interval_end = elapsed + td->poll_interval; + + Curl_expire(conn->data, td->poll_interval); } + return CURLE_OK; } @@ -645,18 +469,18 @@ int Curl_resolv_getsock(struct connectdata *conn, if(td && td->dummy_sock != CURL_SOCKET_BAD) { if(numsocks) { - /* return one socket waiting for writable, even though this is just + /* return one socket waiting for readable, even though this is just a dummy */ socks[0] = td->dummy_sock; - return GETSOCK_WRITESOCK(0); + return GETSOCK_READSOCK(0); } } return 0; } -#ifdef CURLRES_IPV4 +#if !defined(HAVE_GETADDRINFO) /* - * Curl_getaddrinfo() - for Windows threading without ENABLE_IPV6. + * Curl_getaddrinfo() - for platforms without getaddrinfo */ Curl_addrinfo *Curl_getaddrinfo(struct connectdata *conn, const char *hostname, @@ -667,7 +491,7 @@ Curl_addrinfo *Curl_getaddrinfo(struct connectdata *conn, struct SessionHandle *data = conn->data; struct in_addr in; - *waitp = 0; /* don't wait, we act synchronously */ + *waitp = 0; /* default to synchronous response */ if(Curl_inet_pton(AF_INET, hostname, &in) > 0) /* This is a dotted IP address 123.123.123.123-style */ @@ -675,27 +499,18 @@ Curl_addrinfo *Curl_getaddrinfo(struct connectdata *conn, /* fire up a new resolver thread! */ if(init_resolve_thread(conn, hostname, port, NULL)) { - *waitp = TRUE; /* please wait for the response */ + *waitp = 1; /* expect asynchronous response */ return NULL; } /* fall-back to blocking version */ - infof(data, "init_resolve_thread() failed for %s; %s\n", - hostname, Curl_strerror(conn, ERRNO)); - - h = gethostbyname(hostname); - if(!h) { - infof(data, "gethostbyname(2) failed for %s:%d; %s\n", - hostname, port, Curl_strerror(conn, SOCKERRNO)); - return NULL; - } - return Curl_he2ai(h, port); + return Curl_ipv4_resolve_r(hostname, port); } -#endif /* CURLRES_IPV4 */ -#ifdef CURLRES_IPV6 +#else /* HAVE_GETADDRINFO */ + /* - * Curl_getaddrinfo() - for Windows threading IPv6 enabled + * Curl_getaddrinfo() - for getaddrinfo */ Curl_addrinfo *Curl_getaddrinfo(struct connectdata *conn, const char *hostname, @@ -706,11 +521,12 @@ Curl_addrinfo *Curl_getaddrinfo(struct connectdata *conn, Curl_addrinfo *res; int error; char sbuf[NI_MAXSERV]; - int pf; + int pf = PF_INET; struct SessionHandle *data = conn->data; - *waitp = FALSE; /* default to synch response */ + *waitp = 0; /* default to synchronous response */ +#if !defined(CURLRES_IPV4) /* * Check if a limited name resolve has been requested. */ @@ -743,6 +559,7 @@ Curl_addrinfo *Curl_getaddrinfo(struct connectdata *conn, sclose(s); } } +#endif /* !CURLRES_IPV4 */ memset(&hints, 0, sizeof(hints)); hints.ai_family = pf; @@ -750,11 +567,11 @@ Curl_addrinfo *Curl_getaddrinfo(struct connectdata *conn, #if 0 /* removed nov 8 2005 before 7.15.1 */ hints.ai_flags = AI_CANONNAME; #endif - itoa(port, sbuf, 10); + snprintf(sbuf, sizeof(sbuf), "%d", port); /* fire up a new resolver thread! */ if(init_resolve_thread(conn, hostname, port, &hints)) { - *waitp = TRUE; /* please wait for the response */ + *waitp = 1; /* expect asynchronous response */ return NULL; } @@ -770,5 +587,7 @@ Curl_addrinfo *Curl_getaddrinfo(struct connectdata *conn, } return res; } -#endif /* CURLRES_IPV6 */ + +#endif /* HAVE_GETADDRINFO */ + #endif /* CURLRES_THREADED */