multi: multi_wait fixes after #20832

The refactoring in #20832 introduced some inconsistencies between
windows and posix handling, pointed out by reviews. Fix them:

- rename `wait_on_nop` back to `extrawait` as it was called before
- use multi_timeout() to shorten the user supplied timeout for
  both windows/posix in the same way
- remove the extra multi_timeout() check in the posix function
- Add the multi's wakeup socket for monitoring only when there
  are other sockets to poll on or when the caller wants the
  extra waiting time.

Closes #21072
This commit is contained in:
Stefan Eissing 2026-03-23 11:05:07 +01:00 committed by Daniel Stenberg
parent a186ecf4bf
commit dc9a03e855
No known key found for this signature in database
GPG key ID: 5CC908FDB71E12C2

View file

@ -254,6 +254,10 @@ struct Curl_multi *Curl_multi_handle(uint32_t xfer_table_size,
multi->multiplexing = TRUE;
multi->max_concurrent_streams = 100;
multi->last_timeout_ms = -1;
#ifdef ENABLE_WAKEUP
multi->wakeup_pair[0] = CURL_SOCKET_BAD;
multi->wakeup_pair[1] = CURL_SOCKET_BAD;
#endif
if(Curl_mntfy_resize(multi) ||
Curl_uint32_bset_resize(&multi->process, xfer_table_size) ||
@ -296,10 +300,10 @@ struct Curl_multi *Curl_multi_handle(uint32_t xfer_table_size,
goto error;
#endif
#ifdef ENABLE_WAKEUP
if(Curl_wakeup_init(multi->wakeup_pair, TRUE) < 0) {
multi->wakeup_pair[0] = CURL_SOCKET_BAD;
multi->wakeup_pair[1] = CURL_SOCKET_BAD;
}
/* When enabled, rely on this to work. We ignore this in previous
* versions, but that seems an unnecessary complication. */
if(Curl_wakeup_init(multi->wakeup_pair, TRUE) < 0)
goto error;
#endif
#ifdef USE_IPV6
@ -345,6 +349,9 @@ error:
Curl_uint32_bset_destroy(&multi->pending);
Curl_uint32_bset_destroy(&multi->msgsent);
Curl_uint32_tbl_destroy(&multi->xfers);
#ifdef ENABLE_WAKEUP
Curl_wakeup_destroy(multi->wakeup_pair);
#endif
curlx_free(multi);
return NULL;
@ -1135,8 +1142,7 @@ CURLMcode Curl_multi_pollset(struct Curl_easy *data,
/* The admin handle always listens on the wakeup socket when there
* are transfers alive. */
if(data->multi && (data == data->multi->admin) &&
data->multi->xfers_alive &&
(data->multi->wakeup_pair[0] != CURL_SOCKET_BAD)) {
data->multi->xfers_alive) {
result = Curl_pollset_add_in(data, ps, data->multi->wakeup_pair[0]);
}
#endif
@ -1365,7 +1371,7 @@ static CURLMcode multi_winsock_select(struct Curl_multi *multi,
struct curl_waitfd extra_fds[],
unsigned int extra_nfds,
int timeout_ms,
bool wait_on_nop,
bool extrawait,
int *pnevents)
{
CURLMcode mresult = CURLM_OK;
@ -1394,7 +1400,7 @@ static CURLMcode multi_winsock_select(struct Curl_multi *multi,
}
}
if(cpfds->n || wait_on_nop) {
if(cpfds->n || extrawait) {
int pollrc = 0;
if(cpfds->n) { /* pre-check with Winsock */
@ -1475,13 +1481,14 @@ static CURLMcode multi_posix_poll(struct Curl_multi *multi,
struct curl_waitfd extra_fds[],
unsigned int extra_nfds,
int timeout_ms,
bool wait_on_nop,
bool extrawait,
int *pnevents)
{
CURLMcode mresult = CURLM_OK;
int nevents = 0;
size_t i;
(void)multi;
if(cpfds->n) {
int pollrc = Curl_poll(cpfds->pfds, cpfds->n, timeout_ms); /* wait... */
if(pollrc < 0) {
@ -1505,21 +1512,11 @@ static CURLMcode multi_posix_poll(struct Curl_multi *multi,
extra_fds[i].revents = (short)mask;
}
}
else if(wait_on_nop) {
struct curltime expire_time;
long sleep_ms = 0;
/* Avoid busy-looping when there is nothing particular to wait for */
multi_timeout(multi, &expire_time, &sleep_ms);
if(sleep_ms) {
if(sleep_ms > timeout_ms)
sleep_ms = timeout_ms;
/* when there are no easy handles in the multi, this holds a -1
timeout */
else if(sleep_ms < 0)
sleep_ms = timeout_ms;
curlx_wait_ms(sleep_ms);
}
else if(extrawait) {
/* No fds to poll, but asked to obey timeout_ms anyway. We cannot
* use Curl_poll() as it, on some platforms, returns immediately
* without fds. */
curlx_wait_ms(timeout_ms);
}
out:
@ -1536,8 +1533,7 @@ static CURLMcode multi_wait(struct Curl_multi *multi,
unsigned int extra_nfds,
int timeout_ms,
int *ret,
bool wait_on_nop) /* spend time, even if there
* is nothing to monitor */
bool extrawait) /* when no socket, wait */
{
size_t i;
struct curltime expire_time;
@ -1590,7 +1586,11 @@ static CURLMcode multi_wait(struct Curl_multi *multi,
}
#ifdef ENABLE_WAKEUP
if(wait_on_nop && multi->wakeup_pair[0] != CURL_SOCKET_BAD) {
/* If `extrawait` is TRUE *or* we have `extra_fds`to poll *or* we
* have transfer sockets to poll, we obey `timeout_ms`.
* Then we need to also monitor the multi's wakeup
* socket to catch calls to `curl_multi_wakeup()` during the wait. */
if(extrawait || cpfds.n || extra_nfds) {
wakeup_idx = cpfds.n;
if(Curl_pollfds_add_sock(&cpfds, multi->wakeup_pair[0], POLLIN)) {
mresult = CURLM_OUT_OF_MEMORY;
@ -1618,7 +1618,9 @@ static CURLMcode multi_wait(struct Curl_multi *multi,
/* We check the internal timeout *AFTER* we collected all sockets to
* poll. Collecting the sockets may install new timers by protocols
* and connection filters.
* Use the shorter one of the internal and the caller requested timeout. */
* Use the shorter one of the internal and the caller requested timeout.
* If we are called with `!extrawait` and multi_timeout() reports no
* timeouts exist, do not wait. */
multi_timeout(multi, &expire_time, &timeout_internal);
if((timeout_internal >= 0) && (timeout_internal < (long)timeout_ms))
timeout_ms = (int)timeout_internal;
@ -1630,11 +1632,11 @@ static CURLMcode multi_wait(struct Curl_multi *multi,
#ifdef USE_WINSOCK
mresult = multi_winsock_select(multi, &cpfds, curl_nfds,
extra_fds, extra_nfds,
timeout_ms, wait_on_nop, &nevents);
timeout_ms, extrawait, &nevents);
#else
mresult = multi_posix_poll(multi, &cpfds, curl_nfds,
extra_fds, extra_nfds,
timeout_ms, wait_on_nop, &nevents);
timeout_ms, extrawait, &nevents);
#endif
#ifdef ENABLE_WAKEUP
@ -1679,26 +1681,24 @@ CURLMcode curl_multi_wakeup(CURLM *m)
it has to be careful only to access parts of the
Curl_multi struct that are constant */
struct Curl_multi *multi = m;
CURLMcode mresult = CURLM_WAKEUP_FAILURE;
/* GOOD_MULTI_HANDLE can be safely called */
if(!GOOD_MULTI_HANDLE(multi))
return CURLM_BAD_HANDLE;
#ifdef USE_WINSOCK
if(WSASetEvent(multi->wsa_event))
return CURLM_OK;
#endif
#ifdef ENABLE_WAKEUP
/* the wakeup_pair variable is only written during init and cleanup,
making it safe to access from another thread after the init part
and before cleanup */
if(multi->wakeup_pair[1] != CURL_SOCKET_BAD) {
if(Curl_wakeup_signal(multi->wakeup_pair))
return CURLM_WAKEUP_FAILURE;
return CURLM_OK;
}
if(!Curl_wakeup_signal(multi->wakeup_pair))
mresult = CURLM_OK;
#endif
return CURLM_WAKEUP_FAILURE;
#ifdef USE_WINSOCK
if(WSASetEvent(multi->wsa_event))
mresult = CURLM_OK;
#endif
return mresult;
}
/*