curl_multi_remove_handle() don't block terminating c-ares requests

Added Curl_resolver_kill() for all three resolver modes, which only
blocks when necessary, along with test 1592 to confirm
curl_multi_remove_handle() doesn't block unless it must.

Closes #3428
Fixes #3371
This commit is contained in:
Brad Spencer 2018-12-14 17:18:22 -04:00 committed by Daniel Stenberg
parent ebe658c1e5
commit 84a30d0a41
No known key found for this signature in database
GPG key ID: 5CC908FDB71E12C2
8 changed files with 225 additions and 21 deletions

View file

@ -5,7 +5,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
* Copyright (C) 1998 - 2018, Daniel Stenberg, <daniel@haxx.se>, et al.
* Copyright (C) 1998 - 2019, 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
@ -198,6 +198,17 @@ void Curl_resolver_cancel(struct connectdata *conn)
destroy_async_data(&conn->async);
}
/*
* We're equivalent to Curl_resolver_cancel() for the c-ares resolver. We
* never block.
*/
void Curl_resolver_kill(struct connectdata *conn)
{
/* We don't need to check the resolver state because we can be called safely
at any time and we always do the same thing. */
Curl_resolver_cancel(conn);
}
/*
* destroy_async_data() cleans up async resolver data.
*/
@ -361,13 +372,13 @@ CURLcode Curl_resolver_is_resolved(struct connectdata *conn,
/*
* Curl_resolver_wait_resolv()
*
* waits for a resolve to finish. This function should be avoided since using
* Waits for a resolve to finish. This function should be avoided since using
* this risk getting the multi interface to "hang".
*
* If 'entry' is non-NULL, make it point to the resolved dns entry
*
* Returns CURLE_COULDNT_RESOLVE_HOST if the host was not resolved, and
* CURLE_OPERATION_TIMEDOUT if a time-out occurred.
* Returns CURLE_COULDNT_RESOLVE_HOST if the host was not resolved,
* CURLE_OPERATION_TIMEDOUT if a time-out occurred, or other errors.
*/
CURLcode Curl_resolver_wait_resolv(struct connectdata *conn,
struct Curl_dns_entry **entry)

View file

@ -5,7 +5,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
* Copyright (C) 1998 - 2018, Daniel Stenberg, <daniel@haxx.se>, et al.
* Copyright (C) 1998 - 2019, 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
@ -461,14 +461,34 @@ static CURLcode resolver_error(struct connectdata *conn)
return result;
}
/*
* Until we gain a way to signal the resolver threads to stop early, we must
* simply wait for them and ignore their results.
*/
void Curl_resolver_kill(struct connectdata *conn)
{
struct thread_data *td = (struct thread_data*) conn->async.os_specific;
/* If we're still resolving, we must wait for the threads to fully clean up,
unfortunately. Otherwise, we can simply cancel to clean up any resolver
data. */
if(td && td->thread_hnd != curl_thread_t_null)
(void)Curl_resolver_wait_resolv(conn, NULL);
else
Curl_resolver_cancel(conn);
}
/*
* Curl_resolver_wait_resolv()
*
* waits for a resolve to finish. This function should be avoided since using
* Waits for a resolve to finish. This function should be avoided since using
* this risk getting the multi interface to "hang".
*
* If 'entry' is non-NULL, make it point to the resolved dns entry
*
* Returns CURLE_COULDNT_RESOLVE_HOST if the host was not resolved,
* CURLE_OPERATION_TIMEDOUT if a time-out occurred, or other errors.
*
* This is the version for resolves-in-a-thread.
*/
CURLcode Curl_resolver_wait_resolv(struct connectdata *conn,
@ -478,6 +498,7 @@ CURLcode Curl_resolver_wait_resolv(struct connectdata *conn,
CURLcode result = CURLE_OK;
DEBUGASSERT(conn && td);
DEBUGASSERT(td->thread_hnd != curl_thread_t_null);
/* wait for the thread to resolve the name */
if(Curl_thread_join(&td->thread_hnd)) {

View file

@ -7,7 +7,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
* Copyright (C) 1998 - 2011, Daniel Stenberg, <daniel@haxx.se>, et al.
* Copyright (C) 1998 - 2019, 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
@ -87,10 +87,25 @@ CURLcode Curl_resolver_duphandle(struct Curl_easy *easy, void **to,
*
* It is called from inside other functions to cancel currently performing
* resolver request. Should also free any temporary resources allocated to
* perform a request.
* perform a request. This never waits for resolver threads to complete.
*
* It is safe to call this when conn is in any state.
*/
void Curl_resolver_cancel(struct connectdata *conn);
/*
* Curl_resolver_kill().
*
* This acts like Curl_resolver_cancel() except it will block until any threads
* associated with the resolver are complete. This never blocks for resolvers
* that do not use threads. This is intended to be the "last chance" function
* that cleans up an in-progress resolver completely (before its owner is about
* to die).
*
* It is safe to call this when conn is in any state.
*/
void Curl_resolver_kill(struct connectdata *conn);
/* Curl_resolver_getsock()
*
* This function is called from the multi_getsock() function. 'sock' is a
@ -117,14 +132,13 @@ CURLcode Curl_resolver_is_resolved(struct connectdata *conn,
/*
* Curl_resolver_wait_resolv()
*
* waits for a resolve to finish. This function should be avoided since using
* Waits for a resolve to finish. This function should be avoided since using
* this risk getting the multi interface to "hang".
*
* If 'entry' is non-NULL, make it point to the resolved dns entry
*
* Returns CURLE_COULDNT_RESOLVE_HOST if the host was not resolved, and
* CURLE_OPERATION_TIMEDOUT if a time-out occurred.
* Returns CURLE_COULDNT_RESOLVE_HOST if the host was not resolved,
* CURLE_OPERATION_TIMEDOUT if a time-out occurred, or other errors.
*/
CURLcode Curl_resolver_wait_resolv(struct connectdata *conn,
struct Curl_dns_entry **dnsentry);
@ -148,6 +162,7 @@ Curl_addrinfo *Curl_resolver_getaddrinfo(struct connectdata *conn,
#ifndef CURLRES_ASYNCH
/* convert these functions if an asynch resolver isn't used */
#define Curl_resolver_cancel(x) Curl_nop_stmt
#define Curl_resolver_kill(x) Curl_nop_stmt
#define Curl_resolver_is_resolved(x,y) CURLE_COULDNT_RESOLVE_HOST
#define Curl_resolver_wait_resolv(x,y) CURLE_COULDNT_RESOLVE_HOST
#define Curl_resolver_getsock(x,y,z) 0

View file

@ -537,10 +537,8 @@ static CURLcode multi_done(struct connectdata **connp,
/* Stop if multi_done() has already been called */
return CURLE_OK;
if(data->mstate == CURLM_STATE_WAITRESOLVE) {
/* still waiting for the resolve to complete */
(void)Curl_resolver_wait_resolv(conn, NULL);
}
/* Stop the resolver and free its own resources (but not dns_entry yet). */
Curl_resolver_kill(conn);
Curl_getoff_all_pipelines(data, conn);
@ -587,7 +585,6 @@ static CURLcode multi_done(struct connectdata **connp,
}
data->state.done = TRUE; /* called just now! */
Curl_resolver_cancel(conn);
if(conn->dns_entry) {
Curl_resolv_unlock(data, conn->dns_entry); /* done with this */