From b803dc9f208073ef5a4aff94c083a41538b30571 Mon Sep 17 00:00:00 2001 From: Stefan Eissing Date: Thu, 5 Mar 2026 10:20:58 +0100 Subject: [PATCH] async-ares: blocking resolve timeout handling, better Perform the actual timeout calculation in the blocking resolv loop each time in the same way, keeping the logic simpler. The previous version calculated the timeout once, and then reduced it by the elapsed time spent in polling/processing. This is unnecessarily complicated. Closes #20819 --- lib/asyn-ares.c | 93 +++++++++++++++++++++++++------------------------ 1 file changed, 48 insertions(+), 45 deletions(-) diff --git a/lib/asyn-ares.c b/lib/asyn-ares.c index bbf456d1fe..e39e96d2b6 100644 --- a/lib/asyn-ares.c +++ b/lib/asyn-ares.c @@ -368,6 +368,32 @@ out: return result; } +static timediff_t async_ares_poll_timeout(struct async_ares_ctx *ares, + timediff_t timeout_ms) +{ + struct timeval *ares_calced, time_buf, max_timeout; + int itimeout_ms; + +#if TIMEDIFF_T_MAX > INT_MAX + itimeout_ms = (timeout_ms > INT_MAX) ? INT_MAX : + ((timeout_ms < 0) ? -1 : (int)timeout_ms); +#else + itimeout_ms = (int)timeout_ms; +#endif + max_timeout.tv_sec = itimeout_ms / 1000; + max_timeout.tv_usec = (itimeout_ms % 1000) * 1000; + + /* c-ares tells us the shortest timeout of any operation on channel */ + ares_calced = ares_timeout(ares->channel, &max_timeout, &time_buf); + /* use the timeout period ares returned to us above if less than one + second is left, otherwise use 1000ms to make sure the progress callback + gets called frequent enough */ + if(!ares_calced->tv_sec) + return (timediff_t)(ares_calced->tv_usec / 1000); + else + return 1000; +} + /* * Curl_async_await() * @@ -383,67 +409,44 @@ CURLcode Curl_async_await(struct Curl_easy *data, struct Curl_dns_entry **dns) { struct async_ares_ctx *ares = &data->state.async.ares; + struct curltime start = *Curl_pgrs_now(data); CURLcode result = CURLE_OK; - timediff_t timeout_ms; DEBUGASSERT(dns); *dns = NULL; /* clear on entry */ - timeout_ms = Curl_timeleft_ms(data); - if(timeout_ms < 0) { - /* already expired! */ - connclose(data->conn, "Timed out before name resolve started"); - return CURLE_OPERATION_TIMEDOUT; - } - if(!timeout_ms) - timeout_ms = CURL_TIMEOUT_RESOLVE * 1000; /* default name resolve */ - - /* Wait for the name resolve query to complete. */ + /* Wait for the name resolve query to complete or time out. */ while(!result) { - struct timeval *real_timeout, time_buf, max_timeout; - int itimeout_ms; - timediff_t call_timeout_ms; + timediff_t timeout_ms; -#if TIMEDIFF_T_MAX > INT_MAX - itimeout_ms = (timeout_ms > INT_MAX) ? INT_MAX : (int)timeout_ms; -#else - itimeout_ms = (int)timeout_ms; -#endif + timeout_ms = Curl_timeleft_ms(data); + if(!timeout_ms) { /* no applicable timeout from `data`*/ + timediff_t elapsed_ms = curlx_ptimediff_ms(Curl_pgrs_now(data), &start); + if(elapsed_ms < (CURL_TIMEOUT_RESOLVE * 1000)) + timeout_ms = (CURL_TIMEOUT_RESOLVE * 1000) - elapsed_ms; + else + timeout_ms = -1; + } - max_timeout.tv_sec = itimeout_ms / 1000; - max_timeout.tv_usec = (itimeout_ms % 1000) * 1000; + if(timeout_ms < 0) { + result = CURLE_OPERATION_TIMEDOUT; + break; + } - real_timeout = ares_timeout(ares->channel, &max_timeout, &time_buf); - - /* use the timeout period ares returned to us above if less than one - second is left, otherwise use 1000ms to make sure the progress callback - gets called frequent enough */ - if(!real_timeout->tv_sec) - call_timeout_ms = (timediff_t)(real_timeout->tv_usec / 1000); - else - call_timeout_ms = 1000; - - if(Curl_ares_perform(ares->channel, call_timeout_ms) < 0) - return CURLE_UNRECOVERABLE_POLL; + if(Curl_ares_perform(ares->channel, + async_ares_poll_timeout(ares, timeout_ms)) < 0) { + result = CURLE_UNRECOVERABLE_POLL; + break; + } result = Curl_async_is_resolved(data, dns); if(result || data->state.async.done) break; - if(Curl_pgrsUpdate(data)) + if(Curl_pgrsUpdate(data)) { result = CURLE_ABORTED_BY_CALLBACK; - else { - struct curltime now = curlx_now(); /* update in loop */ - timediff_t elapsed_ms = curlx_ptimediff_ms(&now, Curl_pgrs_now(data)); - if(elapsed_ms <= 0) - timeout_ms -= 1; /* always deduct at least 1 */ - else if(elapsed_ms > timeout_ms) - timeout_ms = -1; - else - timeout_ms -= elapsed_ms; + break; } - if(timeout_ms < 0) - result = CURLE_OPERATION_TIMEDOUT; } /* Operation complete, if the lookup was successful we now have the entry