]> git.ipfire.org Git - thirdparty/curl.git/commitdiff
multi: add new information extraction method
authorStefan Eissing <stefan@eissing.org>
Wed, 23 Jul 2025 07:18:59 +0000 (09:18 +0200)
committerDaniel Stenberg <daniel@haxx.se>
Mon, 4 Aug 2025 21:48:57 +0000 (23:48 +0200)
Adds `curl_off_t curl_multi_get_offt(CURLM *multi_handle, CURLMinfo_offt
info)` to the multi interface with enums:

* CURLMINFO_XFERS_CURRENT: current number of transfers
* CURLMINFO_XFERS_RUNNING: number of running transfers
* CURLMINFO_XFERS_PENDING: number of pending transfers
* CURLMINFO_XFERS_DONE: number of finished transfers to read
* CURLMINFO_XFERS_ADDED: total number of transfers added, ever

Add documentation for functions and info enums.

Add use in the curl command line tool to replace two static
variables counting the same "from the outside".

refs #17870
Closes #17992

14 files changed:
.github/scripts/spellcheck.curl
docs/libcurl/Makefile.inc
docs/libcurl/curl_multi_get_offt.md [new file with mode: 0644]
docs/libcurl/symbols-in-versions
include/curl/multi.h
lib/libcurl.def
lib/multi.c
lib/multihandle.h
lib/uint-bset.c
scripts/singleuse.pl
src/tool_operate.c
src/tool_progress.c
src/tool_progress.h
tests/data/test1135

index 4de9d865966f109fc769f5839fd74b56531455e5..c24edf2b3bc88ae40e7d526ac181de064e94cb56 100644 (file)
@@ -132,6 +132,7 @@ curl_multi_timeout
 curl_multi_setopt
 curl_multi_assign
 curl_multi_get_handles
+curl_multi_get_offt
 curl_pushheader_bynum
 curl_pushheader_byname
 curl_multi_waitfds
index 83357cf42b34e6a41b5f8ab1d0f7e9052674e787..9142f65fabd8f16073723b8a51b18d44fc00b06a 100644 (file)
@@ -75,6 +75,7 @@ man_MANS = \
  curl_multi_cleanup.3 \
  curl_multi_fdset.3 \
  curl_multi_get_handles.3 \
+ curl_multi_get_offt.3 \
  curl_multi_info_read.3 \
  curl_multi_init.3 \
  curl_multi_perform.3 \
diff --git a/docs/libcurl/curl_multi_get_offt.md b/docs/libcurl/curl_multi_get_offt.md
new file mode 100644 (file)
index 0000000..cc7873d
--- /dev/null
@@ -0,0 +1,102 @@
+---
+c: Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
+SPDX-License-Identifier: curl
+Title: curl_multi_get_offt
+Section: 3
+Source: libcurl
+See-also:
+  - curl_multi_add_handle (3)
+  - curl_multi_remove_handle (3)
+Protocol:
+  - All
+Added-in: 8.16.0
+---
+
+# NAME
+
+curl_multi_get_offt - extract information from a multi handle
+
+# SYNOPSIS
+
+~~~c
+#include <curl/curl.h>
+
+CURLMcode curl_multi_get_offt(CURLM *multi_handle,
+                              CURLMinfo_offt info,
+                              curl_off_t *pvalue);
+~~~
+
+# DESCRIPTION
+
+Get the *info* kept in the *multi* handle for `CURLMI_OFFT_*`.
+If the *info* is not applicable, this function returns CURLM_UNKNOWN_OPTION.
+
+# OPTIONS
+
+The following information can be extracted:
+
+## CURLMINFO_XFERS_CURRENT
+
+The number of easy handles currently added to the multi. This does not
+count handles removed. It does count internal handles that get
+added for tasks (like resolving via DoH, for example).
+
+For the total number of easy handles ever added to the multi, see
+*CURLMINFO_XFERS_ADDED*.
+
+## CURLMINFO_XFERS_RUNNING
+
+The number of easy handles currently running, e.g. where the transfer
+has started but not finished yet.
+
+## CURLMINFO_XFERS_PENDING
+
+The number of current easy handles waiting to start. An added transfer
+might become pending for various reasons: a connection limit forces it
+to wait, resolving DNS is not finished or it is not clear if an existing,
+matching connection may allow multiplexing (HTTP/2 or HTTP/3).
+
+## CURLMINFO_XFERS_DONE
+
+The number of easy handles currently finished, but not yet processed
+via curl_multi_info_read(3).
+
+## CURLMINFO_XFERS_ADDED
+
+The cumulative number of all easy handles added to the multi, ever.
+This includes internal handles added for tasks (like resolving
+via DoH, for example).
+
+For the current number of easy handles managed by the multi, use
+*CURLMINFO_XFERS_CURRENT*.
+
+# %PROTOCOLS%
+
+# EXAMPLE
+
+~~~c
+int main(void)
+{
+  /* init a multi stack */
+  CURLM *multi = curl_multi_init();
+  CURL *curl = curl_easy_init();
+  curl_off_t n;
+
+  if(curl) {
+    /* add the transfer */
+    curl_multi_add_handle(multi, curl);
+
+    curl_multi_get_offt(multi, CURLMINFO_XFERS_ADDED, &n);
+    /* on successful add, n is 1 */
+  }
+}
+~~~
+
+# %AVAILABILITY%
+
+# RETURN VALUE
+
+This function returns a CURLMcode indicating success or error.
+
+CURLM_OK (0) means everything was OK, non-zero means an error occurred,
+see libcurl-errors(3).
index 6387ea3014ac9171f4e60fca837c88e304d29750..d2a4cc598e5bdce6eca0403fec80a92998817818 100644 (file)
@@ -553,6 +553,12 @@ CURLM_RECURSIVE_API_CALL        7.59.0
 CURLM_UNKNOWN_OPTION            7.15.4
 CURLM_UNRECOVERABLE_POLL        7.84.0
 CURLM_WAKEUP_FAILURE            7.68.0
+CURLMINFO_NONE                  8.16.0
+CURLMINFO_XFERS_CURRENT         8.16.0
+CURLMINFO_XFERS_RUNNING         8.16.0
+CURLMINFO_XFERS_PENDING         8.16.0
+CURLMINFO_XFERS_DONE            8.16.0
+CURLMINFO_XFERS_ADDED           8.16.0
 CURLMIMEOPT_FORMESCAPE          7.81.0
 CURLMOPT_CHUNK_LENGTH_PENALTY_SIZE 7.30.0
 CURLMOPT_CONTENT_LENGTH_PENALTY_SIZE 7.30.0
index 0fbea88707d717da1430abb9cdea2b0d3a8e8b25..e336c94a88377452bff31055b19bbbd4eace9708 100644 (file)
@@ -448,6 +448,36 @@ CURL_EXTERN CURLMcode curl_multi_assign(CURLM *multi_handle,
  */
 CURL_EXTERN CURL **curl_multi_get_handles(CURLM *multi_handle);
 
+
+typedef enum {
+  CURLMINFO_NONE, /* first, never use this */
+  /* The number of easy handles currently managed by the multi handle,
+   * e.g. have been added but not yet removed. */
+  CURLMINFO_XFERS_CURRENT = 1,
+  /* The number of easy handles running, e.g. not done and not queueing. */
+  CURLMINFO_XFERS_RUNNING = 2,
+  /* The number of easy handles waiting to start, e.g. for a connection
+   * to become available due to limits on parallelism, max connections
+   * or other factors. */
+  CURLMINFO_XFERS_PENDING = 3,
+  /* The number of easy handles finished, waiting for their results to
+   * be read via `curl_multi_info_read()`. */
+  CURLMINFO_XFERS_DONE = 4,
+  /* The total number of easy handles added to the multi handle, ever. */
+  CURLMINFO_XFERS_ADDED = 5
+} CURLMinfo_offt;
+
+/*
+ * Name:    curl_multi_get_offt()
+ *
+ * Desc:    Retrieves a numeric value for the `CURLMINFO_*` enums.
+ *
+ * Returns: CULRM_OK or error when value could not be obtained.
+ */
+CURL_EXTERN CURLMcode curl_multi_get_offt(CURLM *multi_handle,
+                                          CURLMinfo_offt info,
+                                          curl_off_t *pvalue);
+
 /*
  * Name: curl_push_callback
  *
index 43e26f655c6f123495717849aace2b012cbdd5aa..ae64776dc270266fc7580bf3cc5c66f0a1c20bce 100644 (file)
@@ -54,6 +54,7 @@ curl_multi_assign
 curl_multi_cleanup
 curl_multi_fdset
 curl_multi_get_handles
+curl_multi_get_offt
 curl_multi_info_read
 curl_multi_init
 curl_multi_perform
index b5a6970eee1d0fc7f6ccda9bca4c69f6f8434817..9a653cf9591a86806a226e1d902bf8026b8bc9be 100644 (file)
@@ -479,6 +479,7 @@ CURLMcode curl_multi_add_handle(CURLM *m, CURL *d)
   /* add the easy handle to the process set */
   Curl_uint_bset_add(&multi->process, data->mid);
   ++multi->xfers_alive;
+  ++multi->xfers_total_ever;
 
   Curl_cpool_xfer_init(data);
   multi_warn_debug(multi, data);
@@ -3741,6 +3742,43 @@ CURL **curl_multi_get_handles(CURLM *m)
   return a;
 }
 
+CURLMcode curl_multi_get_offt(CURLM *m,
+                              CURLMinfo_offt info,
+                              curl_off_t *pvalue)
+{
+  struct Curl_multi *multi = m;
+
+  if(!GOOD_MULTI_HANDLE(multi))
+    return CURLM_BAD_HANDLE;
+  if(!pvalue)
+    return CURLM_BAD_FUNCTION_ARGUMENT;
+
+  switch(info) {
+  case CURLMINFO_XFERS_CURRENT: {
+    unsigned int n = Curl_uint_tbl_count(&multi->xfers);
+    if(n && multi->admin)
+      --n;
+    *pvalue = (curl_off_t)n;
+    return CURLM_OK;
+  }
+  case CURLMINFO_XFERS_RUNNING:
+    *pvalue = (curl_off_t)Curl_uint_bset_count(&multi->process);
+    return CURLM_OK;
+  case CURLMINFO_XFERS_PENDING:
+    *pvalue = (curl_off_t)Curl_uint_bset_count(&multi->pending);
+    return CURLM_OK;
+  case CURLMINFO_XFERS_DONE:
+    *pvalue = (curl_off_t)Curl_uint_bset_count(&multi->msgsent);
+    return CURLM_OK;
+  case CURLMINFO_XFERS_ADDED:
+    *pvalue = multi->xfers_total_ever;
+    return CURLM_OK;
+  default:
+    *pvalue = -1;
+    return CURLM_UNKNOWN_OPTION;
+  }
+}
+
 CURLcode Curl_multi_xfer_buf_borrow(struct Curl_easy *data,
                                     char **pbuf, size_t *pbuflen)
 {
index cdedfb08ab0663d85f98046b41d243c98eb10fc7..ae41044adc2ed4102220eea45d5daaf6ab4a1505 100644 (file)
@@ -89,6 +89,7 @@ struct Curl_multi {
 
   unsigned int xfers_alive; /* amount of added transfers that have
                                not yet reached COMPLETE state */
+  curl_off_t xfers_total_ever; /* total of added transfers, ever. */
   struct uint_tbl xfers; /* transfers added to this multi */
   /* Each transfer's mid may be present in at most one of these */
   struct uint_bset process; /* transfer being processed */
index e612c390a6ba1d96a9b957361e66b93876450102..1c60f22adff241ca37ffe32bfc08d3ee0a2f18ee 100644 (file)
@@ -79,8 +79,9 @@ UNITTEST unsigned int Curl_uint_bset_capacity(struct uint_bset *bset)
 {
   return bset->nslots * 64;
 }
+#endif
 
-UNITTEST unsigned int Curl_uint_bset_count(struct uint_bset *bset)
+unsigned int Curl_uint_bset_count(struct uint_bset *bset)
 {
   unsigned int i;
   unsigned int n = 0;
@@ -90,7 +91,6 @@ UNITTEST unsigned int Curl_uint_bset_count(struct uint_bset *bset)
   }
   return n;
 }
-#endif
 
 bool Curl_uint_bset_empty(struct uint_bset *bset)
 {
index 61564960198ba90cc7e759ebe8d4cc8a0d002954..b47c85c99589e7175467f41fd156ba5a6cbd9767 100755 (executable)
@@ -111,6 +111,7 @@ my %api = (
     'curl_multi_cleanup' => 'API',
     'curl_multi_fdset' => 'API',
     'curl_multi_get_handles' => 'API',
+    'curl_multi_get_offt' => 'API',
     'curl_multi_info_read' => 'API',
     'curl_multi_init' => 'API',
     'curl_multi_perform' => 'API',
index 0c605b80242faf3f51ec9f4ad7c8608e46af548d..11abc1f9d72000db804ad45e92c03fb098c543f0 100644 (file)
@@ -207,7 +207,6 @@ static curl_off_t VmsSpecialSize(const char *name,
 
 struct per_transfer *transfers; /* first node */
 static struct per_transfer *transfersl; /* last node */
-static curl_off_t all_pers;
 
 /* add_per_transfer creates a new 'per_transfer' node in the linked
    list of transfers */
@@ -229,8 +228,6 @@ static CURLcode add_per_transfer(struct per_transfer **per)
     transfersl = p;
   }
   *per = p;
-  all_xfers++; /* count total number of transfers added */
-  all_pers++;
 
   return CURLE_OK;
 }
@@ -259,7 +256,6 @@ static struct per_transfer *del_per_transfer(struct per_transfer *per)
     transfersl = p;
 
   free(per);
-  all_pers--;
 
   return n;
 }
@@ -1420,9 +1416,17 @@ static CURLcode add_parallel_transfers(struct GlobalConfig *global,
   CURLMcode mcode;
   bool sleeping = FALSE;
   char *errorbuf;
+  curl_off_t nxfers;
+
   *addedp = FALSE;
   *morep = FALSE;
-  if(all_pers < (global->parallel_max*2)) {
+  mcode = curl_multi_get_offt(multi, CURLMINFO_XFERS_CURRENT, &nxfers);
+  if(mcode) {
+    DEBUGASSERT(0);
+    return CURLE_UNKNOWN_OPTION;
+  }
+
+  if(nxfers < (curl_off_t)(global->parallel_max*2)) {
     bool skipped = FALSE;
     do {
       result = create_transfer(global, share, addedp, &skipped);
@@ -1762,7 +1766,7 @@ static CURLcode check_finished(struct parastate *s)
   CURLMsg *msg;
   bool checkmore = FALSE;
   struct GlobalConfig *global = s->global;
-  progress_meter(global, &s->start, FALSE);
+  progress_meter(global, s->multi, &s->start, FALSE);
   do {
     msg = curl_multi_info_read(s->multi, &rc);
     if(msg) {
@@ -1887,7 +1891,7 @@ static CURLcode parallel_transfers(struct GlobalConfig *global,
         result = check_finished(s);
     }
 
-    (void)progress_meter(global, &s->start, TRUE);
+    (void)progress_meter(global, s->multi, &s->start, TRUE);
   }
 
   /* Make sure to return some kind of error if there was a multi problem */
index 635f8e25ee0215d6bcfbe6d7aaa6e1c2ef8c1589..a477dbac26434fec789457dac9fc5ad64a4b29f3 100644 (file)
@@ -134,8 +134,6 @@ static curl_off_t all_ultotal = 0;
 static curl_off_t all_dlalready = 0;
 static curl_off_t all_ulalready = 0;
 
-curl_off_t all_xfers = 0;   /* current total */
-
 struct speedcount {
   curl_off_t dl;
   curl_off_t ul;
@@ -151,6 +149,7 @@ static struct speedcount speedstore[SPEEDCNT];
   |  6 --   9.9G     0     2     2   0:00:40  0:00:02  0:00:37 4087M
 */
 bool progress_meter(struct GlobalConfig *global,
+                    CURLM *multi,
                     struct curltime *start,
                     bool final)
 {
@@ -182,9 +181,10 @@ bool progress_meter(struct GlobalConfig *global,
     struct per_transfer *per;
     curl_off_t all_dlnow = 0;
     curl_off_t all_ulnow = 0;
+    curl_off_t xfers_added = 0;
+    curl_off_t xfers_running = 0;
     bool dlknown = TRUE;
     bool ulknown = TRUE;
-    curl_off_t all_running = 0; /* in progress */
     curl_off_t speed = 0;
     unsigned int i;
     stamp = now;
@@ -210,8 +210,6 @@ bool progress_meter(struct GlobalConfig *global,
         all_ultotal += per->ultotal;
         per->ultotal_added = TRUE;
       }
-      if(per->added)
-        all_running++;
     }
     if(dlknown && all_dltotal)
       msnprintf(dlpercen, sizeof(dlpercen), "%3" CURL_FORMAT_CURL_OFF_T,
@@ -274,6 +272,8 @@ bool progress_meter(struct GlobalConfig *global,
     }
     time2str(time_spent, spent);
 
+    (void)curl_multi_get_offt(multi, CURLMINFO_XFERS_ADDED, &xfers_added);
+    (void)curl_multi_get_offt(multi, CURLMINFO_XFERS_RUNNING, &xfers_running);
     fprintf(tool_stderr,
             "\r"
             "%-3s " /* percent downloaded */
@@ -292,8 +292,8 @@ bool progress_meter(struct GlobalConfig *global,
             ulpercen,  /* 3 letters */
             max5data(all_dlnow, buffer[0]),
             max5data(all_ulnow, buffer[1]),
-            all_xfers,
-            all_running,
+            xfers_added,
+            xfers_running,
             time_total,
             time_spent,
             time_left,
index 02c34d42eee81a34380b4831ef4f5030b0f1652a..d9009504a141901f3e6cfd1dbed83510e336c511 100644 (file)
@@ -32,10 +32,9 @@ int xferinfo_cb(void *clientp,
                 curl_off_t ulnow);
 
 bool progress_meter(struct GlobalConfig *global,
+                    CURLM *multi,
                     struct curltime *start,
                     bool final);
 void progress_finalize(struct per_transfer *per);
 
-extern curl_off_t all_xfers;   /* total number */
-
 #endif /* HEADER_CURL_TOOL_PROGRESS_H */
index 070d8dff4caabc44bd0e74da16973d25da6b3545..73668d9994925749a391b3c5e0b0db7d0322796a 100644 (file)
@@ -109,6 +109,7 @@ curl_multi_timeout
 curl_multi_setopt
 curl_multi_assign
 curl_multi_get_handles
+curl_multi_get_offt
 curl_pushheader_bynum
 curl_pushheader_byname
 curl_multi_waitfds