connection: attached transfer count

Since we no longer traverse the transfers attached to a connection,
change the sparse bitset to just a `uint32_t` counter.

This makes multi_ev the single user of sparse bitsets for transfers
using a socket and allocation failures are handled there correctly.

Refs #19818
Closes #19836
This commit is contained in:
Stefan Eissing 2025-12-04 17:15:33 +01:00 committed by Daniel Stenberg
parent 1def380032
commit d7928029fc
No known key found for this signature in database
GPG Key ID: 5CC908FDB71E12C2
10 changed files with 29 additions and 44 deletions

View File

@ -620,7 +620,7 @@ static void cpool_discard_conn(struct cpool *cpool,
if(CONN_INUSE(conn) && !aborted) {
CURL_TRC_M(data, "[CPOOL] not discarding #%" FMT_OFF_T
" still in use by %u transfers", conn->connection_id,
CONN_ATTACHED(conn));
conn->attached_xfers);
return;
}
@ -664,7 +664,7 @@ void Curl_conn_terminate(struct Curl_easy *data,
* are other users of it */
if(CONN_INUSE(conn) && !aborted) {
DEBUGASSERT(0); /* does this ever happen? */
DEBUGF(infof(data, "Curl_disconnect when inuse: %u", CONN_ATTACHED(conn)));
DEBUGF(infof(data, "conn terminate when inuse: %u", conn->attached_xfers));
return;
}

View File

@ -2786,7 +2786,7 @@ static CURLcode cf_h2_query(struct Curl_cfilter *cf,
CF_DATA_SAVE(save, cf, data);
if(!ctx->h2 || !nghttp2_session_check_request_allowed(ctx->h2)) {
/* the limit is what we have in use right now */
effective_max = CONN_ATTACHED(cf->conn);
effective_max = cf->conn->attached_xfers;
}
else {
effective_max = ctx->max_concurrent_streams;

View File

@ -607,12 +607,11 @@ static void multi_done_locked(struct connectdata *conn,
Curl_detach_connection(data);
CURL_TRC_M(data, "multi_done_locked, in use=%u",
Curl_uint32_spbset_count(&conn->xfers_attached));
CURL_TRC_M(data, "multi_done_locked, in use=%u", conn->attached_xfers);
if(CONN_INUSE(conn)) {
/* Stop if still used. */
CURL_TRC_M(data, "Connection still in use %u, no more multi_done now!",
Curl_uint32_spbset_count(&conn->xfers_attached));
conn->attached_xfers);
return;
}
@ -906,9 +905,13 @@ void Curl_detach_connection(struct Curl_easy *data)
{
struct connectdata *conn = data->conn;
if(conn) {
Curl_uint32_spbset_remove(&conn->xfers_attached, data->mid);
if(Curl_uint32_spbset_empty(&conn->xfers_attached))
conn->attached_multi = NULL;
/* this should never happen, prevent underflow */
DEBUGASSERT(conn->attached_xfers);
if(conn->attached_xfers) {
conn->attached_xfers--;
if(!conn->attached_xfers)
conn->attached_multi = NULL;
}
}
data->conn = NULL;
}
@ -924,8 +927,9 @@ void Curl_attach_connection(struct Curl_easy *data,
DEBUGASSERT(data);
DEBUGASSERT(!data->conn);
DEBUGASSERT(conn);
DEBUGASSERT(conn->attached_xfers < UINT32_MAX);
data->conn = conn;
Curl_uint32_spbset_add(&conn->xfers_attached, data->mid);
conn->attached_xfers++;
/* all attached transfers must be from the same multi */
if(!conn->attached_multi)
conn->attached_multi = data->multi;

View File

@ -61,20 +61,6 @@ uint32_t Curl_uint32_spbset_count(struct uint32_spbset *bset)
return n;
}
bool Curl_uint32_spbset_empty(struct uint32_spbset *bset)
{
struct uint32_spbset_chunk *chunk;
uint32_t i;
for(chunk = &bset->head; chunk; chunk = chunk->next) {
for(i = 0; i < CURL_UINT32_SPBSET_CH_SLOTS; ++i) {
if(chunk->slots[i])
return FALSE;
}
}
return TRUE;
}
UNITTEST void Curl_uint32_spbset_clear(struct uint32_spbset *bset)
{
struct uint32_spbset_chunk *next, *chunk;

View File

@ -61,9 +61,6 @@ void Curl_uint32_spbset_destroy(struct uint32_spbset *bset);
/* Get the cardinality of the bitset, e.g. numbers present in the set. */
uint32_t Curl_uint32_spbset_count(struct uint32_spbset *bset);
/* TRUE of bitset is empty */
bool Curl_uint32_spbset_empty(struct uint32_spbset *bset);
/* Add the number `i` to the bitset.
* Numbers can be added more than once, without making a difference.
* Returns FALSE if allocations failed. */

View File

@ -561,7 +561,6 @@ void Curl_conn_free(struct Curl_easy *data, struct connectdata *conn)
Curl_safefree(conn->unix_domain_socket);
#endif
Curl_safefree(conn->destination);
Curl_uint32_spbset_destroy(&conn->xfers_attached);
Curl_hash_destroy(&conn->meta_hash);
curlx_free(conn); /* free all the connection oriented data */
@ -893,16 +892,16 @@ static bool url_match_multiplex_limits(struct connectdata *conn,
if(CONN_INUSE(conn) && m->may_multiplex) {
DEBUGASSERT(conn->bits.multiplex);
/* If multiplexed, make sure we do not go over concurrency limit */
if(CONN_ATTACHED(conn) >=
if(conn->attached_xfers >=
Curl_multi_max_concurrent_streams(m->data->multi)) {
infof(m->data, "client side MAX_CONCURRENT_STREAMS reached"
", skip (%u)", CONN_ATTACHED(conn));
", skip (%u)", conn->attached_xfers);
return FALSE;
}
if(CONN_ATTACHED(conn) >=
if(conn->attached_xfers >=
Curl_conn_get_max_concurrent(m->data, conn, FIRSTSOCKET)) {
infof(m->data, "MAX_CONCURRENT_STREAMS reached, skip (%u)",
CONN_ATTACHED(conn));
conn->attached_xfers);
return FALSE;
}
/* When not multiplexed, we have a match here! */
@ -1343,6 +1342,7 @@ static struct connectdata *allocate_conn(struct Curl_easy *data)
conn->recv_idx = 0; /* default for receiving transfer data */
conn->send_idx = 0; /* default for sending transfer data */
conn->connection_id = -1; /* no ID */
conn->attached_xfers = 0;
conn->remote_port = -1; /* unknown at this point */
/* Store creation time to help future close decision making */
@ -1382,9 +1382,6 @@ static struct connectdata *allocate_conn(struct Curl_easy *data)
conn->connect_only = data->set.connect_only;
conn->transport_wanted = TRNSPRT_TCP; /* most of them are TCP streams */
/* Initialize the attached xfers bitset */
Curl_uint32_spbset_init(&conn->xfers_attached);
/* Store the local bind parameters that will be used for this connection */
if(data->set.str[STRING_DEVICE]) {
conn->localdev = curlx_strdup(data->set.str[STRING_DEVICE]);
@ -3806,7 +3803,7 @@ CURLcode Curl_connect(struct Curl_easy *data,
DEBUGASSERT(conn);
Curl_pgrsTime(data, TIMER_POSTQUEUE);
if(reused) {
if(CONN_ATTACHED(conn) > 1)
if(conn->attached_xfers > 1)
/* multiplexed */
*protocol_done = TRUE;
}

View File

@ -618,8 +618,7 @@ struct connectdata {
handle is still used by one or more easy handles and can only used by any
other easy handle without careful consideration (== only for
multiplexing) and it cannot be used by another multi handle! */
#define CONN_INUSE(c) (!Curl_uint32_spbset_empty(&(c)->xfers_attached))
#define CONN_ATTACHED(c) Curl_uint32_spbset_count(&(c)->xfers_attached)
#define CONN_INUSE(c) (!!(c)->attached_xfers)
/**** Fields set when inited and not modified again */
curl_off_t connection_id; /* Contains a unique number to make it easier to
@ -679,7 +678,6 @@ struct connectdata {
was used on this connection. */
struct curltime keepalive;
struct uint32_spbset xfers_attached; /* mids of attached transfers */
/* A connection cache from a SHARE might be used in several multi handles.
* We MUST not reuse connections that are running in another multi,
* for concurrency reasons. That multi might run in another thread.
@ -721,6 +719,9 @@ struct connectdata {
int remote_port; /* the remote port, not the proxy port! */
int conn_to_port; /* the remote port to connect to. valid only if
bits.conn_to_port is set */
uint32_t attached_xfers; /* # of attached easy handles */
#ifdef USE_IPV6
unsigned int scope_id; /* Scope id for IPv6 */
#endif

View File

@ -2718,7 +2718,7 @@ static CURLcode cf_ngtcp2_query(struct Curl_cfilter *cf,
}
else if(ctx->max_bidi_streams) {
uint64_t avail_bidi_streams = 0;
uint64_t max_streams = CONN_ATTACHED(cf->conn);
uint64_t max_streams = cf->conn->attached_xfers;
if(ctx->max_bidi_streams > ctx->used_bidi_streams)
avail_bidi_streams = ctx->max_bidi_streams - ctx->used_bidi_streams;
max_streams += avail_bidi_streams;
@ -2728,7 +2728,7 @@ static CURLcode cf_ngtcp2_query(struct Curl_cfilter *cf,
*pres1 = (int)Curl_multi_max_concurrent_streams(data->multi);
CURL_TRC_CF(data, cf, "query conn[%" FMT_OFF_T "]: "
"MAX_CONCURRENT -> %d (%u in use)",
cf->conn->connection_id, *pres1, CONN_ATTACHED(cf->conn));
cf->conn->connection_id, *pres1, cf->conn->attached_xfers);
CF_DATA_RESTORE(cf, save);
return CURLE_OK;
}

View File

@ -2323,7 +2323,7 @@ static CURLcode cf_osslq_query(struct Curl_cfilter *cf,
return CURLE_HTTP3;
}
/* we report avail + in_use */
v += CONN_ATTACHED(cf->conn);
v += cf->conn->attached_xfers;
*pres1 = (v > INT_MAX) ? INT_MAX : (int)v;
#else
*pres1 = 100;

View File

@ -1493,14 +1493,14 @@ static CURLcode cf_quiche_query(struct Curl_cfilter *cf,
switch(query) {
case CF_QUERY_MAX_CONCURRENT: {
uint64_t max_streams = CONN_ATTACHED(cf->conn);
uint64_t max_streams = cf->conn->attached_xfers;
if(!ctx->goaway && ctx->qconn) {
max_streams += quiche_conn_peer_streams_left_bidi(ctx->qconn);
}
*pres1 = (max_streams > INT_MAX) ? INT_MAX : (int)max_streams;
CURL_TRC_CF(data, cf, "query conn[%" FMT_OFF_T "]: "
"MAX_CONCURRENT -> %d (%u in use)",
cf->conn->connection_id, *pres1, CONN_ATTACHED(cf->conn));
cf->conn->connection_id, *pres1, cf->conn->attached_xfers);
return CURLE_OK;
}
case CF_QUERY_CONNECT_REPLY_MS: