From: Stefan Eissing Date: Thu, 4 Dec 2025 16:15:33 +0000 (+0100) Subject: connection: attached transfer count X-Git-Tag: rc-8_18_0-1~23 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=d7928029fc0a31df03473d5ca9e13b732357febc;p=thirdparty%2Fcurl.git 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 --- diff --git a/lib/conncache.c b/lib/conncache.c index c50697f177..18b760afe0 100644 --- a/lib/conncache.c +++ b/lib/conncache.c @@ -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; } diff --git a/lib/http2.c b/lib/http2.c index df38f82caf..deed6ece7e 100644 --- a/lib/http2.c +++ b/lib/http2.c @@ -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; diff --git a/lib/multi.c b/lib/multi.c index 3086d61c74..f87ee012d8 100644 --- a/lib/multi.c +++ b/lib/multi.c @@ -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; diff --git a/lib/uint-spbset.c b/lib/uint-spbset.c index 84a893e85f..e27f66fc7b 100644 --- a/lib/uint-spbset.c +++ b/lib/uint-spbset.c @@ -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; diff --git a/lib/uint-spbset.h b/lib/uint-spbset.h index 8c46ce4aa8..0220d05443 100644 --- a/lib/uint-spbset.h +++ b/lib/uint-spbset.h @@ -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. */ diff --git a/lib/url.c b/lib/url.c index 64ad16a1ae..194810553e 100644 --- a/lib/url.c +++ b/lib/url.c @@ -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; } diff --git a/lib/urldata.h b/lib/urldata.h index 76aefec01e..899dfff0f1 100644 --- a/lib/urldata.h +++ b/lib/urldata.h @@ -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 diff --git a/lib/vquic/curl_ngtcp2.c b/lib/vquic/curl_ngtcp2.c index cf7b95bf6f..690ef252a0 100644 --- a/lib/vquic/curl_ngtcp2.c +++ b/lib/vquic/curl_ngtcp2.c @@ -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; } diff --git a/lib/vquic/curl_osslq.c b/lib/vquic/curl_osslq.c index 79bd1180dc..73cea21a15 100644 --- a/lib/vquic/curl_osslq.c +++ b/lib/vquic/curl_osslq.c @@ -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; diff --git a/lib/vquic/curl_quiche.c b/lib/vquic/curl_quiche.c index a35ffbec65..7082d55916 100644 --- a/lib/vquic/curl_quiche.c +++ b/lib/vquic/curl_quiche.c @@ -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: