mirror of
https://github.com/yhirose/cpp-httplib
synced 2026-06-08 18:30:49 +00:00
The Linux/glibc branch of detail::getaddrinfo_with_timeout used getaddrinfo_a(GAI_NOWAIT) with a stack-local struct gaicb. On the connection-timeout branch it called gai_cancel(), which is non-blocking and may return EAI_NOTCANCELED -- in that case the resolver worker thread is still alive and writes back to ar_result on the now-destroyed stack frame after the function has already returned. Drop the entire #elif _GNU_SOURCE && __GLIBC__ branch and let glibc fall through to the existing std::thread + std::shared_ptr<State> implementation that the file already uses for other Unix systems. That path captures shared ownership in the resolver lambda, so the state outlives the caller's frame whether or not the worker finishes in time -- no stack frame is ever referenced after return. The reproducer added in #2433 (issue-2431 repro CI job) goes from hanging at job teardown to passing in ~25s with this change.
This commit is contained in:
@@ -5905,58 +5905,17 @@ inline int getaddrinfo_with_timeout(const char *node, const char *service,
|
|||||||
|
|
||||||
*res = result_addrinfo;
|
*res = result_addrinfo;
|
||||||
return 0;
|
return 0;
|
||||||
#elif defined(_GNU_SOURCE) && defined(__GLIBC__) && \
|
|
||||||
(__GLIBC__ > 2 || (__GLIBC__ == 2 && __GLIBC_MINOR__ >= 2))
|
|
||||||
// Linux implementation using getaddrinfo_a for asynchronous DNS resolution
|
|
||||||
struct gaicb request;
|
|
||||||
struct gaicb *requests[1] = {&request};
|
|
||||||
struct sigevent sevp;
|
|
||||||
struct timespec timeout;
|
|
||||||
|
|
||||||
// Initialize the request structure
|
|
||||||
memset(&request, 0, sizeof(request));
|
|
||||||
request.ar_name = node;
|
|
||||||
request.ar_service = service;
|
|
||||||
request.ar_request = hints;
|
|
||||||
|
|
||||||
// Set up timeout
|
|
||||||
timeout.tv_sec = timeout_sec;
|
|
||||||
timeout.tv_nsec = 0;
|
|
||||||
|
|
||||||
// Initialize sigevent structure (not used, but required)
|
|
||||||
memset(&sevp, 0, sizeof(sevp));
|
|
||||||
sevp.sigev_notify = SIGEV_NONE;
|
|
||||||
|
|
||||||
// Start asynchronous resolution
|
|
||||||
int start_result = getaddrinfo_a(GAI_NOWAIT, requests, 1, &sevp);
|
|
||||||
if (start_result != 0) { return start_result; }
|
|
||||||
|
|
||||||
// Wait for completion with timeout
|
|
||||||
int wait_result =
|
|
||||||
gai_suspend((const struct gaicb *const *)requests, 1, &timeout);
|
|
||||||
|
|
||||||
if (wait_result == 0 || wait_result == EAI_ALLDONE) {
|
|
||||||
// Completed successfully, get the result
|
|
||||||
int gai_result = gai_error(&request);
|
|
||||||
if (gai_result == 0) {
|
|
||||||
*res = request.ar_result;
|
|
||||||
return 0;
|
|
||||||
} else {
|
|
||||||
// Clean up on error
|
|
||||||
if (request.ar_result) { freeaddrinfo(request.ar_result); }
|
|
||||||
return gai_result;
|
|
||||||
}
|
|
||||||
} else if (wait_result == EAI_AGAIN) {
|
|
||||||
// Timeout occurred, cancel the request
|
|
||||||
gai_cancel(&request);
|
|
||||||
return EAI_AGAIN;
|
|
||||||
} else {
|
|
||||||
// Other error occurred
|
|
||||||
gai_cancel(&request);
|
|
||||||
return wait_result;
|
|
||||||
}
|
|
||||||
#else
|
#else
|
||||||
// Fallback implementation using thread-based timeout for other Unix systems
|
// Fallback implementation using thread-based timeout for other Unix systems.
|
||||||
|
//
|
||||||
|
// The previous Linux/glibc path used getaddrinfo_a(GAI_NOWAIT) with a
|
||||||
|
// stack-local gaicb. On timeout it called gai_cancel(), which is non-
|
||||||
|
// blocking and may return EAI_NOTCANCELED -- the resolver worker would
|
||||||
|
// then write back to the destroyed stack frame after this function had
|
||||||
|
// already returned (#2431). The std::thread + shared_ptr fallback below
|
||||||
|
// is correct for the same reason it works on other platforms: the
|
||||||
|
// worker captures shared ownership of the state, so it stays alive as
|
||||||
|
// long as anyone (caller or worker) still holds a reference.
|
||||||
|
|
||||||
struct GetAddrInfoState {
|
struct GetAddrInfoState {
|
||||||
~GetAddrInfoState() {
|
~GetAddrInfoState() {
|
||||||
|
|||||||
Reference in New Issue
Block a user