async-thrdd: use thread queue for resolving

Use a thread queue and pool for asnyc threaded DNS resolves.
Add pytest test_21_* for verification.

Add `CURLMOPT_RESOLVE_THREADS_MAX` to allow applications to
resize the thread pool used.

Add `CURLMOPT_QUICK_EXIT` to allow applications to skip thread
joins when cleaning up a multi handle. Multi handles in
`curl_easy_perform()` inherit this from `CURLOPT_QUICK_EXIT`.

Add several debug environment variables for testing.

Closes #20936
This commit is contained in:
Stefan Eissing 2026-03-24 12:50:53 +01:00 committed by Daniel Stenberg
parent 507e7be573
commit 39036c9021
No known key found for this signature in database
GPG key ID: 5CC908FDB71E12C2
31 changed files with 998 additions and 614 deletions

View file

@ -100,6 +100,14 @@ Pointer to pass to push callback. See CURLMOPT_PUSHDATA(3)
Callback that approves or denies server pushes. See CURLMOPT_PUSHFUNCTION(3)
## CURLMOPT_QUICK_EXIT
Enable a quicker cleanup of the multi handle. See CURLMOPT_QUICK_EXIT(3)
## CURLMOPT_RESOLVE_THREADS_MAX
Max threads used for threaded DNS resolver. See CURLMOPT_RESOLVE_THREADS_MAX(3)
## CURLMOPT_SOCKETDATA
Custom pointer passed to the socket callback. See CURLMOPT_SOCKETDATA(3)

View file

@ -173,3 +173,20 @@ Make a blocking, graceful shutdown of all remaining connections when
a multi handle is destroyed. This implicitly triggers for easy handles
that are run via easy_perform. The value of the environment variable
gives the shutdown timeout in milliseconds.
## `CURL_DBG_RESOLV_MAX_THREADS`
Overrides the maximum number of threads for resolver.
## `CURL_DBG_RESOLV_DELAY`
Makes ever threaded resolve experience an initial delay in milliseconds.
## `CURL_DBG_RESOLV_FAIL_DELAY`
With a threaded resolver, delay each lookup by the given milliseconds
and give a negative answer.
## `CURL_DBG_RESOLV_FAIL_IPV6`
Make libcurl fail a resolve for IPv6 only.

View file

@ -0,0 +1,60 @@
---
c: Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
SPDX-License-Identifier: curl
Title: CURLMOPT_QUICK_EXIT
Section: 3
Source: libcurl
See-also:
- CURLOPT_QUICK_EXIT (3)
Protocol:
- All
Added-in: 8.20.0
---
# NAME
CURLOPT_QUICK_EXIT - allow libcurl to exit quickly
# SYNOPSIS
~~~c
#include <curl/curl.h>
CURLMcode curl_multi_setopt(CURLM *handle, CURLMOPT_QUICK_EXIT,
long value);
~~~
# DESCRIPTION
Pass a long as a parameter, 1L meaning that when recovering from a timeout,
libcurl should skip lengthy cleanups that are intended to avoid all kinds of
leaks (threads etc.), as the caller program is about to call exit() anyway.
This allows for a swift termination after a DNS timeout for example, by
canceling and/or forgetting about a resolver thread, at the expense of a
possible (though short-lived) leak of associated resources.
# DEFAULT
20.
# %PROTOCOLS%
# EXAMPLE
~~~c
int main(void)
{
CURLM *m = curl_multi_init();
/* do not join threads when cleaning up this multi handle */
curl_multi_setopt(m, CURLMOPT_QUICK_EXIT, 1L);
}
~~~
# %AVAILABILITY%
# RETURN VALUE
curl_multi_setopt(3) returns a CURLMcode indicating success or error.
CURLM_OK (0) means everything was OK, non-zero means an error occurred, see
libcurl-errors(3).

View file

@ -0,0 +1,75 @@
---
c: Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
SPDX-License-Identifier: curl
Title: CURLMOPT_RESOLVE_THREADS_MAX
Section: 3
Source: libcurl
See-also:
- CURLOPT_IPRESOLVE (3)
- CURLOPT_RESOLVE (3)
Protocol:
- All
Added-in: 8.20.0
---
# NAME
CURLMOPT_RESOLVE_THREADS_MAX - max threads for threaded DNS resolver
# SYNOPSIS
~~~c
#include <curl/curl.h>
CURLMcode curl_multi_setopt(CURLM *handle, CURLMOPT_RESOLVE_THREADS_MAX,
long amount);
~~~
# DESCRIPTION
Pass a long for the **amount**. The set number is used as the maximum number
of threads to be used for the threaded DNS resolver. It has to be a
positive number in the range of 32 bits.
When libcurl is built with a threaded resolver, which is the default on
many systems, it uses a thread pool to lookup addresses and other
properties of hostnames so other transfers are not blocked by this.
Threads are started on demand to perform the resolving and shut down
again after a period of inactivity. When the maximum number of threads
is reached, outstanding resolves are held in a queue and served when
a thread becomes available.
The default maximum is expected to work fine for many situations. Application
may override it using this option for the multi handle.
Changing this value while there are resolves in progress is possible.
Increasing the value takes effect right away. Lowering the value does
not close down any resolves, but ends threads above the new maximum
once the resolving is done.
# DEFAULT
20.
# %PROTOCOLS%
# EXAMPLE
~~~c
int main(void)
{
CURLM *m = curl_multi_init();
/* never use more than 5 threads for resolving */
curl_multi_setopt(m, CURLMOPT_RESOLVE_THREADS_MAX, 5L);
}
~~~
# %AVAILABILITY%
# RETURN VALUE
curl_multi_setopt(3) returns a CURLMcode indicating success or error.
CURLM_OK (0) means everything was OK, non-zero means an error occurred, see
libcurl-errors(3).

View file

@ -122,6 +122,8 @@ man_MANS = \
CURLMOPT_PIPELINING_SITE_BL.3 \
CURLMOPT_PUSHDATA.3 \
CURLMOPT_PUSHFUNCTION.3 \
CURLMOPT_QUICK_EXIT.3 \
CURLMOPT_RESOLVE_THREADS_MAX.3 \
CURLMOPT_SOCKETDATA.3 \
CURLMOPT_SOCKETFUNCTION.3 \
CURLMOPT_TIMERDATA.3 \

View file

@ -579,6 +579,8 @@ CURLMOPT_PIPELINING_SERVER_BL 7.30.0
CURLMOPT_PIPELINING_SITE_BL 7.30.0
CURLMOPT_PUSHDATA 7.44.0
CURLMOPT_PUSHFUNCTION 7.44.0
CURLMOPT_QUICK_EXIT 8.20.0
CURLMOPT_RESOLVE_THREADS_MAX 8.20.0
CURLMOPT_SOCKETDATA 7.15.4
CURLMOPT_SOCKETFUNCTION 7.15.4
CURLMOPT_TIMERDATA 7.16.0