multi: enhance pending handles fairness

When trying to connect a pending transfer, remember the `mid` that was
last reactivated and start looking for future pending handles from the
last one forward through the pending bitset.

Background: when many pending handles exist, iterating the bitset always
from the start may become unfair to transfers that were assigned higher
`mid` values.

Fixes #21396
Reported-by: Juan Belón
Closes #21412
This commit is contained in:
Stefan Eissing 2026-04-22 15:00:14 +02:00 committed by Daniel Stenberg
parent d6372e60e5
commit 00cac453c7
No known key found for this signature in database
GPG key ID: 5CC908FDB71E12C2
2 changed files with 22 additions and 2 deletions

View file

@ -3760,13 +3760,32 @@ static void move_pending_to_connect(struct Curl_multi *multi,
*/
static void process_pending_handles(struct Curl_multi *multi)
{
uint32_t mid;
uint32_t mid = multi->last_pending_mid;
if(mid) {
while(Curl_uint32_bset_next(&multi->pending, mid, &mid)) {
struct Curl_easy *data = Curl_multi_get_easy(multi, mid);
if(data) {
move_pending_to_connect(multi, data);
multi->last_pending_mid = mid;
return;
}
/* transfer no longer known, should not happen */
Curl_uint32_bset_remove(&multi->pending, mid);
DEBUGASSERT(0);
}
/* found no pending transfers with `mid` larger than `last_pending_mid`.
* Start at the beginning of the pending set again. */
multi->last_pending_mid = 0;
}
if(Curl_uint32_bset_first(&multi->pending, &mid)) {
do {
struct Curl_easy *data = Curl_multi_get_easy(multi, mid);
if(data) {
move_pending_to_connect(multi, data);
break;
multi->last_pending_mid = mid;
return;
}
/* transfer no longer known, should not happen */
Curl_uint32_bset_remove(&multi->pending, mid);

View file

@ -175,6 +175,7 @@ struct Curl_multi {
#ifdef DEBUGBUILD
unsigned int now_access_count;
#endif
uint32_t last_pending_mid; /* mid of last pending transfer rescheduled */
uint32_t last_resolv_id; /* id of the last DNS resolve operation */
BIT(ipv6_works);
BIT(multiplexing); /* multiplexing wanted */