]> git.ipfire.org Git - thirdparty/curl.git/commitdiff
connection: attached transfer count
authorStefan Eissing <stefan@eissing.org>
Thu, 4 Dec 2025 16:15:33 +0000 (17:15 +0100)
committerDaniel Stenberg <daniel@haxx.se>
Thu, 4 Dec 2025 17:45:38 +0000 (18:45 +0100)
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

lib/conncache.c
lib/http2.c
lib/multi.c
lib/uint-spbset.c
lib/uint-spbset.h
lib/url.c
lib/urldata.h
lib/vquic/curl_ngtcp2.c
lib/vquic/curl_osslq.c
lib/vquic/curl_quiche.c

index c50697f177b69dfffd1e8a5316c980838712b5c3..18b760afe052f7ee235ff00ce4c367994ea4d634 100644 (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;
   }
 
index df38f82cafa02b360dcb64422c97d27a8cd326fe..deed6ece7ee172b1066a0706ee7be4915134f685 100644 (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;
index 3086d61c7489ce77bad0bbb5d02a46f90fea6a81..f87ee012d86b89766de538904df88b0bc3c5977a 100644 (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;
index 84a893e85f5abc076207545a9fc9289b9cc307ba..e27f66fc7b1553108a2d952182484a38d2fcb83c 100644 (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;
index 8c46ce4aa8ea55a21d04133f38f1f336c78bbaee..0220d054437854aff26c770ab0c711ab5072572e 100644 (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. */
index 64ad16a1ae819980def959098aa26739ba0fe336..194810553e184cd708b69f2845f0d90ed413add5 100644 (file)
--- 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;
     }
index 76aefec01ec7b5b6039c34fa25ddca0ddefa0b83..899dfff0f10bf41ac6c136889fded21005e2d31b 100644 (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
index cf7b95bf6f94bddd5df583b5bf216848ab82963c..690ef252a0379e62002f6034f95f31e9ec73e56d 100644 (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;
   }
index 79bd1180dcf6df0e5f14e6bb2a1b71c415cd334c..73cea21a15dc004ae7c5f3a6c2bfa6c7e813e2b7 100644 (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;
index a35ffbec65e871ac3f42ed8d25cc4aee1a3100ab..7082d55916b8bd6ef3b4b50879059437c5e07ac3 100644 (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: