]> git.ipfire.org Git - thirdparty/curl.git/commitdiff
lib: keep timestamp in easy handle
authorStefan Eissing <stefan@eissing.org>
Mon, 15 Dec 2025 13:28:09 +0000 (14:28 +0100)
committerDaniel Stenberg <daniel@haxx.se>
Tue, 16 Dec 2025 07:48:44 +0000 (08:48 +0100)
Use `data->progress.now` as the timestamp of proecssing a transfer.
Update it on significant events and refrain from calling `curlx_now()`
in many places.

The problem this addresses is
a) calling curlx_now() has costs, depending on platform. Calling it
   every time results in 25% increase `./runtest` duration on macOS.
b) we used to pass a `struct curltime *` around to save on calls, but
   when some method directly use `curx_now()` and some use the passed
   pointer, the transfer experienes non-linear time. This results in
   timeline checks to report events in the wrong order.

By keeping a timestamp in the easy handle and updating it there, no
longer invoking `curlx_now()` in the "lower" methods, the transfer
can observer a steady clock progression.

Add documentation in docs/internals/TIME-KEEPING.md

Reported-by: Viktor Szakats
Fixes #19935
Closes #19961

67 files changed:
docs/Makefile.am
docs/internals/TIME-KEEPING.md [new file with mode: 0644]
lib/asyn-ares.c
lib/asyn-thrdd.c
lib/cf-h1-proxy.c
lib/cf-h2-proxy.c
lib/cf-https-connect.c
lib/cf-ip-happy.c
lib/cf-socket.c
lib/cfilters.c
lib/conncache.c
lib/conncache.h
lib/connect.c
lib/connect.h
lib/cshutdn.c
lib/curl_trc.c
lib/doh.c
lib/easy.c
lib/file.c
lib/ftp.c
lib/gopher.c
lib/hostip.c
lib/http.c
lib/http2.c
lib/imap.c
lib/mqtt.c
lib/multi.c
lib/multi_ev.c
lib/multi_ev.h
lib/multiif.h
lib/pingpong.c
lib/pingpong.h
lib/pop3.c
lib/progress.c
lib/progress.h
lib/ratelimit.c
lib/ratelimit.h
lib/request.c
lib/sendf.c
lib/setopt.c
lib/smb.c
lib/smtp.c
lib/socks.c
lib/telnet.c
lib/tftp.c
lib/transfer.c
lib/transfer.h
lib/url.c
lib/url.h
lib/urldata.h
lib/vquic/curl_ngtcp2.c
lib/vquic/curl_osslq.c
lib/vquic/curl_quiche.c
lib/vquic/vquic.c
lib/vquic/vquic_int.h
lib/vssh/libssh.c
lib/vssh/libssh2.c
lib/vtls/gtls.c
lib/vtls/openssl.c
lib/vtls/schannel.c
lib/vtls/vtls.c
lib/vtls/wolfssl.c
lib/ws.c
tests/data/test2500
tests/unit/unit1303.c
tests/unit/unit1399.c
tests/unit/unit3216.c

index 0b619b6008836150ac21ffd0d06a29369772dd57..c0c25232b8268f94ef423f53f1e3e9584a60f410 100644 (file)
@@ -68,6 +68,7 @@ INTERNALDOCS =                                  \
  internals/SCORECARD.md                         \
  internals/SPLAY.md                             \
  internals/STRPARSE.md                          \
+ internals/TIME-KEEPING.md                      \
  internals/TLS-SESSIONS.md                      \
  internals/UINT_SETS.md                         \
  internals/WEBSOCKET.md
diff --git a/docs/internals/TIME-KEEPING.md b/docs/internals/TIME-KEEPING.md
new file mode 100644 (file)
index 0000000..fc0020d
--- /dev/null
@@ -0,0 +1,82 @@
+<!--
+Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
+
+SPDX-License-Identifier: curl
+-->
+
+# Keeping Time
+
+Transfers need the current time to handle timeouts and keep a record of events. The current
+time function is `curlx_now()` and it uses a **monotonic** clock on most platforms. This
+ensures that time only ever increases (the timestamps it gives are however not the "real"
+world clock).
+
+The simplest handling of transfer time would be to just always call `curlx_now()`. However
+there is a performance penalty to that - varying by platform - so this is not a desirable
+strategy. Processing thousands of transfers in a loop needs a smarter approach.
+
+## Initial Approach (now historic)
+
+The loop processing functions called `curlx_now()` at the beginning and then passed
+a pointer to the `struct curltime now` to functions to save them the calls. Passing
+this pointer down to all functions possibly involved was not done as this pollutes
+the internal APIs.
+
+So, some functions continued to call `curlx_now()` on their own while others used the
+passed pointer *to a timestamp in the past*. This led to a transfer experiencing *jumps*
+in time, reversing cause and effect. On fast systems, this was mostly not noticeable. On
+slow machines or in CI, this led to rare and annoying test failures.
+
+(Especially when we added assertions that the reported "timeline" of a transfer was
+in the correct order: *queue -> nameloopup -> connect -> appconnect ->...*.)
+
+## Revised Approach
+
+The strategy of handling transfer's time is now:
+
+* Keep a "now" timestamp in `data->progress.now`.
+* Perform time checks and event recording using `data->progress.now`.
+* Set `data->progress.now` at the start of API calls (e.g. `curl_multi_perform()`, etc.).
+* Set `data->progress.now` when recorded events happen (for precision).
+* Set `data->progress.now` on multi state changes.
+* Set `data->progress.now` in `pingpong` timeout handling, since `pingpong` is old and not always non-blocking.
+
+In addition to *setting* `data->progress.now` this timestamp can be *advanced* using 2 new methods:
+
+* `Curl_pgrs_now_at_least(data, &now)`: code that has a "now" timestamp can progress the `data`'s own "now" to be at least as new. If `data->progress.now` is already newer, no change is done. A transfer never goes **back**.
+* `Curl_pgrs_now_update(data1, data2)`: update the "now" in `data1` to be at least as new as the one in `data2`. If it already is newer, nothing changes.
+
+### Time Advancing Loops
+
+This advancing is used in the following way in loop like `curl_multi_perform()`:
+
+```C
+struct curltime now = curlx_now(); /* start of API call */
+forall data in transfers {
+  Curl_pgrs_set_at_least(data, now);
+  progress(data);   /* may update "now" */
+  now = data->progress.now;
+}
+```
+
+Transfers that update their "now" pass that timestamp to the next transfer processed.
+
+### Transfers triggering other transfers
+
+In HTTP/2 and HTTP/3 processing, incoming data causes actions on transfers other than
+the calling one. The protocols may receive data for any transfer on the connection and need
+to dispatch it:
+
+* a Close/Reset comes in for another transfer. That transfer is marked as "dirty", making sure it is processed in a timely manner.
+* Response Data arrives: this data is written out to the client. Before this is done, the "now" timestamp is updated via `Curl_pgrs_now_update(data, calling)` from the "calling" transfer.
+
+## Blocking Operations
+
+We still have places in `libcurl` where we do blocking operations. We should always use `Curl_pgrs_now_set(data)` afterwards since we cannot be sure how much time has passed. Since loop processing passed an updated "now" to the next transfer, a delay due to blocking is passed on.
+
+There are other places where we may lose track of time:
+
+* Cache/Pool Locks: no "now" updates happen after a lock has been acquired. These locks should not be kept for a longer time.
+* User Callbacks: no "now" updates happen after callbacks have been invoked. The expectation is that those do not take long.
+
+Should these assumptions prove wrong, we need to add updates.
index 40ce00db2229fbf9dc3794a23478e8d7fad2a88c..81b7090f2255f0f1fa1fec03b54c7b65e8e1bb62 100644 (file)
@@ -311,7 +311,7 @@ CURLcode Curl_async_is_resolved(struct Curl_easy *data,
      /* This is only set to non-zero if the timer was started. */
      (ares->happy_eyeballs_dns_time.tv_sec ||
       ares->happy_eyeballs_dns_time.tv_usec) &&
-     (curlx_timediff_ms(curlx_now(), ares->happy_eyeballs_dns_time) >=
+     (curlx_timediff_ms(data->progress.now, ares->happy_eyeballs_dns_time) >=
       HAPPY_EYEBALLS_DNS_TIMEOUT)) {
     /* Remember that the EXPIRE_HAPPY_EYEBALLS_DNS timer is no longer
        running. */
@@ -390,12 +390,11 @@ CURLcode Curl_async_await(struct Curl_easy *data,
   struct async_ares_ctx *ares = &data->state.async.ares;
   CURLcode result = CURLE_OK;
   timediff_t timeout_ms;
-  struct curltime now = curlx_now();
 
   DEBUGASSERT(entry);
   *entry = NULL; /* clear on entry */
 
-  timeout_ms = Curl_timeleft_ms(data, &now, TRUE);
+  timeout_ms = Curl_timeleft_ms(data, TRUE);
   if(timeout_ms < 0) {
     /* already expired! */
     connclose(data->conn, "Timed out before name resolve started");
@@ -439,15 +438,15 @@ CURLcode Curl_async_await(struct Curl_easy *data,
     if(Curl_pgrsUpdate(data))
       result = CURLE_ABORTED_BY_CALLBACK;
     else {
-      struct curltime now2 = curlx_now();
-      timediff_t elapsed_ms = curlx_timediff_ms(now2, now); /* spent time */
+      struct curltime now = curlx_now(); /* update in loop */
+      timediff_t elapsed_ms = curlx_timediff_ms(now, data->progress.now);
       if(elapsed_ms <= 0)
         timeout_ms -= 1; /* always deduct at least 1 */
       else if(elapsed_ms > timeout_ms)
         timeout_ms = -1;
       else
         timeout_ms -= elapsed_ms;
-      now = now2; /* for next loop */
+      Curl_pgrs_now_at_least(data, &now);
     }
     if(timeout_ms < 0)
       result = CURLE_OPERATION_TIMEDOUT;
@@ -583,7 +582,7 @@ static void async_ares_hostbyname_cb(void *user_data,
        timeout to prevent it. After all, we do not even know where in the
        c-ares retry cycle each request is.
     */
-    ares->happy_eyeballs_dns_time = curlx_now();
+    ares->happy_eyeballs_dns_time = data->progress.now;
     Curl_expire(data, HAPPY_EYEBALLS_DNS_TIMEOUT, EXPIRE_HAPPY_EYEBALLS_DNS);
   }
 }
index aa686271a0dd5b2acc34038bc6fafbf2192c5287..408f05844dab4fe863809e732980c8423e0584d3 100644 (file)
@@ -442,7 +442,7 @@ static bool async_thrdd_init(struct Curl_easy *data,
 
   /* passing addr_ctx to the thread adds a reference */
   addr_ctx->ref_count = 2;
-  addr_ctx->start = curlx_now();
+  addr_ctx->start = data->progress.now;
 
 #ifdef HAVE_GETADDRINFO
   addr_ctx->thread_hnd = Curl_thread_create(getaddrinfo_thread, addr_ctx);
@@ -655,7 +655,7 @@ CURLcode Curl_async_is_resolved(struct Curl_easy *data,
   else {
     /* poll for name lookup done with exponential backoff up to 250ms */
     /* should be fine even if this converts to 32-bit */
-    timediff_t elapsed = curlx_timediff_ms(curlx_now(),
+    timediff_t elapsed = curlx_timediff_ms(data->progress.now,
                                            data->progress.t_startsingle);
     if(elapsed < 0)
       elapsed = 0;
@@ -706,7 +706,7 @@ CURLcode Curl_async_pollset(struct Curl_easy *data, struct easy_pollset *ps)
     result = Curl_pollset_add_in(data, ps, thrdd->addr->sock_pair[0]);
 #else
     timediff_t milli;
-    timediff_t ms = curlx_timediff_ms(curlx_now(), thrdd->addr->start);
+    timediff_t ms = curlx_timediff_ms(data->progress.now, thrdd->addr->start);
     if(ms < 3)
       milli = 0;
     else if(ms <= 50)
index 19c63a12b04902902abc9caba7970ae7a0ba17c4..5d290366b2da7160ca70032141d14f33b32ddb82 100644 (file)
@@ -528,7 +528,7 @@ static CURLcode H1_CONNECT(struct Curl_cfilter *cf,
 
   do {
 
-    if(Curl_timeleft_ms(data, NULL, TRUE) < 0) {
+    if(Curl_timeleft_ms(data, TRUE) < 0) {
       failf(data, "Proxy CONNECT aborted due to timeout");
       result = CURLE_OPERATION_TIMEDOUT;
       goto out;
index 5a58c280e9f4e05ef136e5efd8efb31040a32f1e..37af4e19d75b12e7806bd1de4b43bad59fb80e70 100644 (file)
@@ -1088,7 +1088,7 @@ static CURLcode cf_h2_proxy_connect(struct Curl_cfilter *cf,
   }
   DEBUGASSERT(ts->authority);
 
-  if(Curl_timeleft_ms(data, NULL, TRUE) < 0) {
+  if(Curl_timeleft_ms(data, TRUE) < 0) {
     failf(data, "Proxy CONNECT aborted due to timeout");
     result = CURLE_OPERATION_TIMEDOUT;
     goto out;
index 47555fed94f291e956106483fa2d3f9828850d58..104df248ebd5cf98ce7923fb81c11a09066345b0 100644 (file)
@@ -149,7 +149,7 @@ static void cf_hc_baller_init(struct cf_hc_baller *b,
   struct Curl_cfilter *save = cf->next;
 
   cf->next = NULL;
-  b->started = curlx_now();
+  b->started = data->progress.now;
   switch(b->alpn_id) {
   case ALPN_h3:
     transport = TRNSPRT_QUIC;
@@ -212,11 +212,11 @@ static CURLcode baller_connected(struct Curl_cfilter *cf,
   if(reply_ms >= 0)
     CURL_TRC_CF(data, cf, "connect+handshake %s: %dms, 1st data: %dms",
                 winner->name,
-                (int)curlx_timediff_ms(curlx_now(),
+                (int)curlx_timediff_ms(data->progress.now,
                                        winner->started), reply_ms);
   else
     CURL_TRC_CF(data, cf, "deferred handshake %s: %dms",
-                winner->name, (int)curlx_timediff_ms(curlx_now(),
+                winner->name, (int)curlx_timediff_ms(data->progress.now,
                                                      winner->started));
 
   /* install the winning filter below this one. */
@@ -293,7 +293,6 @@ static CURLcode cf_hc_connect(struct Curl_cfilter *cf,
                               bool *done)
 {
   struct cf_hc_ctx *ctx = cf->ctx;
-  struct curltime now;
   CURLcode result = CURLE_OK;
   size_t i, failed_ballers;
 
@@ -303,14 +302,13 @@ static CURLcode cf_hc_connect(struct Curl_cfilter *cf,
   }
 
   *done = FALSE;
-  now = curlx_now();
   switch(ctx->state) {
   case CF_HC_INIT:
     DEBUGASSERT(!cf->next);
     for(i = 0; i < ctx->baller_count; i++)
       DEBUGASSERT(!ctx->ballers[i].cf);
     CURL_TRC_CF(data, cf, "connect, init");
-    ctx->started = now;
+    ctx->started = data->progress.now;
     cf_hc_baller_init(&ctx->ballers[0], cf, data, ctx->ballers[0].transport);
     if(ctx->baller_count > 1) {
       Curl_expire(data, ctx->soft_eyeballs_timeout_ms, EXPIRE_ALPN_EYEBALLS);
@@ -329,7 +327,7 @@ static CURLcode cf_hc_connect(struct Curl_cfilter *cf,
       }
     }
 
-    if(time_to_start_next(cf, data, 1, now)) {
+    if(time_to_start_next(cf, data, 1, data->progress.now)) {
       cf_hc_baller_init(&ctx->ballers[1], cf, data, ctx->ballers[1].transport);
     }
 
index d31c6b6caaa65c21d5dba640a2c412ed1fcceedf..029746354ccb2c6272fe71cfc5d77e17ca3061d7 100644 (file)
@@ -353,7 +353,6 @@ static CURLcode cf_ip_ballers_run(struct cf_ip_ballers *bs,
   CURLcode result = CURLE_OK;
   struct cf_ip_attempt *a = NULL, **panchor;
   bool do_more;
-  struct curltime now;
   timediff_t next_expire_ms;
   int i, inconclusive, ongoing;
 
@@ -361,7 +360,6 @@ static CURLcode cf_ip_ballers_run(struct cf_ip_ballers *bs,
     return CURLE_OK;
 
 evaluate:
-  now = curlx_now();
   ongoing = inconclusive = 0;
 
   /* check if a running baller connects now */
@@ -398,7 +396,7 @@ evaluate:
   /* no attempt connected yet, start another one? */
   if(!ongoing) {
     if(!bs->started.tv_sec && !bs->started.tv_usec)
-      bs->started = now;
+      bs->started = data->progress.now;
     do_more = TRUE;
   }
   else {
@@ -408,8 +406,8 @@ evaluate:
       more_possible = cf_ai_iter_has_more(&bs->ipv6_iter);
 #endif
     do_more = more_possible &&
-              (curlx_timediff_ms(now, bs->last_attempt_started) >=
-               bs->attempt_delay_ms);
+      (curlx_timediff_ms(data->progress.now, bs->last_attempt_started) >=
+       bs->attempt_delay_ms);
     if(do_more)
       CURL_TRC_CF(data, cf, "happy eyeballs timeout expired, "
                   "start next attempt");
@@ -447,7 +445,7 @@ evaluate:
       while(*panchor)
         panchor = &((*panchor)->next);
       *panchor = a;
-      bs->last_attempt_started = now;
+      bs->last_attempt_started = data->progress.now;
       bs->last_attempt_ai_family = ai_family;
       /* and run everything again */
       goto evaluate;
@@ -455,7 +453,8 @@ evaluate:
     else if(inconclusive) {
       /* tried all addresses, no success but some where inconclusive.
        * Let's restart the inconclusive ones. */
-      timediff_t since_ms = curlx_timediff_ms(now, bs->last_attempt_started);
+      timediff_t since_ms =
+        curlx_timediff_ms(data->progress.now, bs->last_attempt_started);
       timediff_t delay_ms = bs->attempt_delay_ms - since_ms;
       if(delay_ms <= 0) {
         CURL_TRC_CF(data, cf, "all attempts inconclusive, restarting one");
@@ -468,7 +467,7 @@ evaluate:
           CURL_TRC_CF(data, cf, "restarted baller %d -> %d", i, result);
           if(result) /* serious failure */
             goto out;
-          bs->last_attempt_started = now;
+          bs->last_attempt_started = data->progress.now;
           goto evaluate;
         }
         DEBUGASSERT(0); /* should not come here */
@@ -500,10 +499,10 @@ out:
     bool more_possible;
 
     /* when do we need to be called again? */
-    next_expire_ms = Curl_timeleft_ms(data, &now, TRUE);
+    next_expire_ms = Curl_timeleft_ms(data, TRUE);
     if(next_expire_ms <= 0) {
       failf(data, "Connection timeout after %" FMT_OFF_T " ms",
-            curlx_timediff_ms(now, data->progress.t_startsingle));
+        curlx_timediff_ms(data->progress.now, data->progress.t_startsingle));
       return CURLE_OPERATION_TIMEDOUT;
     }
 
@@ -514,7 +513,8 @@ out:
 #endif
     if(more_possible) {
       timediff_t expire_ms, elapsed_ms;
-      elapsed_ms = curlx_timediff_ms(now, bs->last_attempt_started);
+      elapsed_ms =
+        curlx_timediff_ms(data->progress.now, bs->last_attempt_started);
       expire_ms = CURLMAX(bs->attempt_delay_ms - elapsed_ms, 0);
       next_expire_ms = CURLMIN(next_expire_ms, expire_ms);
       if(next_expire_ms <= 0) {
@@ -675,7 +675,7 @@ static CURLcode is_connected(struct Curl_cfilter *cf,
           proxy_name ? "via " : "",
           proxy_name ? proxy_name : "",
           proxy_name ? " " : "",
-          curlx_timediff_ms(curlx_now(), data->progress.t_startsingle),
+          curlx_timediff_ms(data->progress.now, data->progress.t_startsingle),
           curl_easy_strerror(result));
   }
 
@@ -700,14 +700,14 @@ static CURLcode start_connect(struct Curl_cfilter *cf,
   if(!dns)
     return CURLE_FAILED_INIT;
 
-  if(Curl_timeleft_ms(data, NULL, TRUE) < 0) {
+  if(Curl_timeleft_ms(data, TRUE) < 0) {
     /* a precaution, no need to continue if time already is up */
     failf(data, "Connection time-out");
     return CURLE_OPERATION_TIMEDOUT;
   }
 
   CURL_TRC_CF(data, cf, "init ip ballers for transport %u", ctx->transport);
-  ctx->started = curlx_now();
+  ctx->started = data->progress.now;
   return cf_ip_ballers_init(&ctx->ballers, cf->conn->ip_version,
                             dns->addr, ctx->cf_create, ctx->transport,
                             data->set.happy_eyeballs_timeout);
index 469e9229fd69c8feeaf423008a827ad929018e23..f9a883b0c3a6a9c61b74be33d718131731377551 100644 (file)
@@ -1074,7 +1074,7 @@ static CURLcode cf_socket_open(struct Curl_cfilter *cf,
 
   (void)data;
   DEBUGASSERT(ctx->sock == CURL_SOCKET_BAD);
-  ctx->started_at = curlx_now();
+  ctx->started_at = data->progress.now;
 #ifdef SOCK_NONBLOCK
   /* Do not tuck SOCK_NONBLOCK into socktype when opensocket callback is set
    * because we would not know how socketype is about to be used in the
@@ -1201,7 +1201,7 @@ out:
   }
   else if(isconnected) {
     set_local_ip(cf, data);
-    ctx->connected_at = curlx_now();
+    ctx->connected_at = data->progress.now;
     cf->connected = TRUE;
   }
   CURL_TRC_CF(data, cf, "cf_socket_open() -> %d, fd=%" FMT_SOCKET_T,
@@ -1320,7 +1320,7 @@ static CURLcode cf_tcp_connect(struct Curl_cfilter *cf,
   else if(rc == CURL_CSELECT_OUT || cf->conn->bits.tcp_fastopen) {
     if(verifyconnect(ctx->sock, &ctx->error)) {
       /* we are connected with TCP, awesome! */
-      ctx->connected_at = curlx_now();
+      ctx->connected_at = data->progress.now;
       set_local_ip(cf, data);
       *done = TRUE;
       cf->connected = TRUE;
@@ -1396,13 +1396,13 @@ static CURLcode cf_socket_adjust_pollset(struct Curl_cfilter *cf,
 #define SIO_IDEAL_SEND_BACKLOG_QUERY 0x4004747B
 #endif
 
-static void win_update_sndbuf_size(struct cf_socket_ctx *ctx)
+static void win_update_sndbuf_size(struct Curl_easy *data,
+                                   struct cf_socket_ctx *ctx)
 {
   ULONG ideal;
   DWORD ideallen;
-  struct curltime n = curlx_now();
 
-  if(curlx_timediff_ms(n, ctx->last_sndbuf_query_at) > 1000) {
+  if(curlx_timediff_ms(data->progress.now, ctx->last_sndbuf_query_at) > 1000) {
     if(!WSAIoctl(ctx->sock, SIO_IDEAL_SEND_BACKLOG_QUERY, 0, 0,
                  &ideal, sizeof(ideal), &ideallen, 0, 0) &&
        ideal != ctx->sndbuf_size &&
@@ -1410,7 +1410,7 @@ static void win_update_sndbuf_size(struct cf_socket_ctx *ctx)
                    (const char *)&ideal, sizeof(ideal))) {
       ctx->sndbuf_size = ideal;
     }
-    ctx->last_sndbuf_query_at = n;
+    ctx->last_sndbuf_query_at = data->progress.now;
   }
 }
 
@@ -1491,7 +1491,7 @@ static CURLcode cf_socket_send(struct Curl_cfilter *cf, struct Curl_easy *data,
 
 #ifdef USE_WINSOCK
   if(!result)
-    win_update_sndbuf_size(ctx);
+    win_update_sndbuf_size(data, ctx);
 #endif
 
   CURL_TRC_CF(data, cf, "send(len=%zu) -> %d, %zu",
@@ -1557,7 +1557,7 @@ static CURLcode cf_socket_recv(struct Curl_cfilter *cf, struct Curl_easy *data,
 
   CURL_TRC_CF(data, cf, "recv(len=%zu) -> %d, %zu", len, result, *pnread);
   if(!result && !ctx->got_first_byte) {
-    ctx->first_byte_at = curlx_now();
+    ctx->first_byte_at = data->progress.now;
     ctx->got_first_byte = TRUE;
   }
   return result;
@@ -1990,23 +1990,21 @@ static timediff_t cf_tcp_accept_timeleft(struct Curl_cfilter *cf,
   struct cf_socket_ctx *ctx = cf->ctx;
   timediff_t timeout_ms = DEFAULT_ACCEPT_TIMEOUT;
   timediff_t other_ms;
-  struct curltime now;
 
 #ifndef CURL_DISABLE_FTP
   if(data->set.accepttimeout > 0)
     timeout_ms = data->set.accepttimeout;
 #endif
 
-  now = curlx_now();
   /* check if the generic timeout possibly is set shorter */
-  other_ms = Curl_timeleft_ms(data, &now, FALSE);
+  other_ms = Curl_timeleft_ms(data, FALSE);
   if(other_ms && (other_ms < timeout_ms))
     /* note that this also works fine for when other_ms happens to be negative
        due to it already having elapsed */
     timeout_ms = other_ms;
   else {
     /* subtract elapsed time */
-    timeout_ms -= curlx_timediff_ms(now, ctx->started_at);
+    timeout_ms -= curlx_timediff_ms(data->progress.now, ctx->started_at);
     if(!timeout_ms)
       /* avoid returning 0 as that means no timeout! */
       timeout_ms = -1;
@@ -2142,7 +2140,7 @@ static CURLcode cf_tcp_accept_connect(struct Curl_cfilter *cf,
   cf_tcp_set_accepted_remote_ip(cf, data);
   set_local_ip(cf, data);
   ctx->active = TRUE;
-  ctx->connected_at = curlx_now();
+  ctx->connected_at = data->progress.now;
   cf->connected = TRUE;
   CURL_TRC_CF(data, cf, "accepted_set(sock=%" FMT_SOCKET_T
               ", remote=%s port=%d)",
@@ -2208,7 +2206,7 @@ CURLcode Curl_conn_tcp_listen_set(struct Curl_easy *data,
     goto out;
   Curl_conn_cf_add(data, conn, sockindex, cf);
 
-  ctx->started_at = curlx_now();
+  ctx->started_at = data->progress.now;
   conn->sock[sockindex] = ctx->sock;
   set_local_ip(cf, data);
   CURL_TRC_CF(data, cf, "set filter for listen socket fd=%" FMT_SOCKET_T
index acffae3f00357cf75b8177c2583c5b9c9f83514a..0c259e4439f4eef0c8665075860d930b22d1f59c 100644 (file)
@@ -169,7 +169,6 @@ CURLcode Curl_conn_shutdown(struct Curl_easy *data, int sockindex, bool *done)
   struct Curl_cfilter *cf;
   CURLcode result = CURLE_OK;
   timediff_t timeout_ms;
-  struct curltime now;
 
   DEBUGASSERT(data->conn);
 
@@ -187,14 +186,13 @@ CURLcode Curl_conn_shutdown(struct Curl_easy *data, int sockindex, bool *done)
   }
 
   *done = FALSE;
-  now = curlx_now();
   if(!Curl_shutdown_started(data, sockindex)) {
     CURL_TRC_M(data, "shutdown start on%s connection",
                sockindex ? " secondary" : "");
-    Curl_shutdown_start(data, sockindex, 0, &now);
+    Curl_shutdown_start(data, sockindex, 0);
   }
   else {
-    timeout_ms = Curl_shutdown_timeleft(data->conn, sockindex, &now);
+    timeout_ms = Curl_shutdown_timeleft(data, data->conn, sockindex);
     if(timeout_ms < 0) {
       /* info message, since this might be regarded as acceptable */
       infof(data, "shutdown timeout");
@@ -504,7 +502,7 @@ CURLcode Curl_conn_connect(struct Curl_easy *data,
        * socket and ip related information. */
       cf_cntrl_update_info(data, data->conn);
       conn_report_connect_stats(cf, data);
-      data->conn->keepalive = curlx_now();
+      data->conn->keepalive = data->progress.now;
 #ifndef CURL_DISABLE_VERBOSE_STRINGS
       result = cf_verboseconnect(data, cf);
 #endif
@@ -520,7 +518,7 @@ CURLcode Curl_conn_connect(struct Curl_easy *data,
       goto out;
     else {
       /* check allowed time left */
-      const timediff_t timeout_ms = Curl_timeleft_ms(data, NULL, TRUE);
+      const timediff_t timeout_ms = Curl_timeleft_ms(data, TRUE);
       curl_socket_t sockfd = Curl_conn_cf_get_socket(cf, data);
       int rc;
 
index 54248d14ad6edf2a881714e6237c008897c37a9a..458d85d11024f3a8e5635ad01b951a6b63577311 100644 (file)
@@ -208,6 +208,7 @@ void Curl_cpool_destroy(struct cpool *cpool)
                cpool->share ? "[SHARE] " : "", cpool->num_conn);
     /* Move all connections to the shutdown list */
     sigpipe_init(&pipe_st);
+    Curl_pgrs_now_set(cpool->idata);
     CPOOL_LOCK(cpool, cpool->idata);
     conn = cpool_get_first(cpool);
     while(conn) {
@@ -275,23 +276,22 @@ static struct cpool_bundle *cpool_add_bundle(struct cpool *cpool,
 }
 
 static struct connectdata *
-cpool_bundle_get_oldest_idle(struct cpool_bundle *bundle)
+cpool_bundle_get_oldest_idle(struct cpool_bundle *bundle,
+                             struct curltime *pnow)
 {
   struct Curl_llist_node *curr;
   timediff_t highscore = -1;
   timediff_t score;
-  struct curltime now;
   struct connectdata *oldest_idle = NULL;
   struct connectdata *conn;
 
-  now = curlx_now();
   curr = Curl_llist_head(&bundle->conns);
   while(curr) {
     conn = Curl_node_elem(curr);
 
     if(!CONN_INUSE(conn)) {
       /* Set higher score for the age passed since the connection was used */
-      score = curlx_timediff_ms(now, conn->lastused);
+      score = curlx_timediff_ms(*pnow, conn->lastused);
 
       if(score > highscore) {
         highscore = score;
@@ -303,18 +303,17 @@ cpool_bundle_get_oldest_idle(struct cpool_bundle *bundle)
   return oldest_idle;
 }
 
-static struct connectdata *cpool_get_oldest_idle(struct cpool *cpool)
+static struct connectdata *cpool_get_oldest_idle(struct cpool *cpool,
+                                                 struct curltime *pnow)
 {
   struct Curl_hash_iterator iter;
   struct Curl_llist_node *curr;
   struct Curl_hash_element *he;
   struct connectdata *oldest_idle = NULL;
   struct cpool_bundle *bundle;
-  struct curltime now;
   timediff_t highscore = -1;
   timediff_t score;
 
-  now = curlx_now();
   Curl_hash_start_iterate(&cpool->dest2bundle, &iter);
 
   for(he = Curl_hash_next_element(&iter); he;
@@ -328,7 +327,7 @@ static struct connectdata *cpool_get_oldest_idle(struct cpool *cpool)
       if(CONN_INUSE(conn) || conn->bits.close || conn->connect_only)
         continue;
       /* Set higher score for the age passed since the connection was used */
-      score = curlx_timediff_ms(now, conn->lastused);
+      score = curlx_timediff_ms(*pnow, conn->lastused);
       if(score > highscore) {
         highscore = score;
         oldest_idle = conn;
@@ -359,6 +358,7 @@ int Curl_cpool_check_limits(struct Curl_easy *data,
   if(!dest_limit && !total_limit)
     return CPOOL_LIMIT_OK;
 
+  Curl_pgrs_now_update(cpool->idata, data);
   CPOOL_LOCK(cpool, cpool->idata);
   if(dest_limit) {
     size_t live;
@@ -378,7 +378,8 @@ int Curl_cpool_check_limits(struct Curl_easy *data,
         struct connectdata *oldest_idle = NULL;
         /* The bundle is full. Extract the oldest connection that may
          * be removed now, if there is one. */
-        oldest_idle = cpool_bundle_get_oldest_idle(bundle);
+        oldest_idle = cpool_bundle_get_oldest_idle(bundle,
+                                                   &data->progress.now);
         if(!oldest_idle)
           break;
         /* disconnect the old conn and continue */
@@ -409,7 +410,8 @@ int Curl_cpool_check_limits(struct Curl_easy *data,
           break;
       }
       else {
-        struct connectdata *oldest_idle = cpool_get_oldest_idle(cpool);
+        struct connectdata *oldest_idle =
+          cpool_get_oldest_idle(cpool, &data->progress.now);
         if(!oldest_idle)
           break;
         /* disconnect the old conn and continue */
@@ -429,6 +431,7 @@ int Curl_cpool_check_limits(struct Curl_easy *data,
 
 out:
   CPOOL_UNLOCK(cpool, cpool->idata);
+  Curl_pgrs_now_update(data, cpool->idata);
   return result;
 }
 
@@ -538,7 +541,7 @@ bool Curl_cpool_conn_now_idle(struct Curl_easy *data,
     maxconnects = data->multi->maxconnects;
   }
 
-  conn->lastused = curlx_now(); /* it was used up until now */
+  conn->lastused = data->progress.now; /* it was used up until now */
   if(cpool && maxconnects) {
     /* may be called form a callback already under lock */
     bool do_lock = !CPOOL_IS_LOCKED(cpool);
@@ -548,7 +551,7 @@ bool Curl_cpool_conn_now_idle(struct Curl_easy *data,
       infof(data, "Connection pool is full, closing the oldest of %zu/%u",
             cpool->num_conn, maxconnects);
 
-      oldest_idle = cpool_get_oldest_idle(cpool);
+      oldest_idle = cpool_get_oldest_idle(cpool, &data->progress.now);
       kept = (oldest_idle != conn);
       if(oldest_idle) {
         Curl_conn_terminate(data, oldest_idle, FALSE);
@@ -635,6 +638,7 @@ static void cpool_discard_conn(struct cpool *cpool,
    * If we do a shutdown for an aborted transfer, the server might think
    * it was successful otherwise (for example an ftps: upload). This is
    * not what we want. */
+  Curl_pgrs_now_update(cpool->idata, data);
   if(aborted)
     done = TRUE;
   if(!done) {
@@ -700,16 +704,12 @@ void Curl_conn_terminate(struct Curl_easy *data,
     CPOOL_UNLOCK(cpool, data);
 }
 
-struct cpool_reaper_ctx {
-  struct curltime now;
-};
-
 static int cpool_reap_dead_cb(struct Curl_easy *data,
                               struct connectdata *conn, void *param)
 {
-  struct cpool_reaper_ctx *rctx = param;
+  (void)param;
   if((!CONN_INUSE(conn) && conn->bits.no_reuse) ||
-     Curl_conn_seems_dead(conn, data, &rctx->now)) {
+     Curl_conn_seems_dead(conn, data)) {
     /* stop the iteration here, pass back the connection that was pruned */
     Curl_conn_terminate(data, conn, FALSE);
     return 1;
@@ -727,20 +727,18 @@ static int cpool_reap_dead_cb(struct Curl_easy *data,
 void Curl_cpool_prune_dead(struct Curl_easy *data)
 {
   struct cpool *cpool = cpool_get_instance(data);
-  struct cpool_reaper_ctx rctx;
   timediff_t elapsed;
 
   if(!cpool)
     return;
 
-  rctx.now = curlx_now();
   CPOOL_LOCK(cpool, data);
-  elapsed = curlx_timediff_ms(rctx.now, cpool->last_cleanup);
+  elapsed = curlx_timediff_ms(data->progress.now, cpool->last_cleanup);
 
   if(elapsed >= 1000L) {
-    while(cpool_foreach(data, cpool, &rctx, cpool_reap_dead_cb))
+    while(cpool_foreach(data, cpool, NULL, cpool_reap_dead_cb))
       ;
-    cpool->last_cleanup = rctx.now;
+    cpool->last_cleanup = data->progress.now;
   }
   CPOOL_UNLOCK(cpool, data);
 }
@@ -754,16 +752,15 @@ static int conn_upkeep(struct Curl_easy *data,
   return 0; /* continue iteration */
 }
 
-CURLcode Curl_cpool_upkeep(void *data)
+CURLcode Curl_cpool_upkeep(struct Curl_easy *data)
 {
   struct cpool *cpool = cpool_get_instance(data);
-  struct curltime now = curlx_now();
 
   if(!cpool)
     return CURLE_OK;
 
   CPOOL_LOCK(cpool, data);
-  cpool_foreach(data, cpool, &now, conn_upkeep);
+  cpool_foreach(data, cpool, &data->progress.now, conn_upkeep);
   CPOOL_UNLOCK(cpool, data);
   return CURLE_OK;
 }
index 5761ee9b446471d855ea74fdaa6a73f0dce925d1..b1a8aeaf099d1a4b8155d4586732254fa50ade49 100644 (file)
@@ -139,7 +139,7 @@ void Curl_cpool_prune_dead(struct Curl_easy *data);
 /**
  * Perform upkeep actions on connections in the transfer's pool.
  */
-CURLcode Curl_cpool_upkeep(void *data);
+CURLcode Curl_cpool_upkeep(struct Curl_easy *data);
 
 typedef void Curl_cpool_conn_do_cb(struct connectdata *conn,
                                    struct Curl_easy *data,
index 748cf53a29aec61842212f0cde076a600c8fedbb..f1700e5b96c3871d1b5555010352a32efa046d97 100644 (file)
@@ -109,18 +109,15 @@ enum alpnid Curl_str2alpnid(const struct Curl_str *cstr)
  * infinite time left). If the value is negative, the timeout time has already
  * elapsed.
  * @param data the transfer to check on
- * @param nowp timestamp to use for calculation, NULL to use curlx_now()
  * @param duringconnect TRUE iff connect timeout is also taken into account.
  * @unittest: 1303
  */
 timediff_t Curl_timeleft_ms(struct Curl_easy *data,
-                            struct curltime *nowp,
                             bool duringconnect)
 {
   timediff_t timeleft_ms = 0;
   timediff_t ctimeleft_ms = 0;
   timediff_t ctimeout_ms;
-  struct curltime now;
 
   /* The duration of a connect and the total transfer are calculated from two
      different time-stamps. It can end up with the total timeout being reached
@@ -130,14 +127,9 @@ timediff_t Curl_timeleft_ms(struct Curl_easy *data,
   if((!data->set.timeout || data->set.connect_only) && !duringconnect)
     return 0; /* no timeout in place or checked, return "no limit" */
 
-  if(!nowp) {
-    now = curlx_now();
-    nowp = &now;
-  }
-
   if(data->set.timeout) {
     timeleft_ms = data->set.timeout -
-      curlx_timediff_ms(*nowp, data->progress.t_startop);
+      curlx_timediff_ms(data->progress.now, data->progress.t_startop);
     if(!timeleft_ms)
       timeleft_ms = -1; /* 0 is "no limit", fake 1 ms expiry */
   }
@@ -147,7 +139,7 @@ timediff_t Curl_timeleft_ms(struct Curl_easy *data,
   ctimeout_ms = (data->set.connecttimeout > 0) ?
     data->set.connecttimeout : DEFAULT_CONNECT_TIMEOUT;
   ctimeleft_ms = ctimeout_ms -
-    curlx_timediff_ms(*nowp, data->progress.t_startsingle);
+    curlx_timediff_ms(data->progress.now, data->progress.t_startsingle);
   if(!ctimeleft_ms)
     ctimeleft_ms = -1; /* 0 is "no limit", fake 1 ms expiry */
   if(!timeleft_ms)
@@ -158,61 +150,47 @@ timediff_t Curl_timeleft_ms(struct Curl_easy *data,
 }
 
 void Curl_shutdown_start(struct Curl_easy *data, int sockindex,
-                         int timeout_ms, struct curltime *nowp)
+                         int timeout_ms)
 {
-  struct curltime now;
   struct connectdata *conn = data->conn;
 
   DEBUGASSERT(conn);
-  if(!nowp) {
-    now = curlx_now();
-    nowp = &now;
-  }
-  conn->shutdown.start[sockindex] = *nowp;
+  conn->shutdown.start[sockindex] = data->progress.now;
   conn->shutdown.timeout_ms = (timeout_ms > 0) ?
     (timediff_t)timeout_ms :
     ((data->set.shutdowntimeout > 0) ?
      data->set.shutdowntimeout : DEFAULT_SHUTDOWN_TIMEOUT_MS);
   /* Set a timer, unless we operate on the admin handle */
   if(data->mid)
-    Curl_expire_ex(data, nowp, conn->shutdown.timeout_ms,
-                   EXPIRE_SHUTDOWN);
+    Curl_expire_ex(data, conn->shutdown.timeout_ms, EXPIRE_SHUTDOWN);
 }
 
-timediff_t Curl_shutdown_timeleft(struct connectdata *conn, int sockindex,
-                                  struct curltime *nowp)
+timediff_t Curl_shutdown_timeleft(struct Curl_easy *data,
+                                  struct connectdata *conn,
+                                  int sockindex)
 {
-  struct curltime now;
   timediff_t left_ms;
 
   if(!conn->shutdown.start[sockindex].tv_sec ||
      (conn->shutdown.timeout_ms <= 0))
     return 0; /* not started or no limits */
 
-  if(!nowp) {
-    now = curlx_now();
-    nowp = &now;
-  }
   left_ms = conn->shutdown.timeout_ms -
-            curlx_timediff_ms(*nowp, conn->shutdown.start[sockindex]);
+            curlx_timediff_ms(data->progress.now,
+                              conn->shutdown.start[sockindex]);
   return left_ms ? left_ms : -1;
 }
 
-timediff_t Curl_conn_shutdown_timeleft(struct connectdata *conn,
-                                       struct curltime *nowp)
+timediff_t Curl_conn_shutdown_timeleft(struct Curl_easy *data,
+                                       struct connectdata *conn)
 {
   timediff_t left_ms = 0, ms;
-  struct curltime now;
   int i;
 
   for(i = 0; conn->shutdown.timeout_ms && (i < 2); ++i) {
     if(!conn->shutdown.start[i].tv_sec)
       continue;
-    if(!nowp) {
-      now = curlx_now();
-      nowp = &now;
-    }
-    ms = Curl_shutdown_timeleft(conn, i, nowp);
+    ms = Curl_shutdown_timeleft(data, conn, i);
     if(ms && (!left_ms || ms < left_ms))
       left_ms = ms;
   }
index 7d1c9c908991518715a6d10280be88a3d3f4df51..09776e636f9c9f1770b90de3fb74973deb2b2ed2 100644 (file)
@@ -39,7 +39,6 @@ enum alpnid Curl_str2alpnid(const struct Curl_str *str);
 /* generic function that returns how much time there is left to run, according
    to the timeouts set */
 timediff_t Curl_timeleft_ms(struct Curl_easy *data,
-                            struct curltime *nowp,
                             bool duringconnect);
 
 #define DEFAULT_CONNECT_TIMEOUT 300000 /* milliseconds == five minutes */
@@ -47,17 +46,18 @@ timediff_t Curl_timeleft_ms(struct Curl_easy *data,
 #define DEFAULT_SHUTDOWN_TIMEOUT_MS   (2 * 1000)
 
 void Curl_shutdown_start(struct Curl_easy *data, int sockindex,
-                         int timeout_ms, struct curltime *nowp);
+                         int timeout_ms);
 
 /* return how much time there is left to shutdown the connection at
  * sockindex. Returns 0 if there is no limit or shutdown has not started. */
-timediff_t Curl_shutdown_timeleft(struct connectdata *conn, int sockindex,
-                                  struct curltime *nowp);
+timediff_t Curl_shutdown_timeleft(struct Curl_easy *data,
+                                  struct connectdata *conn,
+                                  int sockindex);
 
 /* return how much time there is left to shutdown the connection.
  * Returns 0 if there is no limit or shutdown has not started. */
-timediff_t Curl_conn_shutdown_timeleft(struct connectdata *conn,
-                                       struct curltime *nowp);
+timediff_t Curl_conn_shutdown_timeleft(struct Curl_easy *data,
+                                       struct connectdata *conn);
 
 void Curl_shutdown_clear(struct Curl_easy *data, int sockindex);
 
index 26c8f9c0dd41dfebb45c21a387863f0c776a9744..21ab1209ebc44fb361e9a73c3388b2903ef71ad3 100644 (file)
@@ -232,8 +232,6 @@ static void cshutdn_perform(struct cshutdn *cshutdn,
   struct Curl_llist_node *e = Curl_llist_head(&cshutdn->list);
   struct Curl_llist_node *enext;
   struct connectdata *conn;
-  struct curltime *nowp = NULL;
-  struct curltime now;
   timediff_t next_expire_ms = 0, ms;
   bool done;
 
@@ -253,11 +251,7 @@ static void cshutdn_perform(struct cshutdn *cshutdn,
     else {
       /* idata has one timer list, but maybe more than one connection.
        * Set EXPIRE_SHUTDOWN to the smallest time left for all. */
-      if(!nowp) {
-        now = curlx_now();
-        nowp = &now;
-      }
-      ms = Curl_conn_shutdown_timeleft(conn, nowp);
+      ms = Curl_conn_shutdown_timeleft(data, conn);
       if(ms && ms < next_expire_ms)
         next_expire_ms = ms;
     }
@@ -265,14 +259,14 @@ static void cshutdn_perform(struct cshutdn *cshutdn,
   }
 
   if(next_expire_ms)
-    Curl_expire_ex(data, nowp, next_expire_ms, EXPIRE_SHUTDOWN);
+    Curl_expire_ex(data, next_expire_ms, EXPIRE_SHUTDOWN);
 }
 
 static void cshutdn_terminate_all(struct cshutdn *cshutdn,
                                   struct Curl_easy *data,
                                   int timeout_ms)
 {
-  struct curltime started = curlx_now();
+  struct curltime started = data->progress.now;
   struct Curl_llist_node *e;
   SIGPIPE_VARIABLE(pipe_st);
 
@@ -295,7 +289,8 @@ static void cshutdn_terminate_all(struct cshutdn *cshutdn,
     }
 
     /* wait for activity, timeout or "nothing" */
-    spent_ms = curlx_timediff_ms(curlx_now(), started);
+    Curl_pgrs_now_set(data); /* update in loop */
+    spent_ms = curlx_timediff_ms(data->progress.now, started);
     if(spent_ms >= (timediff_t)timeout_ms) {
       CURL_TRC_M(data, "[SHUTDOWN] shutdown finished, %s",
                  (timeout_ms > 0) ? "timeout" : "best effort done");
index 169f7a7d6d4702a4f0736bfa1361c7e7f77f559f..8308f49c5dbb6d0e84aa7c42aa476e0a5fe27e41 100644 (file)
@@ -314,12 +314,11 @@ void Curl_trc_easy_timers(struct Curl_easy *data)
   if(CURL_TRC_TIMER_is_verbose(data)) {
     struct Curl_llist_node *e = Curl_llist_head(&data->state.timeoutlist);
     if(e) {
-      struct curltime now = curlx_now();
       while(e) {
         struct time_node *n = Curl_node_elem(e);
         e = Curl_node_next(e);
         CURL_TRC_TIMER(data, n->eid, "expires in %" FMT_TIMEDIFF_T "ns",
-                       curlx_timediff_us(n->time, now));
+                       curlx_timediff_us(n->time, data->progress.now));
       }
     }
   }
index b0f19be73a7eb463b726f13e65105e3314c4c8b6..49901945062939648cbf1f0a35551247450f44dd 100644 (file)
--- a/lib/doh.c
+++ b/lib/doh.c
@@ -307,7 +307,7 @@ static CURLcode doh_probe_run(struct Curl_easy *data,
     goto error;
   }
 
-  timeout_ms = Curl_timeleft_ms(data, NULL, TRUE);
+  timeout_ms = Curl_timeleft_ms(data, TRUE);
   if(timeout_ms <= 0) {
     result = CURLE_OPERATION_TIMEDOUT;
     goto error;
index 138462af877188793f64504d3f7cde6544fca263..8f11c8f58d07238561355b99e983939a06b64721 100644 (file)
@@ -996,6 +996,7 @@ CURL *curl_easy_duphandle(CURL *d)
   if(dupset(outcurl, data))
     goto fail;
 
+  Curl_pgrs_now_set(outcurl); /* start of API call */
   outcurl->progress.hide     = data->progress.hide;
   outcurl->progress.callback = data->progress.callback;
 
@@ -1155,6 +1156,7 @@ CURLcode curl_easy_pause(CURL *d, int action)
   if(Curl_is_in_callback(data))
     recursive = TRUE;
 
+  Curl_pgrs_now_set(data); /* start of API call */
   recv_paused = Curl_xfer_recv_is_paused(data);
   recv_paused_new = (action & CURLPAUSE_RECV);
   send_paused = Curl_xfer_send_is_paused(data);
@@ -1179,7 +1181,7 @@ CURLcode curl_easy_pause(CURL *d, int action)
       Curl_multi_mark_dirty(data); /* make it run */
       /* On changes, tell application to update its timers. */
       if(changed) {
-        if(Curl_update_timer(data->multi) && !result)
+        if(Curl_update_timer(data->multi, &data->progress.now) && !result)
           result = CURLE_ABORTED_BY_CALLBACK;
       }
     }
index 0f969571265ac03fe2cd3a9360a5c0214ea0f91f..39642c9340c4bb663e4d7e4b98bec555b3f0b4bb 100644 (file)
@@ -315,7 +315,6 @@ static CURLcode file_upload(struct Curl_easy *data,
   CURLcode result = CURLE_OK;
   char *xfer_ulbuf;
   size_t xfer_ulblen;
-  curl_off_t bytecount = 0;
   struct_stat file_stat;
   const char *sendbuf;
   bool eos = FALSE;
@@ -405,9 +404,7 @@ static CURLcode file_upload(struct Curl_easy *data,
       result = CURLE_SEND_ERROR;
       break;
     }
-    bytecount += nwritten;
-
-    Curl_pgrsSetUploadCounter(data, bytecount);
+    Curl_pgrs_upload_inc(data, nwritten);
 
     result = Curl_pgrsCheck(data);
   }
index 4e528259c9143a6e16f97fcada3b9c5c82b085fe..4dedde03aaecef74876c2880d4b45ab2034e041d 100644 (file)
--- a/lib/ftp.c
+++ b/lib/ftp.c
@@ -3178,7 +3178,7 @@ static CURLcode ftp_connect(struct Curl_easy *data,
     conn->bits.ftp_use_control_ssl = TRUE;
   }
 
-  Curl_pp_init(pp); /* once per transfer */
+  Curl_pp_init(pp, &data->progress.now); /* once per transfer */
 
   /* When we connect, we start in the state where we await the 220
      response */
@@ -3314,7 +3314,7 @@ static CURLcode ftp_done(struct Curl_easy *data, CURLcode status,
      * data has been transferred. This happens when doing through NATs etc that
      * abandon old silent connections.
      */
-    pp->response = curlx_now(); /* timeout relative now */
+    pp->response = data->progress.now; /* timeout relative now */
     result = getftpresponse(data, &nread, &ftpcode);
 
     if(!nread && (CURLE_OPERATION_TIMEDOUT == result)) {
@@ -3434,7 +3434,7 @@ static CURLcode ftp_sendquote(struct Curl_easy *data,
 
       result = Curl_pp_sendf(data, &ftpc->pp, "%s", cmd);
       if(!result) {
-        pp->response = curlx_now(); /* timeout relative now */
+        pp->response = data->progress.now; /* timeout relative now */
         result = getftpresponse(data, &nread, &ftpcode);
       }
       if(result)
index 93e9287c99b99928d658a7bba7475dc14cb43848..f7a61ae08cc45ddbcc568973f87926abbbdbfae2 100644 (file)
@@ -195,7 +195,7 @@ static CURLcode gopher_do(struct Curl_easy *data, bool *done)
     else
       break;
 
-    timeout_ms = Curl_timeleft_ms(data, NULL, FALSE);
+    timeout_ms = Curl_timeleft_ms(data, FALSE);
     if(timeout_ms < 0) {
       result = CURLE_OPERATION_TIMEDOUT;
       break;
index 4f4122d6ae1f8195cefa5d1c0b71ea168d8c7f11..eb59193d2c412f672cb7b0bf8cc53181a66173ce 100644 (file)
@@ -253,7 +253,6 @@ static void dnscache_unlock(struct Curl_easy *data,
 void Curl_dnscache_prune(struct Curl_easy *data)
 {
   struct Curl_dnscache *dnscache = dnscache_get(data);
-  struct curltime now;
   /* the timeout may be set -1 (forever) */
   timediff_t timeout_ms = data->set.dns_cache_timeout_ms;
 
@@ -263,11 +262,10 @@ void Curl_dnscache_prune(struct Curl_easy *data)
 
   dnscache_lock(data, dnscache);
 
-  now = curlx_now();
-
   do {
     /* Remove outdated and unused entries from the hostcache */
-    timediff_t oldest_ms = dnscache_prune(&dnscache->entries, timeout_ms, now);
+    timediff_t oldest_ms =
+      dnscache_prune(&dnscache->entries, timeout_ms, data->progress.now);
 
     if(Curl_hash_count(&dnscache->entries) > MAX_DNS_CACHE_SIZE)
       /* prune the ones over half this age */
@@ -333,7 +331,7 @@ static struct Curl_dns_entry *fetch_addr(struct Curl_easy *data,
     /* See whether the returned entry is stale. Done before we release lock */
     struct dnscache_prune_data user;
 
-    user.now = curlx_now();
+    user.now = data->progress.now;
     user.max_age_ms = data->set.dns_cache_timeout_ms;
     user.oldest_ms = 0;
 
@@ -522,7 +520,7 @@ Curl_dnscache_mk_entry(struct Curl_easy *data,
     dns->timestamp.tv_usec = 0; /* an entry that never goes stale */
   }
   else {
-    dns->timestamp = curlx_now();
+    dns->timestamp = data->progress.now;
   }
   dns->hostport = port;
   if(hostlen)
@@ -1138,7 +1136,7 @@ clean_up:
      the time we spent until now! */
   if(prev_alarm) {
     /* there was an alarm() set before us, now put it back */
-    timediff_t elapsed_secs = curlx_timediff_ms(curlx_now(),
+    timediff_t elapsed_secs = curlx_timediff_ms(data->progress.now,
                                                 data->conn->created) / 1000;
 
     /* the alarm period is counted in even number of seconds */
index c80deb319eab2301423442fb3bab985bfe8546d7..d65cba95dbcfa7936e83e8d6bbf2dccd6ef67838 100644 (file)
@@ -4949,7 +4949,7 @@ static CURLcode cr_exp100_read(struct Curl_easy *data,
     DEBUGF(infof(data, "cr_exp100_read, start AWAITING_CONTINUE, "
            "timeout %dms", data->set.expect_100_timeout));
     ctx->state = EXP100_AWAITING_CONTINUE;
-    ctx->start = curlx_now();
+    ctx->start = data->progress.now;
     Curl_expire(data, data->set.expect_100_timeout, EXPIRE_100_TIMEOUT);
     *nread = 0;
     *eos = FALSE;
@@ -4960,7 +4960,7 @@ static CURLcode cr_exp100_read(struct Curl_easy *data,
     *eos = FALSE;
     return CURLE_READ_ERROR;
   case EXP100_AWAITING_CONTINUE:
-    ms = curlx_timediff_ms(curlx_now(), ctx->start);
+    ms = curlx_timediff_ms(data->progress.now, ctx->start);
     if(ms < data->set.expect_100_timeout) {
       DEBUGF(infof(data, "cr_exp100_read, AWAITING_CONTINUE, not expired"));
       *nread = 0;
index 83b9280b2a155b46089fcf85aa58f00076e814ae..d02b5db322e7bf0e341fe4ba29de57ae756ca1e8 100644 (file)
@@ -37,6 +37,7 @@
 #include "select.h"
 #include "curlx/base64.h"
 #include "multiif.h"
+#include "progress.h"
 #include "url.h"
 #include "urlapi-int.h"
 #include "cfilters.h"
@@ -305,7 +306,8 @@ static void h2_stream_hash_free(unsigned int id, void *stream)
 static int32_t cf_h2_get_desired_local_win(struct Curl_cfilter *cf,
                                            struct Curl_easy *data)
 {
-  curl_off_t avail = Curl_rlimit_avail(&data->progress.dl.rlimit, curlx_now());
+  curl_off_t avail =
+    Curl_rlimit_avail(&data->progress.dl.rlimit, &data->progress.now);
 
   (void)cf;
   if(avail < CURL_OFF_T_MAX) { /* limit in place */
@@ -1422,7 +1424,7 @@ static int on_data_chunk_recv(nghttp2_session *session, uint8_t flags,
   struct Curl_cfilter *cf = userp;
   struct cf_h2_ctx *ctx = cf->ctx;
   struct h2_stream_ctx *stream;
-  struct Curl_easy *data_s;
+  struct Curl_easy *data_s, *calling = CF_DATA_CURRENT(cf);
   (void)flags;
 
   DEBUGASSERT(stream_id); /* should never be a zero stream ID here */
@@ -1443,6 +1445,8 @@ static int on_data_chunk_recv(nghttp2_session *session, uint8_t flags,
   stream = H2_STREAM_CTX(ctx, data_s);
   if(!stream)
     return NGHTTP2_ERR_CALLBACK_FAILURE;
+  if(calling)
+    Curl_pgrs_now_update(data_s, calling);
 
   h2_xfer_write_resp(cf, data_s, stream, (const char *)mem, len, FALSE);
 
index 05d195ec58f498935987b31489a27fb22be1d964..5bb2474b33e7a91027b7d6ffb106e47b80a27c46 100644 (file)
@@ -1995,7 +1995,7 @@ static CURLcode imap_setup_connection(struct Curl_easy *data,
   Curl_sasl_init(&imapc->sasl, data, &saslimap);
 
   curlx_dyn_init(&imapc->dyn, DYN_IMAP_CMD);
-  Curl_pp_init(pp);
+  Curl_pp_init(pp, &data->progress.now);
 
   if(Curl_conn_meta_set(conn, CURL_META_IMAP_CONN, imapc, imap_conn_dtor))
     return CURLE_OUT_OF_MEMORY;
index df5f37775f4fb664e956fcda5bd9d01745935053..bd6d25331d1b07ea3315ca6c24938934bb0db832 100644 (file)
@@ -186,7 +186,7 @@ static CURLcode mqtt_send(struct Curl_easy *data,
   result = Curl_xfer_send(data, buf, len, FALSE, &n);
   if(result)
     return result;
-  mq->lastTime = curlx_now();
+  mq->lastTime = data->progress.now;
   Curl_debug(data, CURLINFO_HEADER_OUT, buf, n);
   if(len != n) {
     size_t nsend = len - n;
@@ -770,7 +770,7 @@ MQTT_SUBACK_COMING:
     }
 
     /* we received something */
-    mq->lastTime = curlx_now();
+    mq->lastTime = data->progress.now;
 
     /* if QoS is set, message contains packet id */
     result = Curl_client_write(data, CLIENTWRITE_BODY, buffer, nread);
@@ -800,7 +800,7 @@ static CURLcode mqtt_do(struct Curl_easy *data, bool *done)
 
   if(!mq)
     return CURLE_FAILED_INIT;
-  mq->lastTime = curlx_now();
+  mq->lastTime = data->progress.now;
   mq->pingsent = FALSE;
 
   result = mqtt_connect(data);
@@ -839,7 +839,7 @@ static CURLcode mqtt_ping(struct Curl_easy *data)
   if(mqtt->state == MQTT_FIRST &&
      !mq->pingsent &&
      data->set.upkeep_interval_ms > 0) {
-    struct curltime t = curlx_now();
+    struct curltime t = data->progress.now;
     timediff_t diff = curlx_timediff_ms(t, mq->lastTime);
 
     if(diff > data->set.upkeep_interval_ms) {
@@ -898,7 +898,7 @@ static CURLcode mqtt_doing(struct Curl_easy *data, bool *done)
     Curl_debug(data, CURLINFO_HEADER_IN, (const char *)&mq->firstbyte, 1);
 
     /* we received something */
-    mq->lastTime = curlx_now();
+    mq->lastTime = data->progress.now;
 
     /* remember the first byte */
     mq->npacket = 0;
index 760f03186d677bfac2bd498399370336115d6176..5aa39e1053ba760f0da37fbdc9a82e94cf98635a 100644 (file)
@@ -99,6 +99,7 @@ static CURLMcode add_next_timeout(struct curltime now,
                                   struct Curl_multi *multi,
                                   struct Curl_easy *d);
 static void multi_timeout(struct Curl_multi *multi,
+                          struct curltime *pnow,
                           struct curltime *expire_time,
                           long *timeout_ms);
 static void process_pending_handles(struct Curl_multi *multi);
@@ -165,6 +166,8 @@ static void mstate(struct Curl_easy *data, CURLMstate state
   CURL_TRC_M(data, "-> [%s]", CURL_MSTATE_NAME(state));
 #endif
 
+  /* really switching state */
+  Curl_pgrs_now_set(data);
   data->mstate = state;
   switch(state) {
   case MSTATE_DONE:
@@ -414,6 +417,7 @@ CURLMcode curl_multi_add_handle(CURLM *m, CURL *d)
   CURLMcode rc;
   struct Curl_multi *multi = m;
   struct Curl_easy *data = d;
+
   /* First, make some basic checks that the CURLM handle is a good handle */
   if(!GOOD_MULTI_HANDLE(multi))
     return CURLM_BAD_HANDLE;
@@ -445,6 +449,7 @@ CURLMcode curl_multi_add_handle(CURLM *m, CURL *d)
     Curl_uint32_bset_clear(&multi->msgsent);
   }
 
+  Curl_pgrs_now_set(data); /* start of API call */
   if(data->multi_easy) {
     /* if this easy handle was previously used for curl_easy_perform(), there
        is a private multi handle here that we can kill */
@@ -498,7 +503,7 @@ CURLMcode curl_multi_add_handle(CURLM *m, CURL *d)
 
   /* Necessary in event based processing, where dirty handles trigger
    * a timeout callback invocation. */
-  rc = Curl_update_timer(multi);
+  rc = Curl_update_timer(multi, &data->progress.now);
   if(rc) {
     data->multi = NULL; /* not anymore */
     Curl_uint32_tbl_remove(&multi->xfers, data->mid);
@@ -779,6 +784,7 @@ CURLMcode curl_multi_remove_handle(CURLM *m, CURL *d)
   if(multi->in_callback)
     return CURLM_RECURSIVE_API_CALL;
 
+  Curl_pgrs_now_set(data); /* start of API call */
   premature = (data->mstate < MSTATE_COMPLETED);
 
   /* If the 'state' is not INIT or COMPLETED, we might need to do something
@@ -877,7 +883,7 @@ CURLMcode curl_multi_remove_handle(CURLM *m, CURL *d)
   process_pending_handles(multi);
 
   if(removed_timer) {
-    rc = Curl_update_timer(multi);
+    rc = Curl_update_timer(multi, &data->progress.now);
     if(rc)
       return rc;
   }
@@ -945,11 +951,11 @@ static CURLcode multi_adjust_pollset(struct Curl_easy *data,
   CURLcode result = CURLE_OK;
 
   if(ps->n) {
-    struct curltime now = curlx_now();
-    bool send_blocked, recv_blocked;
+    bool send_blocked =
+      (Curl_rlimit_avail(&data->progress.dl.rlimit, &data->progress.now) <= 0);
+    bool recv_blocked =
+      (Curl_rlimit_avail(&data->progress.ul.rlimit, &data->progress.now) <= 0);
 
-    recv_blocked = (Curl_rlimit_avail(&data->progress.dl.rlimit, now) <= 0);
-    send_blocked = (Curl_rlimit_avail(&data->progress.ul.rlimit, now) <= 0);
     if(send_blocked || recv_blocked) {
       int i;
       for(i = 0; i <= SECONDARYSOCKET; ++i) {
@@ -1220,6 +1226,7 @@ CURLMcode curl_multi_fdset(CURLM *m,
   struct Curl_multi *multi = m;
   struct easy_pollset ps;
   unsigned int i, mid;
+  struct curltime now = curlx_now(); /* start of API call */
   (void)exc_fd_set;
 
   if(!GOOD_MULTI_HANDLE(multi))
@@ -1238,6 +1245,7 @@ CURLMcode curl_multi_fdset(CURLM *m,
         continue;
       }
 
+      Curl_pgrs_now_at_least(data, &now);
       Curl_multi_pollset(data, &ps);
       for(i = 0; i < ps.n; i++) {
         if(!FDSET_SOCK(ps.sockets[i]))
@@ -1279,6 +1287,7 @@ CURLMcode curl_multi_waitfds(CURLM *m,
   struct Curl_multi *multi = m;
   struct easy_pollset ps;
   unsigned int need = 0, mid;
+  struct curltime now = curlx_now(); /* start of API call */
 
   if(!ufds && (size || !fd_count))
     return CURLM_BAD_FUNCTION_ARGUMENT;
@@ -1300,6 +1309,7 @@ CURLMcode curl_multi_waitfds(CURLM *m,
         Curl_uint32_bset_remove(&multi->dirty, mid);
         continue;
       }
+      Curl_pgrs_now_at_least(data, &now);
       Curl_multi_pollset(data, &ps);
       need += Curl_waitfds_add_ps(&cwfds, &ps);
     } while(Curl_uint32_bset_next(&multi->process, mid, &mid));
@@ -1353,6 +1363,7 @@ static CURLMcode multi_wait(struct Curl_multi *multi,
   unsigned int curl_nfds = 0; /* how many pfds are for curl transfers */
   struct Curl_easy *data = NULL;
   CURLMcode result = CURLM_OK;
+  struct curltime now = curlx_now(); /* start of API call */
   uint32_t mid;
 
 #ifdef USE_WINSOCK
@@ -1385,11 +1396,13 @@ static CURLMcode multi_wait(struct Curl_multi *multi,
         Curl_uint32_bset_remove(&multi->dirty, mid);
         continue;
       }
+      Curl_pgrs_now_at_least(data, &now);
       Curl_multi_pollset(data, &ps);
       if(Curl_pollfds_add_ps(&cpfds, &ps)) {
         result = CURLM_OUT_OF_MEMORY;
         goto out;
       }
+      now = data->progress.now;
     } while(Curl_uint32_bset_next(&multi->process, mid, &mid));
   }
 
@@ -1450,7 +1463,7 @@ static CURLMcode multi_wait(struct Curl_multi *multi,
    * poll. Collecting the sockets may install new timers by protocols
    * and connection filters.
    * Use the shorter one of the internal and the caller requested timeout. */
-  multi_timeout(multi, &expire_time, &timeout_internal);
+  multi_timeout(multi, &now, &expire_time, &timeout_internal);
   if((timeout_internal >= 0) && (timeout_internal < (long)timeout_ms))
     timeout_ms = (int)timeout_internal;
 
@@ -1571,7 +1584,7 @@ static CURLMcode multi_wait(struct Curl_multi *multi,
     long sleep_ms = 0;
 
     /* Avoid busy-looping when there is nothing particular to wait for */
-    multi_timeout(multi, &expire_time, &sleep_ms);
+    multi_timeout(multi, &now, &expire_time, &sleep_ms);
     if(sleep_ms) {
       if(sleep_ms > timeout_ms)
         sleep_ms = timeout_ms;
@@ -1764,12 +1777,12 @@ static CURLcode multi_do_more(struct Curl_easy *data, int *complete)
  * Check whether a timeout occurred, and handle it if it did
  */
 static bool multi_handle_timeout(struct Curl_easy *data,
-                                 struct curltime *now,
                                  bool *stream_error,
                                  CURLcode *result)
 {
   bool connect_timeout = data->mstate < MSTATE_DO;
-  timediff_t timeout_ms = Curl_timeleft_ms(data, now, connect_timeout);
+  timediff_t timeout_ms =
+    Curl_timeleft_ms(data, connect_timeout);
   if(timeout_ms < 0) {
     /* Handle timed out */
     struct curltime since;
@@ -1779,22 +1792,23 @@ static bool multi_handle_timeout(struct Curl_easy *data,
       since = data->progress.t_startop;
     if(data->mstate == MSTATE_RESOLVING)
       failf(data, "Resolving timed out after %" FMT_TIMEDIFF_T
-            " milliseconds", curlx_timediff_ms(*now, since));
+            " milliseconds", curlx_timediff_ms(data->progress.now, since));
     else if(data->mstate == MSTATE_CONNECTING)
       failf(data, "Connection timed out after %" FMT_TIMEDIFF_T
-            " milliseconds", curlx_timediff_ms(*now, since));
+            " milliseconds", curlx_timediff_ms(data->progress.now, since));
     else {
       struct SingleRequest *k = &data->req;
       if(k->size != -1) {
         failf(data, "Operation timed out after %" FMT_TIMEDIFF_T
               " milliseconds with %" FMT_OFF_T " out of %"
               FMT_OFF_T " bytes received",
-              curlx_timediff_ms(*now, since), k->bytecount, k->size);
+              curlx_timediff_ms(data->progress.now, since),
+              k->bytecount, k->size);
       }
       else {
         failf(data, "Operation timed out after %" FMT_TIMEDIFF_T
               " milliseconds with %" FMT_OFF_T " bytes received",
-              curlx_timediff_ms(*now, since), k->bytecount);
+              curlx_timediff_ms(data->progress.now, since), k->bytecount);
       }
     }
     *result = CURLE_OPERATION_TIMEDOUT;
@@ -1920,14 +1934,16 @@ static CURLcode multi_follow(struct Curl_easy *data,
   return CURLE_TOO_MANY_REDIRECTS;
 }
 
-static CURLcode mspeed_check(struct Curl_easy *data, struct curltime now)
+static CURLcode mspeed_check(struct Curl_easy *data)
 {
   timediff_t recv_wait_ms = 0;
   timediff_t send_wait_ms = 0;
 
   /* check if our send/recv limits require idle waits */
-  send_wait_ms = Curl_rlimit_wait_ms(&data->progress.ul.rlimit, now);
-  recv_wait_ms = Curl_rlimit_wait_ms(&data->progress.dl.rlimit, now);
+  send_wait_ms =
+    Curl_rlimit_wait_ms(&data->progress.ul.rlimit, &data->progress.now);
+  recv_wait_ms =
+    Curl_rlimit_wait_ms(&data->progress.dl.rlimit, &data->progress.now);
 
   if(send_wait_ms || recv_wait_ms) {
     if(data->mstate != MSTATE_RATELIMITING) {
@@ -1947,7 +1963,6 @@ static CURLcode mspeed_check(struct Curl_easy *data, struct curltime now)
 }
 
 static CURLMcode state_performing(struct Curl_easy *data,
-                                  struct curltime *nowp,
                                   bool *stream_errorp,
                                   CURLcode *resultp)
 {
@@ -1957,11 +1972,11 @@ static CURLMcode state_performing(struct Curl_easy *data,
   CURLcode result = *resultp = CURLE_OK;
   *stream_errorp = FALSE;
 
-  if(mspeed_check(data, *nowp) == CURLE_AGAIN)
+  if(mspeed_check(data) == CURLE_AGAIN)
     return CURLM_OK;
 
   /* read/write data if it is ready to do so */
-  result = Curl_sendrecv(data, nowp);
+  result = Curl_sendrecv(data);
 
   if(data->req.done || (result == CURLE_RECV_ERROR)) {
     /* If CURLE_RECV_ERROR happens early enough, we assume it was a race
@@ -2079,8 +2094,7 @@ static CURLMcode state_performing(struct Curl_easy *data,
     }
   }
   else { /* not errored, not done */
-    *nowp = curlx_now();
-    mspeed_check(data, *nowp);
+    mspeed_check(data);
   }
   curlx_free(newurl);
   *resultp = result;
@@ -2225,7 +2239,6 @@ end:
 }
 
 static CURLMcode state_ratelimiting(struct Curl_easy *data,
-                                    struct curltime *nowp,
                                     CURLcode *resultp)
 {
   CURLcode result = CURLE_OK;
@@ -2243,7 +2256,7 @@ static CURLMcode state_ratelimiting(struct Curl_easy *data,
     multi_done(data, result, TRUE);
   }
   else {
-    if(!mspeed_check(data, *nowp))
+    if(!mspeed_check(data))
       rc = CURLM_CALL_MULTI_PERFORM;
   }
   *resultp = result;
@@ -2302,7 +2315,6 @@ static CURLMcode state_resolving(struct Curl_multi *multi,
 
 static CURLMcode state_connect(struct Curl_multi *multi,
                                struct Curl_easy *data,
-                               struct curltime *nowp,
                                CURLcode *resultp)
 {
   /* Connect. We want to get a connection identifier filled in. This state can
@@ -2326,7 +2338,6 @@ static CURLMcode state_connect(struct Curl_multi *multi,
     process_pending_handles(data->multi);
 
   if(!result) {
-    *nowp = curlx_now();
     if(async)
       /* We are now waiting for an asynchronous name lookup */
       multistate(data, MSTATE_RESOLVING);
@@ -2354,7 +2365,6 @@ static CURLMcode state_connect(struct Curl_multi *multi,
 }
 
 static CURLMcode multi_runsingle(struct Curl_multi *multi,
-                                 struct curltime *nowp,
                                  struct Curl_easy *data)
 {
   struct Curl_message *msg = NULL;
@@ -2393,8 +2403,6 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi,
        (HTTP/2), or the full connection for older protocols */
     bool stream_error = FALSE;
     rc = CURLM_OK;
-    /* update at start for continuous increase when looping */
-    *nowp = curlx_now();
 
     if(multi_ischanged(multi, TRUE)) {
       CURL_TRC_M(data, "multi changed, check CONNECT_PEND queue");
@@ -2412,7 +2420,7 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi,
     /* Wait for the connect state as only then is the start time stored, but
        we must not check already completed handles */
     if((data->mstate >= MSTATE_CONNECT) && (data->mstate < MSTATE_COMPLETED) &&
-       multi_handle_timeout(data, nowp, &stream_error, &result))
+       multi_handle_timeout(data, &stream_error, &result))
       /* Skip the statemachine and go directly to error handling section. */
       goto statemachine_end;
 
@@ -2432,7 +2440,7 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi,
     case MSTATE_SETUP:
       /* Transitional state. Setup things for a new transfer. The handle
          can come back to this state on a redirect. */
-      *nowp = Curl_pgrsTime(data, TIMER_STARTSINGLE);
+      Curl_pgrsTime(data, TIMER_STARTSINGLE);
       if(data->set.timeout)
         Curl_expire(data, data->set.timeout, EXPIRE_TIMEOUT);
       if(data->set.connecttimeout)
@@ -2445,7 +2453,7 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi,
       FALLTHROUGH();
 
     case MSTATE_CONNECT:
-      rc = state_connect(multi, data, nowp, &result);
+      rc = state_connect(multi, data, &result);
       break;
 
     case MSTATE_RESOLVING:
@@ -2596,11 +2604,11 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi,
       break;
 
     case MSTATE_RATELIMITING: /* limit-rate exceeded in either direction */
-      rc = state_ratelimiting(data, nowp, &result);
+      rc = state_ratelimiting(data, &result);
       break;
 
     case MSTATE_PERFORMING:
-      rc = state_performing(data, nowp, &stream_error, &result);
+      rc = state_performing(data, &stream_error, &result);
       break;
 
     case MSTATE_DONE:
@@ -2655,7 +2663,7 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi,
        * (i.e. CURLM_CALL_MULTI_PERFORM == TRUE) then we should do that before
        * declaring the connection timed out as we may almost have a completed
        * connection. */
-      multi_handle_timeout(data, nowp, &stream_error, &result);
+      multi_handle_timeout(data, &stream_error, &result);
     }
 
 statemachine_end:
@@ -2756,18 +2764,15 @@ statemachine_end:
   return rc;
 }
 
-CURLMcode curl_multi_perform(CURLM *m, int *running_handles)
+static CURLMcode multi_perform(struct Curl_multi *multi,
+                               struct curltime *pnow,
+                               int *running_handles)
 {
   CURLMcode returncode = CURLM_OK;
   struct Curl_tree *t = NULL;
-  struct curltime now = curlx_now();
-  struct Curl_multi *multi = m;
   uint32_t mid;
   SIGPIPE_VARIABLE(pipe_st);
 
-  if(!GOOD_MULTI_HANDLE(multi))
-    return CURLM_BAD_HANDLE;
-
   if(multi->in_callback)
     return CURLM_RECURSIVE_API_CALL;
 
@@ -2788,15 +2793,17 @@ CURLMcode curl_multi_perform(CURLM *m, int *running_handles)
         continue;
       }
       sigpipe_apply(data, &pipe_st);
-      result = multi_runsingle(multi, &now, data);
+      Curl_pgrs_now_at_least(data, pnow);
+      result = multi_runsingle(multi, data);
+      *pnow = data->progress.now; /* in case transfer updated */
       if(result)
         returncode = result;
     } while(Curl_uint32_bset_next(&multi->process, mid, &mid));
   }
   sigpipe_restore(&pipe_st);
 
-  if(multi_ischanged(m, TRUE))
-    process_pending_handles(m);
+  if(multi_ischanged(multi, TRUE))
+    process_pending_handles(multi);
 
   if(!returncode)
     returncode = Curl_mntfy_dispatch_all(multi);
@@ -2812,15 +2819,15 @@ CURLMcode curl_multi_perform(CURLM *m, int *running_handles)
    * been handled!
    */
   do {
-    multi->timetree = Curl_splaygetbest(now, multi->timetree, &t);
+    multi->timetree = Curl_splaygetbest(*pnow, multi->timetree, &t);
     if(t) {
       /* the removed may have another timeout in queue */
       struct Curl_easy *data = Curl_splayget(t);
-      (void)add_next_timeout(now, multi, data);
+      (void)add_next_timeout(*pnow, multi, data);
       if(data->mstate == MSTATE_PENDING) {
         bool stream_unused;
         CURLcode result_unused;
-        if(multi_handle_timeout(data, &now, &stream_unused, &result_unused)) {
+        if(multi_handle_timeout(data, &stream_unused, &result_unused)) {
           infof(data, "PENDING handle timeout");
           move_pending_to_connect(multi, data);
         }
@@ -2834,11 +2841,22 @@ CURLMcode curl_multi_perform(CURLM *m, int *running_handles)
   }
 
   if(CURLM_OK >= returncode)
-    returncode = Curl_update_timer(multi);
+    returncode = Curl_update_timer(multi, pnow);
 
   return returncode;
 }
 
+CURLMcode curl_multi_perform(CURLM *m, int *running_handles)
+{
+  struct curltime now = curlx_now(); /* start of API call */
+  struct Curl_multi *multi = m;
+
+  if(!GOOD_MULTI_HANDLE(multi))
+    return CURLM_BAD_HANDLE;
+  multi = m;
+  return multi_perform(multi, &now, running_handles);
+}
+
 CURLMcode curl_multi_cleanup(CURLM *m)
 {
   struct Curl_multi *multi = m;
@@ -3103,7 +3121,9 @@ static CURLMcode multi_run_dirty(struct multi_run_ctx *mrc)
         mrc->run_xfers++;
         sigpipe_apply(data, &mrc->pipe_st);
         /* runsingle() clears the dirty mid */
-        result = multi_runsingle(multi, &mrc->now, data);
+        Curl_pgrs_now_at_least(data, &mrc->now);
+        result = multi_runsingle(multi, data);
+        mrc->now = data->progress.now; /* in case transfer updated */
 
         if(CURLM_OK >= result) {
           /* reassess event handling of data */
@@ -3135,16 +3155,17 @@ static CURLMcode multi_socket(struct Curl_multi *multi,
   (void)ev_bitmask;
   memset(&mrc, 0, sizeof(mrc));
   mrc.multi = multi;
-  mrc.now = curlx_now();
+  mrc.now = curlx_now(); /* start of API call */
   sigpipe_init(&mrc.pipe_st);
 
   if(checkall) {
     /* *perform() deals with running_handles on its own */
-    result = curl_multi_perform(multi, running_handles);
+    result = multi_perform(multi, &mrc.now, running_handles);
 
     if(result != CURLM_BAD_HANDLE) {
       /* Reassess event status of all active transfers */
-      result = Curl_multi_ev_assess_xfer_bset(multi, &multi->process);
+      result = Curl_multi_ev_assess_xfer_bset(multi, &multi->process,
+                                              &mrc.now);
     }
     goto out;
   }
@@ -3173,7 +3194,6 @@ static CURLMcode multi_socket(struct Curl_multi *multi,
      * to set a 0 timeout and call us again, we run them here.
      * Do that only once or it might be unfair to transfers on other
      * sockets. */
-    mrc.now = curlx_now();
     multi_mark_expired_as_dirty(&mrc);
     result = multi_run_dirty(&mrc);
   }
@@ -3193,7 +3213,7 @@ out:
   }
 
   if(CURLM_OK >= result)
-    result = Curl_update_timer(multi);
+    result = Curl_update_timer(multi, &mrc.now);
   return result;
 }
 
@@ -3346,6 +3366,7 @@ static bool multi_has_dirties(struct Curl_multi *multi)
 }
 
 static void multi_timeout(struct Curl_multi *multi,
+                          struct curltime *pnow,
                           struct curltime *expire_time,
                           long *timeout_ms)
 {
@@ -3360,14 +3381,11 @@ static void multi_timeout(struct Curl_multi *multi,
   }
 
   if(multi_has_dirties(multi)) {
-    *expire_time = curlx_now();
+    *expire_time = *pnow;
     *timeout_ms = 0;
     return;
   }
   else if(multi->timetree) {
-    /* we have a tree of expire times */
-    struct curltime now = curlx_now();
-
     /* splay the lowest to the bottom */
     multi->timetree = Curl_splay(tv_zero, multi->timetree);
     /* this will not return NULL from a non-empty tree, but some compilers
@@ -3377,9 +3395,9 @@ static void multi_timeout(struct Curl_multi *multi,
     /* 'multi->timetree' will be non-NULL here but the compilers sometimes
        yell at us if we assume so */
     if(multi->timetree &&
-       curlx_timediff_us(multi->timetree->key, now) > 0) {
+       curlx_timediff_us(multi->timetree->key, *pnow) > 0) {
       /* some time left before expiration */
-      timediff_t diff_ms = curlx_timediff_ceil_ms(multi->timetree->key, now);
+      timediff_t diff_ms = curlx_timediff_ceil_ms(multi->timetree->key, *pnow);
 #ifndef CURL_DISABLE_VERBOSE_STRINGS
       data = Curl_splayget(multi->timetree);
 #endif
@@ -3419,6 +3437,7 @@ CURLMcode curl_multi_timeout(CURLM *m,
 {
   struct curltime expire_time;
   struct Curl_multi *multi = m;
+  struct curltime now = curlx_now(); /* start of API call */
 
   /* First, make some basic checks that the CURLM handle is a good handle */
   if(!GOOD_MULTI_HANDLE(multi))
@@ -3427,7 +3446,7 @@ CURLMcode curl_multi_timeout(CURLM *m,
   if(multi->in_callback)
     return CURLM_RECURSIVE_API_CALL;
 
-  multi_timeout(multi, &expire_time, timeout_ms);
+  multi_timeout(multi, &now, &expire_time, timeout_ms);
   return CURLM_OK;
 }
 
@@ -3435,7 +3454,8 @@ CURLMcode curl_multi_timeout(CURLM *m,
  * Tell the application it should update its timers, if it subscribes to the
  * update timer callback.
  */
-CURLMcode Curl_update_timer(struct Curl_multi *multi)
+CURLMcode Curl_update_timer(struct Curl_multi *multi,
+                            struct curltime *pnow)
 {
   struct curltime expire_ts;
   long timeout_ms;
@@ -3444,7 +3464,7 @@ CURLMcode Curl_update_timer(struct Curl_multi *multi)
 
   if(!multi->timer_cb || multi->dead)
     return CURLM_OK;
-  multi_timeout(multi, &expire_ts, &timeout_ms);
+  multi_timeout(multi, pnow, &expire_ts, &timeout_ms);
 
   if(timeout_ms < 0 && multi->last_timeout_ms < 0) {
     /* nothing to do */
@@ -3552,7 +3572,6 @@ static CURLMcode multi_addtimeout(struct Curl_easy *data,
 }
 
 void Curl_expire_ex(struct Curl_easy *data,
-                    const struct curltime *nowp,
                     timediff_t milli, expire_id id)
 {
   struct Curl_multi *multi = data->multi;
@@ -3566,7 +3585,7 @@ void Curl_expire_ex(struct Curl_easy *data,
 
   DEBUGASSERT(id < EXPIRE_LAST);
 
-  set = *nowp;
+  set = data->progress.now;
   set.tv_sec += (time_t)(milli / 1000); /* may be a 64 to 32-bit conversion */
   set.tv_usec += (int)(milli % 1000) * 1000;
 
@@ -3580,7 +3599,7 @@ void Curl_expire_ex(struct Curl_easy *data,
 
   /* Add it to the timer list. It must stay in the list until it has expired
      in case we need to recompute the minimum timer later. */
-  multi_addtimeout(data, &set, id, nowp);
+  multi_addtimeout(data, &set, id, &data->progress.now);
 
   if(curr_expire->tv_sec || curr_expire->tv_usec) {
     /* This means that the struct is added as a node in the splay tree.
@@ -3624,8 +3643,7 @@ void Curl_expire_ex(struct Curl_easy *data,
  */
 void Curl_expire(struct Curl_easy *data, timediff_t milli, expire_id id)
 {
-  struct curltime now = curlx_now();
-  Curl_expire_ex(data, &now, milli, id);
+  Curl_expire_ex(data, milli, id);
 }
 
 /*
index 40b49da85d95f391fd9c13f7ec4fcaf20e307b37..8bd7b291604bbba9d755b6d028dc977b631b8a2a 100644 (file)
@@ -33,6 +33,7 @@
 #include "multiif.h"
 #include "curlx/timeval.h"
 #include "multi_ev.h"
+#include "progress.h"
 #include "select.h"
 #include "uint-bset.h"
 #include "uint-spbset.h"
@@ -480,7 +481,8 @@ static struct easy_pollset *mev_get_last_pollset(struct Curl_easy *data,
   return NULL;
 }
 
-static CURLMcode mev_assess(struct Curl_multi *multi, struct Curl_easy *data,
+static CURLMcode mev_assess(struct Curl_multi *multi,
+                            struct Curl_easy *data,
                             struct connectdata *conn)
 {
   struct easy_pollset ps, *last_ps;
@@ -536,7 +538,8 @@ CURLMcode Curl_multi_ev_assess_conn(struct Curl_multi *multi,
 }
 
 CURLMcode Curl_multi_ev_assess_xfer_bset(struct Curl_multi *multi,
-                                         struct uint32_bset *set)
+                                         struct uint32_bset *set,
+                                         struct curltime *pnow)
 {
   uint32_t mid;
   CURLMcode result = CURLM_OK;
@@ -544,8 +547,10 @@ CURLMcode Curl_multi_ev_assess_xfer_bset(struct Curl_multi *multi,
   if(multi && multi->socket_cb && Curl_uint32_bset_first(set, &mid)) {
     do {
       struct Curl_easy *data = Curl_multi_get_easy(multi, mid);
-      if(data)
+      if(data) {
+        Curl_pgrs_now_at_least(data, pnow);
         result = Curl_multi_ev_assess_xfer(multi, data);
+      }
     } while(!result && Curl_uint32_bset_next(set, mid, &mid));
   }
   return result;
index 4e5b2a454d2670950a01ae538a9ea56bf3504988..d9794ed86529e22e5fdd985c2517d92690c3ffaa 100644 (file)
@@ -55,7 +55,8 @@ CURLMcode Curl_multi_ev_assess_xfer(struct Curl_multi *multi,
                                     struct Curl_easy *data);
 /* Assess all easy handles on the list */
 CURLMcode Curl_multi_ev_assess_xfer_bset(struct Curl_multi *multi,
-                                         struct uint32_bset *set);
+                                         struct uint32_bset *set,
+                                         struct curltime *pnow);
 /* Assess the connection by getting its current pollset */
 CURLMcode Curl_multi_ev_assess_conn(struct Curl_multi *multi,
                                     struct Curl_easy *data,
index 9db5b0100029ee1f63d09340c4a467276bf5020d..42651c5150c23cbc9e108e3e6aa58603935826b7 100644 (file)
 
 void Curl_expire(struct Curl_easy *data, timediff_t milli, expire_id);
 void Curl_expire_ex(struct Curl_easy *data,
-                    const struct curltime *nowp,
                     timediff_t milli, expire_id id);
 bool Curl_expire_clear(struct Curl_easy *data);
 void Curl_expire_done(struct Curl_easy *data, expire_id id);
-CURLMcode Curl_update_timer(struct Curl_multi *multi) WARN_UNUSED_RESULT;
+CURLMcode Curl_update_timer(struct Curl_multi *multi,
+                            struct curltime *pnow) WARN_UNUSED_RESULT;
 void Curl_attach_connection(struct Curl_easy *data,
                             struct connectdata *conn);
 void Curl_detach_connection(struct Curl_easy *data);
index 36acc34a36689faa5095be4991d50073d6ac891c..fcfd991d81538edd2955eaf4be22c8b4cb01565a 100644 (file)
@@ -47,26 +47,30 @@ timediff_t Curl_pp_state_timeout(struct Curl_easy *data,
   timediff_t timeout_ms; /* in milliseconds */
   timediff_t response_time = data->set.server_response_timeout ?
     data->set.server_response_timeout : RESP_TIMEOUT;
-  struct curltime now = curlx_now();
 
   /* if CURLOPT_SERVER_RESPONSE_TIMEOUT is set, use that to determine
      remaining time, or use pp->response because SERVER_RESPONSE_TIMEOUT is
      supposed to govern the response for any given server response, not for
      the time from connect to the given server response. */
 
+  /* pingpong can spend some time processing, always update
+   * the transfer timestamp before checking timeouts. */
+  Curl_pgrs_now_set(data);
+
   /* Without a requested timeout, we only wait 'response_time' seconds for the
      full response to arrive before we bail out */
-  timeout_ms = response_time - curlx_timediff_ms(now, pp->response);
+  timeout_ms = response_time -
+               curlx_timediff_ms(data->progress.now, pp->response);
 
   if(data->set.timeout && !disconnecting) {
     /* if timeout is requested, find out how much overall remains */
-    timediff_t timeout2_ms = Curl_timeleft_ms(data, &now, FALSE);
+    timediff_t timeout2_ms = Curl_timeleft_ms(data, FALSE);
     /* pick the lowest number */
     timeout_ms = CURLMIN(timeout_ms, timeout2_ms);
   }
 
   if(disconnecting) {
-    timediff_t total_left_ms = Curl_timeleft_ms(data, NULL, FALSE);
+    timediff_t total_left_ms = Curl_timeleft_ms(data, FALSE);
     timeout_ms = CURLMIN(timeout_ms, CURLMAX(total_left_ms, 0));
   }
 
@@ -135,11 +139,11 @@ CURLcode Curl_pp_statemach(struct Curl_easy *data,
 }
 
 /* initialize stuff to prepare for reading a fresh new response */
-void Curl_pp_init(struct pingpong *pp)
+void Curl_pp_init(struct pingpong *pp, struct curltime *pnow)
 {
   DEBUGASSERT(!pp->initialised);
   pp->nread_resp = 0;
-  pp->response = curlx_now(); /* start response time-out now! */
+  pp->response = *pnow; /* start response time-out */
   pp->pending_resp = TRUE;
   curlx_dyn_init(&pp->sendbuf, DYN_PINGPPONG_CMD);
   curlx_dyn_init(&pp->recvbuf, DYN_PINGPPONG_CMD);
@@ -208,7 +212,7 @@ CURLcode Curl_pp_vsendf(struct Curl_easy *data,
   else {
     pp->sendthis = NULL;
     pp->sendleft = pp->sendsize = 0;
-    pp->response = curlx_now();
+    pp->response = data->progress.now;
   }
 
   return CURLE_OK;
@@ -399,7 +403,7 @@ CURLcode Curl_pp_flushsend(struct Curl_easy *data,
   else {
     pp->sendthis = NULL;
     pp->sendleft = pp->sendsize = 0;
-    pp->response = curlx_now();
+    pp->response = data->progress.now;
   }
   return CURLE_OK;
 }
index 5492453d84591720c4372e7f40335d85bd6714b0..a41d76308cbe2bccae20fe0c4d8ae70b88f39607 100644 (file)
@@ -87,7 +87,7 @@ CURLcode Curl_pp_statemach(struct Curl_easy *data, struct pingpong *pp,
                            bool block, bool disconnecting);
 
 /* initialize stuff to prepare for reading a fresh new response */
-void Curl_pp_init(struct pingpong *pp);
+void Curl_pp_init(struct pingpong *pp, struct curltime *pnow);
 
 /* Returns timeout in ms. 0 or negative number means the timeout has already
    triggered */
index 0ddff3e45ef969dbdf55c6a95c31bc7eb2197a39..1fd22116c5a4b7dd86746e19c2ffde39ddbb386c 100644 (file)
@@ -1297,7 +1297,7 @@ static CURLcode pop3_connect(struct Curl_easy *data, bool *done)
   Curl_sasl_init(&pop3c->sasl, data, &saslpop3);
 
   /* Initialise the pingpong layer */
-  Curl_pp_init(pp);
+  Curl_pp_init(pp, &data->progress.now);
 
   /* Parse the URL options */
   result = pop3_parse_url_options(conn);
index 1ae8b5801d016cc77622786c8d603e859f8320ba..e11a1ff82fa32d8d0a533bf653912baa08531938 100644 (file)
@@ -141,6 +141,25 @@ UNITTEST CURLcode pgrs_speedcheck(struct Curl_easy *data,
   return CURLE_OK;
 }
 
+void Curl_pgrs_now_set(struct Curl_easy *data)
+{
+  data->progress.now = curlx_now();
+}
+
+void Curl_pgrs_now_at_least(struct Curl_easy *data, struct curltime *pts)
+{
+  if((pts->tv_sec > data->progress.now.tv_sec) ||
+     ((pts->tv_sec == data->progress.now.tv_sec) &&
+      (pts->tv_usec > data->progress.now.tv_usec))) {
+    data->progress.now = *pts;
+  }
+}
+
+void Curl_pgrs_now_update(struct Curl_easy *data, struct Curl_easy *other)
+{
+  Curl_pgrs_now_at_least(data, &other->progress.now);
+}
+
 /*
 
    New proposed interface, 9th of February 2000:
@@ -174,7 +193,7 @@ int Curl_pgrsDone(struct Curl_easy *data)
 void Curl_pgrsReset(struct Curl_easy *data)
 {
   Curl_pgrsSetUploadCounter(data, 0);
-  Curl_pgrsSetDownloadCounter(data, 0);
+  data->progress.dl.cur_size = 0;
   Curl_pgrsSetUploadSize(data, -1);
   Curl_pgrsSetDownloadSize(data, -1);
   data->progress.speeder_c = 0; /* reset speed records */
@@ -288,19 +307,17 @@ void Curl_pgrsTimeWas(struct Curl_easy *data, timerid timer,
  *
  * @unittest: 1399
  */
-struct curltime Curl_pgrsTime(struct Curl_easy *data, timerid timer)
+void Curl_pgrsTime(struct Curl_easy *data, timerid timer)
 {
-  struct curltime now = curlx_now();
-
-  Curl_pgrsTimeWas(data, timer, now);
-  return now;
+  Curl_pgrs_now_set(data); /* update on real progress */
+  Curl_pgrsTimeWas(data, timer, data->progress.now);
 }
 
 void Curl_pgrsStartNow(struct Curl_easy *data)
 {
   struct Progress *p = &data->progress;
   p->speeder_c = 0; /* reset the progress meter display */
-  p->start = curlx_now();
+  p->start = data->progress.now;
   p->is_t_startransfer_set = FALSE;
   p->dl.cur_size = 0;
   p->ul.cur_size = 0;
@@ -309,12 +326,20 @@ void Curl_pgrsStartNow(struct Curl_easy *data)
   p->ul_size_known = FALSE;
 }
 
-/*
- * Set the number of downloaded bytes so far.
- */
-void Curl_pgrsSetDownloadCounter(struct Curl_easy *data, curl_off_t size)
+void Curl_pgrs_download_inc(struct Curl_easy *data, size_t delta)
+{
+  if(delta) {
+    data->progress.dl.cur_size += delta;
+    Curl_rlimit_drain(&data->progress.dl.rlimit, delta, &data->progress.now);
+  }
+}
+
+void Curl_pgrs_upload_inc(struct Curl_easy *data, size_t delta)
 {
-  data->progress.dl.cur_size = size;
+  if(delta) {
+    data->progress.ul.cur_size += delta;
+    Curl_rlimit_drain(&data->progress.ul.rlimit, delta, &data->progress.now);
+  }
 }
 
 /*
@@ -618,18 +643,16 @@ static CURLcode pgrs_update(struct Curl_easy *data, struct curltime *pnow)
 
 CURLcode Curl_pgrsUpdate(struct Curl_easy *data)
 {
-  struct curltime now = curlx_now(); /* what time is it */
-  return pgrs_update(data, &now);
+  return pgrs_update(data, &data->progress.now);
 }
 
 CURLcode Curl_pgrsCheck(struct Curl_easy *data)
 {
-  struct curltime now = curlx_now();
   CURLcode result;
 
-  result = pgrs_update(data, &now);
+  result = pgrs_update(data, &data->progress.now);
   if(!result && !data->req.done)
-    result = pgrs_speedcheck(data, &now);
+    result = pgrs_speedcheck(data, &data->progress.now);
   return result;
 }
 
@@ -638,6 +661,5 @@ CURLcode Curl_pgrsCheck(struct Curl_easy *data)
  */
 void Curl_pgrsUpdate_nometer(struct Curl_easy *data)
 {
-  struct curltime now = curlx_now(); /* what time is it */
-  (void)progress_calc(data, &now);
+  (void)progress_calc(data, &data->progress.now);
 }
index 5535c69cbff5f6f58259cee52d665d8a85b41f99..2154ff60bdfa760bba3b132bf6d70ec6fce767e9 100644 (file)
@@ -44,12 +44,24 @@ typedef enum {
   TIMER_LAST /* must be last */
 } timerid;
 
+#define CURL_PGRS_NOW_MONOTONIC
+
+/* Set current time in data->progress.now */
+void Curl_pgrs_now_set(struct Curl_easy *data);
+/* Advance `now` timestamp at least to given timestamp.
+ * No effect it data's `now` is already later than `pts`. */
+void Curl_pgrs_now_at_least(struct Curl_easy *data, struct curltime *pts);
+/* `data` progressing continues after `other` processing. Advance `data`s
+ * now timestamp to at least `other's` timestamp. */
+void Curl_pgrs_now_update(struct Curl_easy *data, struct Curl_easy *other);
+
 int Curl_pgrsDone(struct Curl_easy *data);
 void Curl_pgrsStartNow(struct Curl_easy *data);
 void Curl_pgrsSetDownloadSize(struct Curl_easy *data, curl_off_t size);
 void Curl_pgrsSetUploadSize(struct Curl_easy *data, curl_off_t size);
 
-void Curl_pgrsSetDownloadCounter(struct Curl_easy *data, curl_off_t size);
+void Curl_pgrs_download_inc(struct Curl_easy *data, size_t delta);
+void Curl_pgrs_upload_inc(struct Curl_easy *data, size_t delta);
 void Curl_pgrsSetUploadCounter(struct Curl_easy *data, curl_off_t size);
 
 /* perform progress update, invoking callbacks at intervals */
@@ -68,7 +80,7 @@ void Curl_pgrsReset(struct Curl_easy *data);
 /* Reset sizes for up- and download. */
 void Curl_pgrsResetTransferSizes(struct Curl_easy *data);
 
-struct curltime Curl_pgrsTime(struct Curl_easy *data, timerid timer);
+void Curl_pgrsTime(struct Curl_easy *data, timerid timer);
 /**
  * Update progress timer with the elapsed time from its start to `timestamp`.
  * This allows updating timers later and is used by happy eyeballing, where
index 7e3592506aa87f17b7f435e1c51a34e0049a79ab..5c2e005133138fab4d400d3e7b3f76c1c4077c97 100644 (file)
 void Curl_rlimit_init(struct Curl_rlimit *r,
                       curl_off_t rate_per_s,
                       curl_off_t burst_per_s,
-                      struct curltime ts)
+                      struct curltime *pts)
 {
   curl_off_t rate_steps;
 
   DEBUGASSERT(rate_per_s >= 0);
   DEBUGASSERT(burst_per_s >= rate_per_s || !burst_per_s);
+  DEBUGASSERT(pts);
   r->step_us = CURL_US_PER_SEC;
   r->rate_per_step = rate_per_s;
   r->burst_per_step = burst_per_s;
@@ -57,15 +58,15 @@ void Curl_rlimit_init(struct Curl_rlimit *r,
   }
   r->tokens = r->rate_per_step;
   r->spare_us = 0;
-  r->ts = ts;
+  r->ts = *pts;
   r->blocked = FALSE;
 }
 
-void Curl_rlimit_start(struct Curl_rlimit *r, struct curltime ts)
+void Curl_rlimit_start(struct Curl_rlimit *r, struct curltime *pts)
 {
   r->tokens = r->rate_per_step;
   r->spare_us = 0;
-  r->ts = ts;
+  r->ts = *pts;
 }
 
 bool Curl_rlimit_active(struct Curl_rlimit *r)
@@ -79,16 +80,16 @@ bool Curl_rlimit_is_blocked(struct Curl_rlimit *r)
 }
 
 static void ratelimit_update(struct Curl_rlimit *r,
-                             struct curltime ts)
+                             struct curltime *pts)
 {
   timediff_t elapsed_us, elapsed_steps;
   curl_off_t token_gain;
 
   DEBUGASSERT(r->rate_per_step);
-  if((r->ts.tv_sec == ts.tv_sec) && (r->ts.tv_usec == ts.tv_usec))
+  if((r->ts.tv_sec == pts->tv_sec) && (r->ts.tv_usec == pts->tv_usec))
     return;
 
-  elapsed_us = curlx_timediff_us(ts, r->ts);
+  elapsed_us = curlx_timediff_us(*pts, r->ts);
   if(elapsed_us < 0) { /* not going back in time */
     DEBUGASSERT(0);
     return;
@@ -99,7 +100,7 @@ static void ratelimit_update(struct Curl_rlimit *r,
     return;
 
   /* we do the update */
-  r->ts = ts;
+  r->ts = *pts;
   elapsed_steps = elapsed_us / r->step_us;
   r->spare_us = elapsed_us % r->step_us;
 
@@ -119,12 +120,12 @@ static void ratelimit_update(struct Curl_rlimit *r,
 }
 
 curl_off_t Curl_rlimit_avail(struct Curl_rlimit *r,
-                             struct curltime ts)
+                             struct curltime *pts)
 {
   if(r->blocked)
     return 0;
   else if(r->rate_per_step) {
-    ratelimit_update(r, ts);
+    ratelimit_update(r, pts);
     return r->tokens;
   }
   else
@@ -133,12 +134,12 @@ curl_off_t Curl_rlimit_avail(struct Curl_rlimit *r,
 
 void Curl_rlimit_drain(struct Curl_rlimit *r,
                        size_t tokens,
-                       struct curltime ts)
+                       struct curltime *pts)
 {
   if(r->blocked || !r->rate_per_step)
     return;
 
-  ratelimit_update(r, ts);
+  ratelimit_update(r, pts);
 #if SIZEOF_CURL_OFF_T <= SIZEOF_SIZE_T
   if(tokens > CURL_OFF_T_MAX) {
     r->tokens = CURL_OFF_T_MIN;
@@ -156,13 +157,13 @@ void Curl_rlimit_drain(struct Curl_rlimit *r,
 }
 
 timediff_t Curl_rlimit_wait_ms(struct Curl_rlimit *r,
-                               struct curltime ts)
+                               struct curltime *pts)
 {
   timediff_t wait_us, elapsed_us;
 
   if(r->blocked || !r->rate_per_step)
     return 0;
-  ratelimit_update(r, ts);
+  ratelimit_update(r, pts);
   if(r->tokens > 0)
     return 0;
 
@@ -171,7 +172,7 @@ timediff_t Curl_rlimit_wait_ms(struct Curl_rlimit *r,
   wait_us = (1 + (-r->tokens / r->rate_per_step)) * r->step_us;
   wait_us -= r->spare_us;
 
-  elapsed_us = curlx_timediff_us(ts, r->ts);
+  elapsed_us = curlx_timediff_us(*pts, r->ts);
   if(elapsed_us >= wait_us)
     return 0;
   wait_us -= elapsed_us;
@@ -180,17 +181,17 @@ timediff_t Curl_rlimit_wait_ms(struct Curl_rlimit *r,
 
 void Curl_rlimit_block(struct Curl_rlimit *r,
                        bool activate,
-                       struct curltime ts)
+                       struct curltime *pts)
 {
   if(!activate == !r->blocked)
     return;
 
-  r->ts = ts;
+  r->ts = *pts;
   r->blocked = activate;
   if(!r->blocked) {
     /* Start rate limiting fresh. The amount of time this was blocked
      * does not generate extra tokens. */
-    Curl_rlimit_start(r, ts);
+    Curl_rlimit_start(r, pts);
   }
   else {
     r->tokens = 0;
index 53f803039145175365f7e1557c670e6fcea99cd2..b5a7c56fce48798161fb1f9174388b10268844a4 100644 (file)
@@ -62,14 +62,14 @@ struct Curl_rlimit {
 void Curl_rlimit_init(struct Curl_rlimit *r,
                       curl_off_t rate_per_s,
                       curl_off_t burst_per_s,
-                      struct curltime ts);
+                      struct curltime *pts);
 
 /* Start ratelimiting with the given timestamp. Resets available tokens. */
-void Curl_rlimit_start(struct Curl_rlimit *r, struct curltime ts);
+void Curl_rlimit_start(struct Curl_rlimit *r, struct curltime *pts);
 
 /* How many milliseconds to wait until token are available again. */
 timediff_t Curl_rlimit_wait_ms(struct Curl_rlimit *r,
-                               struct curltime ts);
+                               struct curltime *pts);
 
 /* Return if rate limiting of tokens is active */
 bool Curl_rlimit_active(struct Curl_rlimit *r);
@@ -77,16 +77,16 @@ bool Curl_rlimit_is_blocked(struct Curl_rlimit *r);
 
 /* Return how many tokens are available to spend, may be negative */
 curl_off_t Curl_rlimit_avail(struct Curl_rlimit *r,
-                             struct curltime ts);
+                             struct curltime *pts);
 
 /* Drain tokens from the ratelimit, return how many are now available. */
 void Curl_rlimit_drain(struct Curl_rlimit *r,
                        size_t tokens,
-                       struct curltime ts);
+                       struct curltime *pts);
 
 /* Block/unblock ratelimiting. A blocked ratelimit has 0 tokens available. */
 void Curl_rlimit_block(struct Curl_rlimit *r,
                        bool activate,
-                       struct curltime ts);
+                       struct curltime *pts);
 
 #endif /* HEADER_Curl_rlimit_H */
index fa83380878ad1ec47a14a86306b5da56e2580e9b..03dde6ab6d62a3785b769e3afd1f6a957c3659c0 100644 (file)
@@ -90,7 +90,7 @@ CURLcode Curl_req_soft_reset(struct SingleRequest *req,
 CURLcode Curl_req_start(struct SingleRequest *req,
                         struct Curl_easy *data)
 {
-  req->start = curlx_now();
+  req->start = data->progress.now;
   return Curl_req_soft_reset(req, data);
 }
 
@@ -219,7 +219,7 @@ static CURLcode xfer_send(struct Curl_easy *data,
         size_t body_len = *pnwritten - hds_len;
         Curl_debug(data, CURLINFO_DATA_OUT, buf + hds_len, body_len);
         data->req.writebytecount += body_len;
-        Curl_pgrsSetUploadCounter(data, data->req.writebytecount);
+        Curl_pgrs_upload_inc(data, body_len);
       }
     }
   }
index f4a816e23173d6b187007e48fd4b798e413b8a23..c236d558e627a70bfdcbe076638d25ddd38a8cbd 100644 (file)
@@ -230,7 +230,7 @@ static CURLcode cw_download_write(struct Curl_easy *data,
   if(!ctx->started_response &&
      !(type & (CLIENTWRITE_INFO | CLIENTWRITE_CONNECT))) {
     Curl_pgrsTime(data, TIMER_STARTTRANSFER);
-    Curl_rlimit_start(&data->progress.dl.rlimit, curlx_now());
+    Curl_rlimit_start(&data->progress.dl.rlimit, &data->progress.now);
     ctx->started_response = TRUE;
   }
 
@@ -303,9 +303,10 @@ static CURLcode cw_download_write(struct Curl_easy *data,
   }
 
   /* Update stats, write and report progress */
-  Curl_rlimit_drain(&data->progress.dl.rlimit, nwrite, curlx_now());
-  data->req.bytecount += nwrite;
-  Curl_pgrsSetDownloadCounter(data, data->req.bytecount);
+  if(nwrite) {
+    data->req.bytecount += nwrite;
+    Curl_pgrs_download_inc(data, nwrite);
+  }
 
   if(excess_len) {
     if(!data->req.ignorebody) {
@@ -1201,13 +1202,13 @@ CURLcode Curl_client_read(struct Curl_easy *data, char *buf, size_t blen,
     DEBUGASSERT(data->req.reader_stack);
   }
   if(!data->req.reader_started) {
-    Curl_rlimit_start(&data->progress.ul.rlimit, curlx_now());
+    Curl_rlimit_start(&data->progress.ul.rlimit, &data->progress.now);
     data->req.reader_started = TRUE;
   }
 
   if(Curl_rlimit_active(&data->progress.ul.rlimit)) {
     curl_off_t ul_avail =
-      Curl_rlimit_avail(&data->progress.ul.rlimit, curlx_now());
+      Curl_rlimit_avail(&data->progress.ul.rlimit, &data->progress.now);
     if(ul_avail <= 0) {
       result = CURLE_OK;
       *eos = FALSE;
@@ -1218,8 +1219,6 @@ CURLcode Curl_client_read(struct Curl_easy *data, char *buf, size_t blen,
   }
   result = Curl_creader_read(data, data->req.reader_stack, buf, blen,
                              nread, eos);
-  if(!result)
-    Curl_rlimit_drain(&data->progress.ul.rlimit, *nread, curlx_now());
 
 out:
   CURL_TRC_READ(data, "client_read(len=%zu) -> %d, nread=%zu, eos=%d",
index 24d8fe8de060d1557edc8dffb00e500ab52f81c9..627865dc31ce92cd88c090ccd0426eef086a58c1 100644 (file)
@@ -2813,7 +2813,8 @@ static CURLcode setopt_offt(struct Curl_easy *data, CURLoption option,
     if(offt < 0)
       return CURLE_BAD_FUNCTION_ARGUMENT;
     s->max_send_speed = offt;
-    Curl_rlimit_init(&data->progress.ul.rlimit, offt, offt, curlx_now());
+    Curl_rlimit_init(&data->progress.ul.rlimit, offt, offt,
+                     &data->progress.now);
     break;
   case CURLOPT_MAX_RECV_SPEED_LARGE:
     /*
@@ -2823,7 +2824,8 @@ static CURLcode setopt_offt(struct Curl_easy *data, CURLoption option,
     if(offt < 0)
       return CURLE_BAD_FUNCTION_ARGUMENT;
     s->max_recv_speed = offt;
-    Curl_rlimit_init(&data->progress.dl.rlimit, offt, offt, curlx_now());
+    Curl_rlimit_init(&data->progress.dl.rlimit, offt, offt,
+                     &data->progress.now);
     break;
   case CURLOPT_RESUME_FROM_LARGE:
     /*
index c52d4eca57af95d738836c1ee28f26a0437e0858..2b2e7acaa7acbbd03cb55cef3709c35579e0a567 100644 (file)
--- a/lib/smb.c
+++ b/lib/smb.c
@@ -1146,7 +1146,7 @@ static CURLcode smb_request_state(struct Curl_easy *data, bool *done)
                          sizeof(struct smb_header) + 5);
     data->req.bytecount += len;
     data->req.offset += len;
-    Curl_pgrsSetUploadCounter(data, data->req.bytecount);
+    Curl_pgrs_upload_inc(data, len);
     if(data->req.bytecount >= data->req.size)
       next_state = SMB_CLOSE;
     else
index fa821fe45de34dcac9e7ad9dd1256dc5bbf19336..f8cef899f3524929eb896be325ce6d3f7e58d7cd 100644 (file)
@@ -1441,7 +1441,7 @@ static CURLcode smtp_connect(struct Curl_easy *data, bool *done)
   Curl_sasl_init(&smtpc->sasl, data, &saslsmtp);
 
   /* Initialise the pingpong layer */
-  Curl_pp_init(&smtpc->pp);
+  Curl_pp_init(&smtpc->pp, &data->progress.now);
 
   /* Parse the URL options */
   result = smtp_parse_url_options(data->conn, smtpc);
index 46208506527ca2b5e889e023d3e296b186b541d2..a03d26e5b9bbc08da2d39ec022f432a536727ace 100644 (file)
@@ -133,7 +133,7 @@ CURLcode Curl_blockread_all(struct Curl_cfilter *cf,
 
   *pnread = 0;
   for(;;) {
-    timediff_t timeout_ms = Curl_timeleft_ms(data, NULL, TRUE);
+    timediff_t timeout_ms = Curl_timeleft_ms(data, TRUE);
     if(timeout_ms < 0) {
       /* we already got the timeout */
       return CURLE_OPERATION_TIMEDOUT;
index 3153a392f67908f007d48b24187bb3a74a32013c..843a6e5b00fcf76c34337a5c4b7b712842f60d54 100644 (file)
@@ -1325,11 +1325,8 @@ static CURLcode telnet_do(struct Curl_easy *data, bool *done)
   timediff_t interval_ms;
   struct pollfd pfd[2];
   int poll_cnt;
-  curl_off_t total_dl = 0;
-  curl_off_t total_ul = 0;
   ssize_t snread;
 #endif
-  struct curltime now;
   bool keepon = TRUE;
   char buffer[4 * 1024];
   struct TELNET *tn;
@@ -1511,8 +1508,9 @@ static CURLcode telnet_do(struct Curl_easy *data, bool *done)
     } /* switch */
 
     if(data->set.timeout) {
-      now = curlx_now();
-      if(curlx_timediff_ms(now, conn->created) >= data->set.timeout) {
+      Curl_pgrs_now_set(data);
+      if(curlx_timediff_ms(data->progress.now, conn->created) >=
+         data->set.timeout) {
         failf(data, "Time-out");
         result = CURLE_OPERATION_TIMEDOUT;
         keepon = FALSE;
@@ -1581,8 +1579,7 @@ static CURLcode telnet_do(struct Curl_easy *data, bool *done)
           break;
         }
 
-        total_dl += nread;
-        Curl_pgrsSetDownloadCounter(data, total_dl);
+        Curl_pgrs_download_inc(data, nread);
         result = telrcv(data, tn, (unsigned char *)buffer, nread);
         if(result) {
           keepon = FALSE;
@@ -1622,8 +1619,7 @@ static CURLcode telnet_do(struct Curl_easy *data, bool *done)
           keepon = FALSE;
           break;
         }
-        total_ul += snread;
-        Curl_pgrsSetUploadCounter(data, total_ul);
+        Curl_pgrs_upload_inc(data, (size_t)snread);
       }
       else if(snread < 0)
         keepon = FALSE;
@@ -1632,8 +1628,9 @@ static CURLcode telnet_do(struct Curl_easy *data, bool *done)
     } /* poll switch statement */
 
     if(data->set.timeout) {
-      now = curlx_now();
-      if(curlx_timediff_ms(now, conn->created) >= data->set.timeout) {
+      Curl_pgrs_now_set(data);
+      if(curlx_timediff_ms(data->progress.now, conn->created) >=
+         data->set.timeout) {
         failf(data, "Time-out");
         result = CURLE_OPERATION_TIMEDOUT;
         keepon = FALSE;
index 58e9696fb2c20e74b8d3e0b55018546b97501f6e..c39222d9c335528ef2450414da5f3c9b0a35d4e2 100644 (file)
@@ -205,7 +205,7 @@ static CURLcode tftp_set_timeouts(struct tftp_conn *state)
   bool start = (state->state == TFTP_STATE_START);
 
   /* Compute drop-dead time */
-  timeout_ms = Curl_timeleft_ms(state->data, NULL, start);
+  timeout_ms = Curl_timeleft_ms(state->data, start);
 
   if(timeout_ms < 0) {
     /* time-out, bail out, go home */
@@ -793,7 +793,7 @@ static CURLcode tftp_tx(struct tftp_conn *state, tftp_event_t event)
     }
     /* Update the progress meter */
     k->writebytecount += state->sbytes;
-    Curl_pgrsSetUploadCounter(data, k->writebytecount);
+    Curl_pgrs_upload_inc(data, state->sbytes);
     break;
 
   case TFTP_EVENT_TIMEOUT:
@@ -1192,7 +1192,7 @@ static timediff_t tftp_state_timeout(struct tftp_conn *state,
   if(event)
     *event = TFTP_EVENT_NONE;
 
-  timeout_ms = Curl_timeleft_ms(state->data, NULL,
+  timeout_ms = Curl_timeleft_ms(state->data,
                                 (state->state == TFTP_STATE_START));
   if(timeout_ms < 0) {
     state->error = TFTP_ERR_TIMEOUT;
index bd6be821dc2a7df005964a84481ccc73a580c530..5407ba4477f65b0f7616d2b44d32f33dfffa361f 100644 (file)
@@ -261,7 +261,7 @@ static CURLcode sendrecv_dl(struct Curl_easy *data,
 
     if(bytestoread && Curl_rlimit_active(&data->progress.dl.rlimit)) {
       curl_off_t dl_avail = Curl_rlimit_avail(&data->progress.dl.rlimit,
-                                              curlx_now());
+                                              &data->progress.now);
       /* DEBUGF(infof(data, "dl_rlimit, available=%" FMT_OFF_T, dl_avail));
        */
       /* In case of rate limited downloads: if this loop already got
@@ -364,12 +364,11 @@ static CURLcode sendrecv_ul(struct Curl_easy *data)
  * Curl_sendrecv() is the low-level function to be called when data is to
  * be read and written to/from the connection.
  */
-CURLcode Curl_sendrecv(struct Curl_easy *data, struct curltime *nowp)
+CURLcode Curl_sendrecv(struct Curl_easy *data)
 {
   struct SingleRequest *k = &data->req;
   CURLcode result = CURLE_OK;
 
-  DEBUGASSERT(nowp);
   if(Curl_xfer_is_blocked(data)) {
     result = CURLE_OK;
     goto out;
@@ -395,18 +394,20 @@ CURLcode Curl_sendrecv(struct Curl_easy *data, struct curltime *nowp)
     goto out;
 
   if(k->keepon) {
-    if(Curl_timeleft_ms(data, nowp, FALSE) < 0) {
+    if(Curl_timeleft_ms(data, FALSE) < 0) {
       if(k->size != -1) {
         failf(data, "Operation timed out after %" FMT_TIMEDIFF_T
               " milliseconds with %" FMT_OFF_T " out of %"
               FMT_OFF_T " bytes received",
-              curlx_timediff_ms(*nowp, data->progress.t_startsingle),
+              curlx_timediff_ms(data->progress.now,
+                                data->progress.t_startsingle),
               k->bytecount, k->size);
       }
       else {
         failf(data, "Operation timed out after %" FMT_TIMEDIFF_T
               " milliseconds with %" FMT_OFF_T " bytes received",
-              curlx_timediff_ms(*nowp, data->progress.t_startsingle),
+              curlx_timediff_ms(data->progress.now,
+                                data->progress.t_startsingle),
               k->bytecount);
       }
       result = CURLE_OPERATION_TIMEDOUT;
@@ -902,7 +903,7 @@ bool Curl_xfer_recv_is_paused(struct Curl_easy *data)
 CURLcode Curl_xfer_pause_send(struct Curl_easy *data, bool enable)
 {
   CURLcode result = CURLE_OK;
-  Curl_rlimit_block(&data->progress.ul.rlimit, enable, curlx_now());
+  Curl_rlimit_block(&data->progress.ul.rlimit, enable, &data->progress.now);
   if(!enable && Curl_creader_is_paused(data))
     result = Curl_creader_unpause(data);
   Curl_pgrsSendPause(data, enable);
@@ -912,7 +913,7 @@ CURLcode Curl_xfer_pause_send(struct Curl_easy *data, bool enable)
 CURLcode Curl_xfer_pause_recv(struct Curl_easy *data, bool enable)
 {
   CURLcode result = CURLE_OK;
-  Curl_rlimit_block(&data->progress.dl.rlimit, enable, curlx_now());
+  Curl_rlimit_block(&data->progress.dl.rlimit, enable, &data->progress.now);
   if(!enable && Curl_cwriter_is_paused(data))
     result = Curl_cwriter_unpause(data);
   Curl_conn_ev_data_pause(data, enable);
index 3da5e2445139dc0ca2dfed5edccc017fade9470f..78473b1f0d8936e6b4c51118a588c0dfaa2bac64 100644 (file)
@@ -34,7 +34,7 @@ void Curl_init_CONNECT(struct Curl_easy *data);
 
 CURLcode Curl_pretransfer(struct Curl_easy *data);
 
-CURLcode Curl_sendrecv(struct Curl_easy *data, struct curltime *nowp);
+CURLcode Curl_sendrecv(struct Curl_easy *data);
 CURLcode Curl_retry_request(struct Curl_easy *data, char **url);
 bool Curl_meets_timecondition(struct Curl_easy *data, time_t timeofdoc);
 
index 98fc8ce44f87556fba406048f091ac99af51ea07..fe5c3f38da0411b4f5845001955d1a71dbeb6c86 100644 (file)
--- a/lib/url.c
+++ b/lib/url.c
@@ -514,6 +514,7 @@ CURLcode Curl_open(struct Curl_easy **curl)
 #endif
   Curl_netrc_init(&data->state.netrc);
   Curl_init_userdefined(data);
+  Curl_pgrs_now_set(data); /* on easy handle create */
 
   *curl = data;
   return CURLE_OK;
@@ -664,21 +665,15 @@ static bool conn_maxage(struct Curl_easy *data,
  * Return TRUE iff the given connection is considered dead.
  */
 bool Curl_conn_seems_dead(struct connectdata *conn,
-                          struct Curl_easy *data,
-                          struct curltime *pnow)
+                          struct Curl_easy *data)
 {
   DEBUGASSERT(!data->conn);
   if(!CONN_INUSE(conn)) {
     /* The check for a dead socket makes sense only if the connection is not in
        use */
     bool dead;
-    struct curltime now;
-    if(!pnow) {
-      now = curlx_now();
-      pnow = &now;
-    }
 
-    if(conn_maxage(data, conn, *pnow)) {
+    if(conn_maxage(data, conn, data->progress.now)) {
       /* avoid check if already too old */
       dead = TRUE;
     }
@@ -1241,7 +1236,7 @@ static bool url_match_conn(struct connectdata *conn, void *userdata)
   if(!url_match_multiplex_limits(conn, m))
     return FALSE;
 
-  if(!CONN_INUSE(conn) && Curl_conn_seems_dead(conn, m->data, NULL)) {
+  if(!CONN_INUSE(conn) && Curl_conn_seems_dead(conn, m->data)) {
     /* remove and disconnect. */
     Curl_conn_terminate(m->data, conn, FALSE);
     return FALSE;
@@ -1346,7 +1341,7 @@ static struct connectdata *allocate_conn(struct Curl_easy *data)
   conn->remote_port = -1; /* unknown at this point */
 
   /* Store creation time to help future close decision making */
-  conn->created = curlx_now();
+  conn->created = data->progress.now;
 
   /* Store current time to give a baseline to keepalive connection times. */
   conn->keepalive = conn->created;
@@ -3208,7 +3203,7 @@ static CURLcode resolve_server(struct Curl_easy *data,
 {
   struct hostname *ehost;
   int eport;
-  timediff_t timeout_ms = Curl_timeleft_ms(data, NULL, TRUE);
+  timediff_t timeout_ms = Curl_timeleft_ms(data, TRUE);
   const char *peertype = "host";
   CURLcode result;
 
@@ -3266,7 +3261,7 @@ static CURLcode resolve_server(struct Curl_easy *data,
   else if(result == CURLE_OPERATION_TIMEDOUT) {
     failf(data, "Failed to resolve %s '%s' with timeout after %"
           FMT_TIMEDIFF_T " ms", peertype, ehost->dispname,
-          curlx_timediff_ms(curlx_now(), data->progress.t_startsingle));
+          curlx_timediff_ms(data->progress.now, data->progress.t_startsingle));
     return CURLE_OPERATION_TIMEDOUT;
   }
   else if(result) {
index 82d869c5ff22d453d43f2eee1d17b9b2db8dd2b9..6c2f1e144c579bc23e2c7c3acfe7344e53112885 100644 (file)
--- a/lib/url.h
+++ b/lib/url.h
@@ -84,8 +84,7 @@ const struct Curl_handler *Curl_getn_scheme_handler(const char *scheme,
  * @param nowp      NULL or pointer to time being checked against.
  */
 bool Curl_conn_seems_dead(struct connectdata *conn,
-                          struct Curl_easy *data,
-                          struct curltime *nowp);
+                          struct Curl_easy *data);
 
 /**
  * Perform upkeep operations on the connection.
index 20a0a1cd5d26561da330af53080e72b748a7dae5..062348233a74f7f3549837d896f21bcc692278b2 100644 (file)
@@ -802,6 +802,7 @@ struct pgrs_dir {
 };
 
 struct Progress {
+  struct curltime now; /* current time of processing */
   time_t lastshow; /* time() of the last displayed progress meter or NULL to
                       force redraw at next call */
   struct pgrs_dir ul;
index b9eaa1e98e2e7137341915edc4af530a788929cf..881f06760d85c739c15f648b113a09b19f37da0a 100644 (file)
@@ -384,12 +384,13 @@ struct pkt_io_ctx {
   ngtcp2_path_storage ps;
 };
 
-static void pktx_update_time(struct pkt_io_ctx *pktx,
+static void pktx_update_time(struct Curl_easy *data,
+                             struct pkt_io_ctx *pktx,
                              struct Curl_cfilter *cf)
 {
   struct cf_ngtcp2_ctx *ctx = cf->ctx;
 
-  vquic_ctx_update_time(&ctx->q);
+  vquic_ctx_update_time(data, &ctx->q);
   pktx->ts = (ngtcp2_tstamp)ctx->q.last_op.tv_sec * NGTCP2_SECONDS +
              (ngtcp2_tstamp)ctx->q.last_op.tv_usec * NGTCP2_MICROSECONDS;
 }
@@ -398,10 +399,14 @@ static void pktx_init(struct pkt_io_ctx *pktx,
                       struct Curl_cfilter *cf,
                       struct Curl_easy *data)
 {
+  struct cf_ngtcp2_ctx *ctx = cf->ctx;
+
   pktx->cf = cf;
   pktx->data = data;
   ngtcp2_path_storage_zero(&pktx->ps);
-  pktx_update_time(pktx, cf);
+  vquic_ctx_set_time(data, &ctx->q);
+  pktx->ts = (ngtcp2_tstamp)ctx->q.last_op.tv_sec * NGTCP2_SECONDS +
+             (ngtcp2_tstamp)ctx->q.last_op.tv_usec * NGTCP2_MICROSECONDS;
 }
 
 static int cb_h3_acked_req_body(nghttp3_conn *conn, int64_t stream_id,
@@ -500,7 +505,8 @@ static int cf_ngtcp2_handshake_completed(ngtcp2_conn *tconn, void *user_data)
   if(!ctx || !data)
     return NGHTTP3_ERR_CALLBACK_FAILURE;
 
-  ctx->handshake_at = curlx_now();
+  Curl_pgrs_now_set(data); /* real change */
+  ctx->handshake_at = data->progress.now;
   ctx->tls_handshake_complete = TRUE;
   Curl_vquic_report_handshake(&ctx->tls, cf, data);
 
@@ -900,7 +906,7 @@ static CURLcode check_and_set_expiry(struct Curl_cfilter *cf,
     pktx = &local_pktx;
   }
   else {
-    pktx_update_time(pktx, cf);
+    pktx_update_time(data, pktx, cf);
   }
 
   expiry = ngtcp2_conn_get_expiry(ctx->qconn);
@@ -1046,14 +1052,13 @@ static void cf_ngtcp2_ack_stream(struct Curl_cfilter *cf,
                                  struct h3_stream_ctx *stream)
 {
   struct cf_ngtcp2_ctx *ctx = cf->ctx;
-  struct curltime now = curlx_now();
   curl_off_t avail;
   uint64_t ack_len = 0;
 
   /* How many byte to ack on the stream? */
 
   /* how much does rate limiting allow us to acknowledge? */
-  avail = Curl_rlimit_avail(&data->progress.dl.rlimit, now);
+  avail = Curl_rlimit_avail(&data->progress.dl.rlimit, &data->progress.now);
   if(avail == CURL_OFF_T_MAX) { /* no rate limit, ack all */
     ack_len = stream->download_unacked;
   }
@@ -1077,6 +1082,7 @@ static int cb_h3_recv_data(nghttp3_conn *conn, int64_t stream3_id,
   struct Curl_cfilter *cf = user_data;
   struct cf_ngtcp2_ctx *ctx = cf->ctx;
   struct Curl_easy *data = stream_user_data;
+  struct Curl_easy *calling = CF_DATA_CURRENT(cf);
   struct h3_stream_ctx *stream = H3_STREAM_CTX(ctx, data);
 
   (void)conn;
@@ -1084,6 +1090,8 @@ static int cb_h3_recv_data(nghttp3_conn *conn, int64_t stream3_id,
 
   if(!stream)
     return NGHTTP3_ERR_CALLBACK_FAILURE;
+  if(calling)
+    Curl_pgrs_now_update(data, calling);
 
   h3_xfer_write_resp(cf, data, stream, (const char *)buf, blen, FALSE);
   CURL_TRC_CF(data, cf, "[%" PRId64 "] DATA len=%zu", stream->id, blen);
@@ -1740,19 +1748,30 @@ denied:
   return result;
 }
 
+struct cf_ngtcp2_recv_ctx {
+  struct pkt_io_ctx *pktx;
+  size_t pkt_count;
+};
+
 static CURLcode cf_ngtcp2_recv_pkts(const unsigned char *buf, size_t buflen,
                                     size_t gso_size,
                                     struct sockaddr_storage *remote_addr,
                                     socklen_t remote_addrlen, int ecn,
                                     void *userp)
 {
-  struct pkt_io_ctx *pktx = userp;
+  struct cf_ngtcp2_recv_ctx *rctx = userp;
+  struct pkt_io_ctx *pktx = rctx->pktx;
   struct cf_ngtcp2_ctx *ctx = pktx->cf->ctx;
   ngtcp2_pkt_info pi;
   ngtcp2_path path;
   size_t offset, pktlen;
   int rv;
 
+  if(!rctx->pkt_count) {
+    pktx_update_time(pktx->data, pktx, pktx->cf);
+    ngtcp2_path_storage_zero(&pktx->ps);
+  }
+
   if(ecn)
     CURL_TRC_CF(pktx->data, pktx->cf, "vquic_recv(len=%zu, gso=%zu, ecn=%x)",
                 buflen, gso_size, ecn);
@@ -1763,6 +1782,7 @@ static CURLcode cf_ngtcp2_recv_pkts(const unsigned char *buf, size_t buflen,
   pi.ecn = (uint8_t)ecn;
 
   for(offset = 0; offset < buflen; offset += gso_size) {
+    rctx->pkt_count++;
     pktlen = ((offset + gso_size) <= buflen) ? gso_size : (buflen - offset);
     rv = ngtcp2_conn_read_pkt(ctx->qconn, &path, &pi,
                               buf + offset, pktlen, pktx->ts);
@@ -1787,23 +1807,22 @@ static CURLcode cf_progress_ingress(struct Curl_cfilter *cf,
 {
   struct cf_ngtcp2_ctx *ctx = cf->ctx;
   struct pkt_io_ctx local_pktx;
+  struct cf_ngtcp2_recv_ctx rctx;
   CURLcode result = CURLE_OK;
 
   if(!pktx) {
     pktx_init(&local_pktx, cf, data);
     pktx = &local_pktx;
   }
-  else {
-    pktx_update_time(pktx, cf);
-    ngtcp2_path_storage_zero(&pktx->ps);
-  }
 
   result = Curl_vquic_tls_before_recv(&ctx->tls, cf, data);
   if(result)
     return result;
 
+  rctx.pktx = pktx;
+  rctx.pkt_count = 0;
   return vquic_recv_packets(cf, data, &ctx->q, 1000,
-                            cf_ngtcp2_recv_pkts, pktx);
+                            cf_ngtcp2_recv_pkts, &rctx);
 }
 
 /**
@@ -1929,7 +1948,7 @@ static CURLcode cf_progress_egress(struct Curl_cfilter *cf,
     pktx = &local_pktx;
   }
   else {
-    pktx_update_time(pktx, cf);
+    pktx_update_time(data, pktx, cf);
     ngtcp2_path_storage_zero(&pktx->ps);
   }
 
@@ -2016,7 +2035,7 @@ static CURLcode cf_progress_egress(struct Curl_cfilter *cf,
       }
       return curlcode;
     }
-    pktx_update_time(pktx, cf);
+    pktx_update_time(data, pktx, cf);
     ngtcp2_conn_update_pkt_tx_time(ctx->qconn, pktx->ts);
   }
   return CURLE_OK;
@@ -2529,7 +2548,7 @@ static CURLcode cf_connect_start(struct Curl_cfilter *cf,
   ctx->qlogfd = qfd; /* -1 if failure above */
   quic_settings(ctx, data, pktx);
 
-  result = vquic_ctx_init(&ctx->q);
+  result = vquic_ctx_init(data, &ctx->q);
   if(result)
     return result;
 
@@ -2598,7 +2617,6 @@ static CURLcode cf_ngtcp2_connect(struct Curl_cfilter *cf,
   struct cf_ngtcp2_ctx *ctx = cf->ctx;
   CURLcode result = CURLE_OK;
   struct cf_call_data save;
-  struct curltime now;
   struct pkt_io_ctx pktx;
 
   if(cf->connected) {
@@ -2614,13 +2632,12 @@ static CURLcode cf_ngtcp2_connect(struct Curl_cfilter *cf,
   }
 
   *done = FALSE;
-  now = curlx_now();
   pktx_init(&pktx, cf, data);
 
   CF_DATA_SAVE(save, cf, data);
 
   if(!ctx->qconn) {
-    ctx->started_at = now;
+    ctx->started_at = data->progress.now;
     result = cf_connect_start(cf, data, &pktx);
     if(result)
       goto out;
@@ -2794,7 +2811,8 @@ static bool cf_ngtcp2_conn_is_alive(struct Curl_cfilter *cf,
    * it will close the connection when it expires. */
   rp = ngtcp2_conn_get_remote_transport_params(ctx->qconn);
   if(rp && rp->max_idle_timeout) {
-    timediff_t idletime_ms = curlx_timediff_ms(curlx_now(), ctx->q.last_io);
+    timediff_t idletime_ms =
+      curlx_timediff_ms(data->progress.now, ctx->q.last_io);
     if(idletime_ms > 0) {
       uint64_t max_idle_ms =
         (uint64_t)(rp->max_idle_timeout / NGTCP2_MILLISECONDS);
index 4e6e325295e43eb244a44dd1405504b58b55a136..44a12526f0118ec9526d27527e2c3bafdc38d854 100644 (file)
@@ -1161,7 +1161,7 @@ static CURLcode cf_osslq_ctx_start(struct Curl_cfilter *cf,
   if(result)
     goto out;
 
-  result = vquic_ctx_init(&ctx->q);
+  result = vquic_ctx_init(data, &ctx->q);
   if(result)
     goto out;
 
@@ -1748,7 +1748,6 @@ static CURLcode cf_osslq_connect(struct Curl_cfilter *cf,
   struct cf_osslq_ctx *ctx = cf->ctx;
   CURLcode result = CURLE_OK;
   struct cf_call_data save;
-  struct curltime now;
   int err;
 
   if(cf->connected) {
@@ -1764,11 +1763,10 @@ static CURLcode cf_osslq_connect(struct Curl_cfilter *cf,
   }
 
   *done = FALSE;
-  now = curlx_now();
   CF_DATA_SAVE(save, cf, data);
 
   if(!ctx->tls.ossl.ssl) {
-    ctx->started_at = now;
+    ctx->started_at = data->progress.now;
     result = cf_osslq_ctx_start(cf, data);
     if(result)
       goto out;
@@ -1778,7 +1776,7 @@ static CURLcode cf_osslq_connect(struct Curl_cfilter *cf,
     int readable = SOCKET_READABLE(ctx->q.sockfd, 0);
     if(readable > 0 && (readable & CURL_CSELECT_IN)) {
       ctx->got_first_byte = TRUE;
-      ctx->first_byte_at = curlx_now();
+      ctx->first_byte_at = data->progress.now;
     }
   }
 
@@ -1799,14 +1797,14 @@ static CURLcode cf_osslq_connect(struct Curl_cfilter *cf,
       /* if not recorded yet, take the timestamp before we called
        * SSL_do_handshake() as the time we received the first packet. */
       ctx->got_first_byte = TRUE;
-      ctx->first_byte_at = now;
+      ctx->first_byte_at = data->progress.now;
     }
     /* Record the handshake complete with a new time stamp. */
-    now = curlx_now();
-    ctx->handshake_at = now;
-    ctx->q.last_io = now;
+    Curl_pgrs_now_set(data);
+    ctx->handshake_at = data->progress.now;
+    ctx->q.last_io = data->progress.now;
     CURL_TRC_CF(data, cf, "handshake complete after %" FMT_TIMEDIFF_T "ms",
-                curlx_timediff_ms(now, ctx->started_at));
+                curlx_timediff_ms(data->progress.now, ctx->started_at));
     result = cf_osslq_verify_peer(cf, data);
     if(!result) {
       CURL_TRC_CF(data, cf, "peer verified");
@@ -1818,17 +1816,17 @@ static CURLcode cf_osslq_connect(struct Curl_cfilter *cf,
     int detail = SSL_get_error(ctx->tls.ossl.ssl, err);
     switch(detail) {
     case SSL_ERROR_WANT_READ:
-      ctx->q.last_io = now;
+      ctx->q.last_io = data->progress.now;
       CURL_TRC_CF(data, cf, "QUIC SSL_connect() -> WANT_RECV");
       goto out;
     case SSL_ERROR_WANT_WRITE:
-      ctx->q.last_io = now;
+      ctx->q.last_io = data->progress.now;
       CURL_TRC_CF(data, cf, "QUIC SSL_connect() -> WANT_SEND");
       result = CURLE_OK;
       goto out;
 #ifdef SSL_ERROR_WANT_ASYNC
     case SSL_ERROR_WANT_ASYNC:
-      ctx->q.last_io = now;
+      ctx->q.last_io = data->progress.now;
       CURL_TRC_CF(data, cf, "QUIC SSL_connect() -> WANT_ASYNC");
       result = CURLE_OK;
       goto out;
@@ -2242,7 +2240,7 @@ static bool cf_osslq_conn_is_alive(struct Curl_cfilter *cf,
       goto out;
     }
     CURL_TRC_CF(data, cf, "negotiated idle timeout: %" PRIu64 "ms", idle_ms);
-    idletime = curlx_timediff_ms(curlx_now(), ctx->q.last_io);
+    idletime = curlx_timediff_ms(data->progress.now, ctx->q.last_io);
     if(idle_ms && idletime > 0 && (uint64_t)idletime > idle_ms)
       goto out;
   }
index feceaf6369d9a390354d8b1fd802569ab71d70d1..4ee12dc005128fdaa748fcf2a3908e9d5f279c2d 100644 (file)
@@ -499,6 +499,7 @@ static void cf_quiche_recv_body(struct Curl_cfilter *cf,
 }
 
 static void cf_quiche_process_ev(struct Curl_cfilter *cf,
+                                 struct Curl_easy *calling,
                                  struct Curl_easy *data,
                                  struct h3_stream_ctx *stream,
                                  quiche_h3_event *ev)
@@ -506,6 +507,7 @@ static void cf_quiche_process_ev(struct Curl_cfilter *cf,
   if(!stream)
     return;
 
+  Curl_pgrs_now_update(data, calling);
   switch(quiche_h3_event_type(ev)) {
   case QUICHE_H3_EVENT_HEADERS: {
     struct cb_ctx cb_ctx;
@@ -561,6 +563,7 @@ struct cf_quich_disp_ctx {
   uint64_t stream_id;
   struct Curl_cfilter *cf;
   struct Curl_multi *multi;
+  struct Curl_easy *calling;
   quiche_h3_event *ev;
 };
 
@@ -572,7 +575,7 @@ static bool cf_quiche_disp_event(uint32_t mid, void *val, void *user_data)
   if(stream->id == dctx->stream_id) {
     struct Curl_easy *sdata = Curl_multi_get_easy(dctx->multi, mid);
     if(sdata)
-      cf_quiche_process_ev(dctx->cf, sdata, stream, dctx->ev);
+      cf_quiche_process_ev(dctx->cf, dctx->calling, sdata, stream, dctx->ev);
     return FALSE; /* stop iterating */
   }
   return TRUE;
@@ -599,7 +602,7 @@ static CURLcode cf_poll_events(struct Curl_cfilter *cf,
       stream = H3_STREAM_CTX(ctx, data);
       if(stream && stream->id == (uint64_t)rv) {
         /* event for calling transfer */
-        cf_quiche_process_ev(cf, data, stream, ev);
+        cf_quiche_process_ev(cf, data, data, stream, ev);
         quiche_h3_event_free(ev);
         if(stream->xfer_result)
           return stream->xfer_result;
@@ -610,6 +613,7 @@ static CURLcode cf_poll_events(struct Curl_cfilter *cf,
         struct cf_quich_disp_ctx dctx;
         dctx.stream_id = (uint64_t)rv;
         dctx.cf = cf;
+        dctx.calling = data;
         dctx.multi = data->multi;
         dctx.ev = ev;
         Curl_uint32_hash_visit(&ctx->streams, cf_quiche_disp_event, &dctx);
@@ -865,7 +869,7 @@ static CURLcode cf_quiche_recv(struct Curl_cfilter *cf, struct Curl_easy *data,
   *pnread = 0;
   (void)buf;
   (void)blen;
-  vquic_ctx_update_time(&ctx->q);
+  vquic_ctx_update_time(data, &ctx->q);
 
   if(!stream)
     return CURLE_RECV_ERROR;
@@ -1075,7 +1079,7 @@ static CURLcode cf_quiche_send(struct Curl_cfilter *cf, struct Curl_easy *data,
   CURLcode result;
 
   *pnwritten = 0;
-  vquic_ctx_update_time(&ctx->q);
+  vquic_ctx_update_time(data, &ctx->q);
 
   result = cf_process_ingress(cf, data);
   if(result)
@@ -1233,7 +1237,7 @@ static CURLcode cf_quiche_ctx_open(struct Curl_cfilter *cf,
   DEBUGASSERT(ctx->q.sockfd != CURL_SOCKET_BAD);
   DEBUGASSERT(ctx->initialized);
 
-  result = vquic_ctx_init(&ctx->q);
+  result = vquic_ctx_init(data, &ctx->q);
   if(result)
     return result;
 
@@ -1352,7 +1356,7 @@ static CURLcode cf_quiche_connect(struct Curl_cfilter *cf,
   }
 
   *done = FALSE;
-  vquic_ctx_update_time(&ctx->q);
+  vquic_ctx_update_time(data, &ctx->q);
 
   if(!ctx->qconn) {
     result = cf_quiche_ctx_open(cf, data);
@@ -1434,7 +1438,7 @@ static CURLcode cf_quiche_shutdown(struct Curl_cfilter *cf,
     int err;
 
     ctx->shutdown_started = TRUE;
-    vquic_ctx_update_time(&ctx->q);
+    vquic_ctx_update_time(data, &ctx->q);
     err = quiche_conn_close(ctx->qconn, TRUE, 0, NULL, 0);
     if(err) {
       CURL_TRC_CF(data, cf, "error %d adding shutdown packet, "
index 88c1b7226650b266e9807ba2bf8eae8cadf8eada..be35a96021fe1c451a5a9a9f4d5a89e28bb488ea 100644 (file)
@@ -41,6 +41,7 @@
 #include "curl_osslq.h"
 #include "curl_quiche.h"
 #include "../multiif.h"
+#include "../progress.h"
 #include "../rand.h"
 #include "vquic.h"
 #include "vquic_int.h"
@@ -75,7 +76,8 @@ void Curl_quic_ver(char *p, size_t len)
 #endif
 }
 
-CURLcode vquic_ctx_init(struct cf_quic_ctx *qctx)
+CURLcode vquic_ctx_init(struct Curl_easy *data,
+                        struct cf_quic_ctx *qctx)
 {
   Curl_bufq_init2(&qctx->sendbuf, NW_CHUNK_SIZE, NW_SEND_CHUNKS,
                   BUFQ_OPT_SOFT_LIMIT);
@@ -94,7 +96,7 @@ CURLcode vquic_ctx_init(struct cf_quic_ctx *qctx)
     }
   }
 #endif
-  vquic_ctx_update_time(qctx);
+  vquic_ctx_set_time(data, qctx);
 
   return CURLE_OK;
 }
@@ -104,9 +106,17 @@ void vquic_ctx_free(struct cf_quic_ctx *qctx)
   Curl_bufq_free(&qctx->sendbuf);
 }
 
-void vquic_ctx_update_time(struct cf_quic_ctx *qctx)
+void vquic_ctx_set_time(struct Curl_easy *data,
+                        struct cf_quic_ctx *qctx)
 {
-  qctx->last_op = curlx_now();
+  qctx->last_op = data->progress.now;
+}
+
+void vquic_ctx_update_time(struct Curl_easy *data,
+                           struct cf_quic_ctx *qctx)
+{
+  Curl_pgrs_now_set(data);
+  qctx->last_op = data->progress.now;
 }
 
 static CURLcode send_packet_no_gso(struct Curl_cfilter *cf,
index cd8e8989d2ea621e7e8411b864bd6d64313d4fa8..efe4e2c816f1ea0e885b34b94cb77667dfdaaaad 100644 (file)
@@ -54,10 +54,15 @@ struct cf_quic_ctx {
 #define H3_STREAM_CTX(ctx, data)                                        \
   (data ? Curl_uint32_hash_get(&(ctx)->streams, (data)->mid) : NULL)
 
-CURLcode vquic_ctx_init(struct cf_quic_ctx *qctx);
+CURLcode vquic_ctx_init(struct Curl_easy *data,
+                        struct cf_quic_ctx *qctx);
 void vquic_ctx_free(struct cf_quic_ctx *qctx);
 
-void vquic_ctx_update_time(struct cf_quic_ctx *qctx);
+void vquic_ctx_set_time(struct Curl_easy *data,
+                        struct cf_quic_ctx *qctx);
+
+void vquic_ctx_update_time(struct Curl_easy *data,
+                           struct cf_quic_ctx *qctx);
 
 void vquic_push_blocked_pkt(struct Curl_cfilter *cf,
                             struct cf_quic_ctx *qctx,
index cd3a7d6622e15ed0045fb0067f61eedcb3edebc9..e03ad5a9688b55419f6dfbd3fc9b2c63be93fb87 100644 (file)
@@ -2500,7 +2500,7 @@ static CURLcode myssh_block_statemach(struct Curl_easy *data,
       if(result)
         break;
 
-      left_ms = Curl_timeleft_ms(data, NULL, FALSE);
+      left_ms = Curl_timeleft_ms(data, FALSE);
       if(left_ms < 0) {
         failf(data, "Operation timed out");
         return CURLE_OPERATION_TIMEDOUT;
index 4437724fcf421097c4aaf9862e9566d2764cd267..ba2a9375d728ed7b52c83223cced6a86685c820f 100644 (file)
@@ -3121,13 +3121,13 @@ static CURLcode ssh_block_statemach(struct Curl_easy *data,
                                     bool disconnect)
 {
   CURLcode result = CURLE_OK;
-  struct curltime dis = curlx_now();
+  struct curltime start = data->progress.now;
 
   while((sshc->state != SSH_STOP) && !result) {
     bool block;
     timediff_t left_ms = 1000;
-    struct curltime now = curlx_now();
 
+    Curl_pgrs_now_set(data); /* timeout disconnect */
     result = ssh_statemachine(data, sshc, sshp, &block);
     if(result)
       break;
@@ -3137,13 +3137,13 @@ static CURLcode ssh_block_statemach(struct Curl_easy *data,
       if(result)
         break;
 
-      left_ms = Curl_timeleft_ms(data, NULL, FALSE);
+      left_ms = Curl_timeleft_ms(data, FALSE);
       if(left_ms < 0) {
         failf(data, "Operation timed out");
         return CURLE_OPERATION_TIMEDOUT;
       }
     }
-    else if(curlx_timediff_ms(now, dis) > 1000) {
+    else if(curlx_timediff_ms(data->progress.now, start) > 1000) {
       /* disconnect timeout */
       failf(data, "Disconnect timed out");
       result = CURLE_OK;
index a98ff4a62059e2c55adfe94e70dbbf5eae109f9b..bdea5e58e31e091cf86798e5d7b566f9b08f0437 100644 (file)
@@ -411,7 +411,7 @@ CURLcode Curl_gtls_shared_creds_create(struct Curl_easy *data,
   }
 
   shared->refcount = 1;
-  shared->time = curlx_now();
+  shared->time = data->progress.now;
   *pcreds = shared;
   return CURLE_OK;
 }
@@ -562,8 +562,7 @@ static bool gtls_shared_creds_expired(const struct Curl_easy *data,
                                       const struct gtls_shared_creds *sc)
 {
   const struct ssl_general_config *cfg = &data->set.general_ssl;
-  struct curltime now = curlx_now();
-  timediff_t elapsed_ms = curlx_timediff_ms(now, sc->time);
+  timediff_t elapsed_ms = curlx_timediff_ms(data->progress.now, sc->time);
   timediff_t timeout_ms = cfg->ca_cache_timeout * (timediff_t)1000;
 
   if(timeout_ms < 0)
index 444fbc4e04530a5aaf6727d7af57875750bb193d..a7f169d641cfec7cb9b8d1d0e310f839f695728f 100644 (file)
@@ -3219,8 +3219,7 @@ static bool ossl_cached_x509_store_expired(const struct Curl_easy *data,
   if(cfg->ca_cache_timeout < 0)
     return FALSE;
   else {
-    struct curltime now = curlx_now();
-    timediff_t elapsed_ms = curlx_timediff_ms(now, mb->time);
+    timediff_t elapsed_ms = curlx_timediff_ms(data->progress.now, mb->time);
     timediff_t timeout_ms = cfg->ca_cache_timeout * (timediff_t)1000;
 
     return elapsed_ms >= timeout_ms;
@@ -3305,7 +3304,7 @@ static void ossl_set_cached_x509_store(struct Curl_cfilter *cf,
       curlx_free(share->CAfile);
     }
 
-    share->time = curlx_now();
+    share->time = data->progress.now;
     share->store = store;
     share->store_is_empty = is_empty;
     share->CAfile = CAfile;
index 6159d90ab50bf912eb0c60d3d1582c3df86bf277..8a6550d8bf1201a59bad8cfa61634006ba1d107b 100644 (file)
@@ -1806,7 +1806,7 @@ schannel_recv_renegotiate(struct Curl_cfilter *cf, struct Curl_easy *data,
       remaining = MAX_RENEG_BLOCK_TIME - elapsed;
 
       if(blocking) {
-        timeout_ms = Curl_timeleft_ms(data, NULL, FALSE);
+        timeout_ms = Curl_timeleft_ms(data, FALSE);
 
         if(timeout_ms < 0) {
           result = CURLE_OPERATION_TIMEDOUT;
@@ -1959,7 +1959,7 @@ static CURLcode schannel_send(struct Curl_cfilter *cf, struct Curl_easy *data,
     while(len > *pnwritten) {
       size_t this_write = 0;
       int what;
-      timediff_t timeout_ms = Curl_timeleft_ms(data, NULL, FALSE);
+      timediff_t timeout_ms = Curl_timeleft_ms(data, FALSE);
       if(timeout_ms < 0) {
         /* we already got the timeout */
         failf(data, "schannel: timed out sending data "
index 696c7fbaf8609f54aa72ad17844f25208d3662f1..171e3731e8f7afd1c05a36b7041d17a853ec337a 100644 (file)
@@ -1370,8 +1370,10 @@ static CURLcode ssl_cf_connect(struct Curl_cfilter *cf,
 
   if(!result && *done) {
     cf->connected = TRUE;
-    if(connssl->state == ssl_connection_complete)
-      connssl->handshake_done = curlx_now();
+    if(connssl->state == ssl_connection_complete) {
+      Curl_pgrs_now_set(data);
+      connssl->handshake_done = data->progress.now;
+    }
     /* Connection can be deferred when sending early data */
     DEBUGASSERT(connssl->state == ssl_connection_complete ||
                 connssl->state == ssl_connection_deferred);
@@ -1839,7 +1841,7 @@ static CURLcode vtls_shutdown_blocking(struct Curl_cfilter *cf,
 
   *done = FALSE;
   while(!result && !*done && loop--) {
-    timeout_ms = Curl_shutdown_timeleft(cf->conn, cf->sockindex, NULL);
+    timeout_ms = Curl_shutdown_timeleft(data, cf->conn, cf->sockindex);
 
     if(timeout_ms < 0) {
       /* no need to continue if time is already up */
@@ -1886,7 +1888,7 @@ CURLcode Curl_ssl_cfilter_remove(struct Curl_easy *data,
     if(cf->cft == &Curl_cft_ssl) {
       bool done;
       CURL_TRC_CF(data, cf, "shutdown and remove SSL, start");
-      Curl_shutdown_start(data, sockindex, 0, NULL);
+      Curl_shutdown_start(data, sockindex, 0);
       result = vtls_shutdown_blocking(cf, data, send_shutdown, &done);
       Curl_shutdown_clear(data, sockindex);
       if(!result && !done) /* blocking failed? */
index 0c47e9c2eefbd037c151322702911573f1b00469..8a845a6ba1d957e25eaaf6d45b1ee094508f5bcb 100644 (file)
@@ -727,8 +727,7 @@ static bool wssl_cached_x509_store_expired(const struct Curl_easy *data,
                                            const struct wssl_x509_share *mb)
 {
   const struct ssl_general_config *cfg = &data->set.general_ssl;
-  struct curltime now = curlx_now();
-  timediff_t elapsed_ms = curlx_timediff_ms(now, mb->time);
+  timediff_t elapsed_ms = curlx_timediff_ms(data->progress.now, mb->time);
   timediff_t timeout_ms = cfg->ca_cache_timeout * (timediff_t)1000;
 
   if(timeout_ms < 0)
@@ -811,7 +810,7 @@ static void wssl_set_cached_x509_store(struct Curl_cfilter *cf,
       curlx_free(share->CAfile);
     }
 
-    share->time = curlx_now();
+    share->time = data->progress.now;
     share->store = store;
     share->CAfile = CAfile;
   }
index 38d4195879e1e1908480fe34f6648913bad82494..bde13419e63f120343d6ceddf795d46427560208 100644 (file)
--- a/lib/ws.c
+++ b/lib/ws.c
@@ -1696,7 +1696,7 @@ static CURLcode ws_send_raw_blocking(struct Curl_easy *data,
 
       CURL_TRC_WS(data, "ws_send_raw_blocking() partial, %zu left to send",
                   buflen);
-      left_ms = Curl_timeleft_ms(data, NULL, FALSE);
+      left_ms = Curl_timeleft_ms(data, FALSE);
       if(left_ms < 0) {
         failf(data, "[WS] Timeout waiting for socket becoming writable");
         return CURLE_SEND_ERROR;
index f08e6f19fa6be9fe3795961461748cb5ceda463d..3c5b15cea5f8af161fc4623fdc681fd876f615cd 100644 (file)
@@ -72,7 +72,7 @@ via: 1.1 nghttpx
 s/^server: nghttpx.*\r?\n//
 </stripfile>
 <limits>
-Allocations: 150
+Allocations: 155
 Maximum allocated: 1800000
 </limits>
 </verify>
index 9bed1ebff6f4d63a925d5a0985b917d7d3ae986a..1c3f0cc639f7f1cfad32cab014c9411426c56440 100644 (file)
@@ -147,7 +147,8 @@ static CURLcode test_unit1303(const char *arg)
     timediff_t timeout;
     NOW(run[i].now_s, run[i].now_us);
     TIMEOUTS(run[i].timeout_ms, run[i].connecttimeout_ms);
-    timeout = Curl_timeleft_ms(easy, &now, run[i].connecting);
+    easy->progress.now = now;
+    timeout = Curl_timeleft_ms(easy, run[i].connecting);
     if(timeout != run[i].result)
       fail(run[i].comment);
   }
index ca632eb2d6937f0aa1a38a9ba3d099cd2813bea8..e02bf08bd97fd4440f848057f366e9ac6912904e 100644 (file)
@@ -78,6 +78,7 @@ static CURLcode test_unit1399(const char *arg)
   struct Curl_easy data;
   struct curltime now = curlx_now();
 
+  data.progress.now = now;
   data.progress.t_nslookup = 0;
   data.progress.t_connect = 0;
   data.progress.t_appconnect = 0;
index 0f9d27492c03396f839b94bb7546e02511858f57..f65355f362a80ca0cac1c0013451e35b843f5bb1 100644 (file)
@@ -33,71 +33,72 @@ static CURLcode test_unit3216(const char *arg)
 
   /* A ratelimit that is unlimited */
   ts = curlx_now();
-  Curl_rlimit_init(&r, 0, 0, ts);
-  fail_unless(Curl_rlimit_avail(&r, ts) == CURL_OFF_T_MAX, "inf");
-  Curl_rlimit_drain(&r, 1000000, ts);
-  fail_unless(Curl_rlimit_avail(&r, ts) == CURL_OFF_T_MAX, "drain keep inf");
-  fail_unless(Curl_rlimit_wait_ms(&r, ts) == 0, "inf never waits");
+  Curl_rlimit_init(&r, 0, 0, &ts);
+  fail_unless(Curl_rlimit_avail(&r, &ts) == CURL_OFF_T_MAX, "inf");
+  Curl_rlimit_drain(&r, 1000000, &ts);
+  fail_unless(Curl_rlimit_avail(&r, &ts) == CURL_OFF_T_MAX, "drain keep inf");
+  fail_unless(Curl_rlimit_wait_ms(&r, &ts) == 0, "inf never waits");
 
-  Curl_rlimit_block(&r, TRUE, ts);
-  fail_unless(Curl_rlimit_avail(&r, ts) == 0, "inf blocked to 0");
-  Curl_rlimit_drain(&r, 1000000, ts);
-  fail_unless(Curl_rlimit_avail(&r, ts) == 0, "blocked inf");
-  Curl_rlimit_block(&r, FALSE, ts);
-  fail_unless(Curl_rlimit_avail(&r, ts) == CURL_OFF_T_MAX,
+  Curl_rlimit_block(&r, TRUE, &ts);
+  fail_unless(Curl_rlimit_avail(&r, &ts) == 0, "inf blocked to 0");
+  Curl_rlimit_drain(&r, 1000000, &ts);
+  fail_unless(Curl_rlimit_avail(&r, &ts) == 0, "blocked inf");
+  Curl_rlimit_block(&r, FALSE, &ts);
+  fail_unless(Curl_rlimit_avail(&r, &ts) == CURL_OFF_T_MAX,
               "unblocked unlimited");
 
   /* A ratelimit that give 10 tokens per second */
   ts = curlx_now();
-  Curl_rlimit_init(&r, 10, 0, ts);
-  fail_unless(Curl_rlimit_avail(&r, ts) == 10, "initial 10");
-  Curl_rlimit_drain(&r, 5, ts);
-  fail_unless(Curl_rlimit_avail(&r, ts) == 5, "drain to 5");
-  Curl_rlimit_drain(&r, 3, ts);
-  fail_unless(Curl_rlimit_avail(&r, ts) == 2, "drain to 2");
+  Curl_rlimit_init(&r, 10, 0, &ts);
+  fail_unless(Curl_rlimit_avail(&r, &ts) == 10, "initial 10");
+  Curl_rlimit_drain(&r, 5, &ts);
+  fail_unless(Curl_rlimit_avail(&r, &ts) == 5, "drain to 5");
+  Curl_rlimit_drain(&r, 3, &ts);
+  fail_unless(Curl_rlimit_avail(&r, &ts) == 2, "drain to 2");
   ts.tv_usec += 1000; /* 1ms */
-  Curl_rlimit_drain(&r, 3, ts);
-  fail_unless(Curl_rlimit_avail(&r, ts) == -1, "drain to -1");
-  fail_unless(Curl_rlimit_wait_ms(&r, ts) == 999, "wait 999ms");
+  Curl_rlimit_drain(&r, 3, &ts);
+  fail_unless(Curl_rlimit_avail(&r, &ts) == -1, "drain to -1");
+  fail_unless(Curl_rlimit_wait_ms(&r, &ts) == 999, "wait 999ms");
   ts.tv_usec += 1000; /* 1ms */
-  fail_unless(Curl_rlimit_wait_ms(&r, ts) == 998, "wait 998ms");
+  fail_unless(Curl_rlimit_wait_ms(&r, &ts) == 998, "wait 998ms");
   ts.tv_sec += 1;
-  fail_unless(Curl_rlimit_avail(&r, ts) == 9, "10 inc per sec");
+  fail_unless(Curl_rlimit_avail(&r, &ts) == 9, "10 inc per sec");
   ts.tv_sec += 1;
-  fail_unless(Curl_rlimit_avail(&r, ts) == 19, "10 inc per sec(2)");
+  fail_unless(Curl_rlimit_avail(&r, &ts) == 19, "10 inc per sec(2)");
 
-  Curl_rlimit_block(&r, TRUE, curlx_now());
-  fail_unless(Curl_rlimit_avail(&r, curlx_now()) == 0, "10 blocked to 0");
-  Curl_rlimit_block(&r, FALSE, curlx_now());
-  fail_unless(Curl_rlimit_avail(&r, curlx_now()) == 10, "unblocked 10");
+  ts = curlx_now();
+  Curl_rlimit_block(&r, TRUE, &ts);
+  fail_unless(Curl_rlimit_avail(&r, &ts) == 0, "10 blocked to 0");
+  Curl_rlimit_block(&r, FALSE, &ts);
+  fail_unless(Curl_rlimit_avail(&r, &ts) == 10, "unblocked 10");
 
   /* A ratelimit that give 10 tokens per second, max burst 15/s */
   ts = curlx_now();
-  Curl_rlimit_init(&r, 10, 15, ts);
-  fail_unless(Curl_rlimit_avail(&r, ts) == 10, "initial 10");
-  Curl_rlimit_drain(&r, 5, ts);
-  fail_unless(Curl_rlimit_avail(&r, ts) == 5, "drain to 5");
-  Curl_rlimit_drain(&r, 3, ts);
-  fail_unless(Curl_rlimit_avail(&r, ts) == 2, "drain to 2");
-  Curl_rlimit_drain(&r, 3, ts);
-  fail_unless(Curl_rlimit_avail(&r, ts) == -1, "drain to -1");
+  Curl_rlimit_init(&r, 10, 15, &ts);
+  fail_unless(Curl_rlimit_avail(&r, &ts) == 10, "initial 10");
+  Curl_rlimit_drain(&r, 5, &ts);
+  fail_unless(Curl_rlimit_avail(&r, &ts) == 5, "drain to 5");
+  Curl_rlimit_drain(&r, 3, &ts);
+  fail_unless(Curl_rlimit_avail(&r, &ts) == 2, "drain to 2");
+  Curl_rlimit_drain(&r, 3, &ts);
+  fail_unless(Curl_rlimit_avail(&r, &ts) == -1, "drain to -1");
   ts.tv_sec += 1;
-  fail_unless(Curl_rlimit_avail(&r, ts) == 9, "10 inc per sec");
+  fail_unless(Curl_rlimit_avail(&r, &ts) == 9, "10 inc per sec");
   ts.tv_sec += 1;
-  fail_unless(Curl_rlimit_avail(&r, ts) == 15, "10/15 burst limit");
+  fail_unless(Curl_rlimit_avail(&r, &ts) == 15, "10/15 burst limit");
   ts.tv_sec += 1;
-  fail_unless(Curl_rlimit_avail(&r, ts) == 15, "10/15 burst limit(2)");
-  Curl_rlimit_drain(&r, 15, ts);
-  fail_unless(Curl_rlimit_avail(&r, ts) == 0, "drain to 0");
-  fail_unless(Curl_rlimit_wait_ms(&r, ts) == 1000, "wait 1 sec");
+  fail_unless(Curl_rlimit_avail(&r, &ts) == 15, "10/15 burst limit(2)");
+  Curl_rlimit_drain(&r, 15, &ts);
+  fail_unless(Curl_rlimit_avail(&r, &ts) == 0, "drain to 0");
+  fail_unless(Curl_rlimit_wait_ms(&r, &ts) == 1000, "wait 1 sec");
   ts.tv_usec += 500000; /* half a sec, cheating on second carry */
-  fail_unless(Curl_rlimit_avail(&r, ts) == 0, "0 after 0.5 sec");
-  fail_unless(Curl_rlimit_wait_ms(&r, ts) == 500, "wait 0.5 sec");
+  fail_unless(Curl_rlimit_avail(&r, &ts) == 0, "0 after 0.5 sec");
+  fail_unless(Curl_rlimit_wait_ms(&r, &ts) == 500, "wait 0.5 sec");
   ts.tv_sec += 1;
-  fail_unless(Curl_rlimit_avail(&r, ts) == 10, "10 after 1.5 sec");
-  fail_unless(Curl_rlimit_wait_ms(&r, ts) == 0, "wait 0");
+  fail_unless(Curl_rlimit_avail(&r, &ts) == 10, "10 after 1.5 sec");
+  fail_unless(Curl_rlimit_wait_ms(&r, &ts) == 0, "wait 0");
   ts.tv_usec += 500000; /* half a sec, cheating on second carry */
-  fail_unless(Curl_rlimit_avail(&r, ts) == 15, "10 after 2 sec");
+  fail_unless(Curl_rlimit_avail(&r, &ts) == 15, "10 after 2 sec");
 
   UNITTEST_END_SIMPLE
 }