From 2de22a00c7adb81b4e5cbc90785e29b4b083c1ed Mon Sep 17 00:00:00 2001 From: Stefan Eissing Date: Mon, 15 Dec 2025 14:28:09 +0100 Subject: [PATCH] lib: keep timestamp in easy handle 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 --- docs/Makefile.am | 1 + docs/internals/TIME-KEEPING.md | 82 ++++++++++++++++++ lib/asyn-ares.c | 13 ++- lib/asyn-thrdd.c | 6 +- lib/cf-h1-proxy.c | 2 +- lib/cf-h2-proxy.c | 2 +- lib/cf-https-connect.c | 12 ++- lib/cf-ip-happy.c | 28 +++--- lib/cf-socket.c | 28 +++--- lib/cfilters.c | 10 +-- lib/conncache.c | 49 +++++------ lib/conncache.h | 2 +- lib/connect.c | 48 +++------- lib/connect.h | 12 +-- lib/cshutdn.c | 15 ++-- lib/curl_trc.c | 3 +- lib/doh.c | 2 +- lib/easy.c | 4 +- lib/file.c | 5 +- lib/ftp.c | 6 +- lib/gopher.c | 2 +- lib/hostip.c | 12 ++- lib/http.c | 4 +- lib/http2.c | 8 +- lib/imap.c | 2 +- lib/mqtt.c | 10 +-- lib/multi.c | 154 ++++++++++++++++++--------------- lib/multi_ev.c | 11 ++- lib/multi_ev.h | 3 +- lib/multiif.h | 4 +- lib/pingpong.c | 20 +++-- lib/pingpong.h | 2 +- lib/pop3.c | 2 +- lib/progress.c | 60 +++++++++---- lib/progress.h | 16 +++- lib/ratelimit.c | 37 ++++---- lib/ratelimit.h | 12 +-- lib/request.c | 4 +- lib/sendf.c | 15 ++-- lib/setopt.c | 6 +- lib/smb.c | 2 +- lib/smtp.c | 2 +- lib/socks.c | 2 +- lib/telnet.c | 19 ++-- lib/tftp.c | 6 +- lib/transfer.c | 17 ++-- lib/transfer.h | 2 +- lib/url.c | 19 ++-- lib/url.h | 3 +- lib/urldata.h | 1 + lib/vquic/curl_ngtcp2.c | 58 ++++++++----- lib/vquic/curl_osslq.c | 26 +++--- lib/vquic/curl_quiche.c | 18 ++-- lib/vquic/vquic.c | 18 +++- lib/vquic/vquic_int.h | 9 +- lib/vssh/libssh.c | 2 +- lib/vssh/libssh2.c | 8 +- lib/vtls/gtls.c | 5 +- lib/vtls/openssl.c | 5 +- lib/vtls/schannel.c | 4 +- lib/vtls/vtls.c | 10 ++- lib/vtls/wolfssl.c | 5 +- lib/ws.c | 2 +- tests/data/test2500 | 2 +- tests/unit/unit1303.c | 3 +- tests/unit/unit1399.c | 1 + tests/unit/unit3216.c | 93 ++++++++++---------- 67 files changed, 598 insertions(+), 458 deletions(-) create mode 100644 docs/internals/TIME-KEEPING.md diff --git a/docs/Makefile.am b/docs/Makefile.am index 0b619b6008..c0c25232b8 100644 --- a/docs/Makefile.am +++ b/docs/Makefile.am @@ -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 index 0000000000..fc0020d6a4 --- /dev/null +++ b/docs/internals/TIME-KEEPING.md @@ -0,0 +1,82 @@ + + +# 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. diff --git a/lib/asyn-ares.c b/lib/asyn-ares.c index 40ce00db22..81b7090f22 100644 --- a/lib/asyn-ares.c +++ b/lib/asyn-ares.c @@ -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); } } diff --git a/lib/asyn-thrdd.c b/lib/asyn-thrdd.c index aa686271a0..408f05844d 100644 --- a/lib/asyn-thrdd.c +++ b/lib/asyn-thrdd.c @@ -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) diff --git a/lib/cf-h1-proxy.c b/lib/cf-h1-proxy.c index 19c63a12b0..5d290366b2 100644 --- a/lib/cf-h1-proxy.c +++ b/lib/cf-h1-proxy.c @@ -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; diff --git a/lib/cf-h2-proxy.c b/lib/cf-h2-proxy.c index 5a58c280e9..37af4e19d7 100644 --- a/lib/cf-h2-proxy.c +++ b/lib/cf-h2-proxy.c @@ -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; diff --git a/lib/cf-https-connect.c b/lib/cf-https-connect.c index 47555fed94..104df248eb 100644 --- a/lib/cf-https-connect.c +++ b/lib/cf-https-connect.c @@ -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); } diff --git a/lib/cf-ip-happy.c b/lib/cf-ip-happy.c index d31c6b6caa..029746354c 100644 --- a/lib/cf-ip-happy.c +++ b/lib/cf-ip-happy.c @@ -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); diff --git a/lib/cf-socket.c b/lib/cf-socket.c index 469e9229fd..f9a883b0c3 100644 --- a/lib/cf-socket.c +++ b/lib/cf-socket.c @@ -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 diff --git a/lib/cfilters.c b/lib/cfilters.c index acffae3f00..0c259e4439 100644 --- a/lib/cfilters.c +++ b/lib/cfilters.c @@ -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; diff --git a/lib/conncache.c b/lib/conncache.c index 54248d14ad..458d85d110 100644 --- a/lib/conncache.c +++ b/lib/conncache.c @@ -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; } diff --git a/lib/conncache.h b/lib/conncache.h index 5761ee9b44..b1a8aeaf09 100644 --- a/lib/conncache.h +++ b/lib/conncache.h @@ -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, diff --git a/lib/connect.c b/lib/connect.c index 748cf53a29..f1700e5b96 100644 --- a/lib/connect.c +++ b/lib/connect.c @@ -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; } diff --git a/lib/connect.h b/lib/connect.h index 7d1c9c9089..09776e636f 100644 --- a/lib/connect.h +++ b/lib/connect.h @@ -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); diff --git a/lib/cshutdn.c b/lib/cshutdn.c index 26c8f9c0dd..21ab1209eb 100644 --- a/lib/cshutdn.c +++ b/lib/cshutdn.c @@ -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"); diff --git a/lib/curl_trc.c b/lib/curl_trc.c index 169f7a7d6d..8308f49c5d 100644 --- a/lib/curl_trc.c +++ b/lib/curl_trc.c @@ -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)); } } } diff --git a/lib/doh.c b/lib/doh.c index b0f19be73a..4990194506 100644 --- 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; diff --git a/lib/easy.c b/lib/easy.c index 138462af87..8f11c8f58d 100644 --- a/lib/easy.c +++ b/lib/easy.c @@ -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; } } diff --git a/lib/file.c b/lib/file.c index 0f96957126..39642c9340 100644 --- a/lib/file.c +++ b/lib/file.c @@ -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); } diff --git a/lib/ftp.c b/lib/ftp.c index 4e528259c9..4dedde03aa 100644 --- 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) diff --git a/lib/gopher.c b/lib/gopher.c index 93e9287c99..f7a61ae08c 100644 --- a/lib/gopher.c +++ b/lib/gopher.c @@ -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; diff --git a/lib/hostip.c b/lib/hostip.c index 4f4122d6ae..eb59193d2c 100644 --- a/lib/hostip.c +++ b/lib/hostip.c @@ -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 */ diff --git a/lib/http.c b/lib/http.c index c80deb319e..d65cba95db 100644 --- a/lib/http.c +++ b/lib/http.c @@ -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; diff --git a/lib/http2.c b/lib/http2.c index 83b9280b2a..d02b5db322 100644 --- a/lib/http2.c +++ b/lib/http2.c @@ -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); diff --git a/lib/imap.c b/lib/imap.c index 05d195ec58..5bb2474b33 100644 --- a/lib/imap.c +++ b/lib/imap.c @@ -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; diff --git a/lib/mqtt.c b/lib/mqtt.c index df5f37775f..bd6d25331d 100644 --- a/lib/mqtt.c +++ b/lib/mqtt.c @@ -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; diff --git a/lib/multi.c b/lib/multi.c index 760f03186d..5aa39e1053 100644 --- a/lib/multi.c +++ b/lib/multi.c @@ -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); } /* diff --git a/lib/multi_ev.c b/lib/multi_ev.c index 40b49da85d..8bd7b29160 100644 --- a/lib/multi_ev.c +++ b/lib/multi_ev.c @@ -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; diff --git a/lib/multi_ev.h b/lib/multi_ev.h index 4e5b2a454d..d9794ed865 100644 --- a/lib/multi_ev.h +++ b/lib/multi_ev.h @@ -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, diff --git a/lib/multiif.h b/lib/multiif.h index 9db5b01000..42651c5150 100644 --- a/lib/multiif.h +++ b/lib/multiif.h @@ -30,11 +30,11 @@ 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); diff --git a/lib/pingpong.c b/lib/pingpong.c index 36acc34a36..fcfd991d81 100644 --- a/lib/pingpong.c +++ b/lib/pingpong.c @@ -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; } diff --git a/lib/pingpong.h b/lib/pingpong.h index 5492453d84..a41d76308c 100644 --- a/lib/pingpong.h +++ b/lib/pingpong.h @@ -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 */ diff --git a/lib/pop3.c b/lib/pop3.c index 0ddff3e45e..1fd22116c5 100644 --- a/lib/pop3.c +++ b/lib/pop3.c @@ -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); diff --git a/lib/progress.c b/lib/progress.c index 1ae8b5801d..e11a1ff82f 100644 --- a/lib/progress.c +++ b/lib/progress.c @@ -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) { - data->progress.dl.cur_size = size; + 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) +{ + 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); } diff --git a/lib/progress.h b/lib/progress.h index 5535c69cbf..2154ff60bd 100644 --- a/lib/progress.h +++ b/lib/progress.h @@ -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 diff --git a/lib/ratelimit.c b/lib/ratelimit.c index 7e3592506a..5c2e005133 100644 --- a/lib/ratelimit.c +++ b/lib/ratelimit.c @@ -35,12 +35,13 @@ 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; diff --git a/lib/ratelimit.h b/lib/ratelimit.h index 53f8030391..b5a7c56fce 100644 --- a/lib/ratelimit.h +++ b/lib/ratelimit.h @@ -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 */ diff --git a/lib/request.c b/lib/request.c index fa83380878..03dde6ab6d 100644 --- a/lib/request.c +++ b/lib/request.c @@ -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); } } } diff --git a/lib/sendf.c b/lib/sendf.c index f4a816e231..c236d558e6 100644 --- a/lib/sendf.c +++ b/lib/sendf.c @@ -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", diff --git a/lib/setopt.c b/lib/setopt.c index 24d8fe8de0..627865dc31 100644 --- a/lib/setopt.c +++ b/lib/setopt.c @@ -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: /* diff --git a/lib/smb.c b/lib/smb.c index c52d4eca57..2b2e7acaa7 100644 --- 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 diff --git a/lib/smtp.c b/lib/smtp.c index fa821fe45d..f8cef899f3 100644 --- a/lib/smtp.c +++ b/lib/smtp.c @@ -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); diff --git a/lib/socks.c b/lib/socks.c index 4620850652..a03d26e5b9 100644 --- a/lib/socks.c +++ b/lib/socks.c @@ -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; diff --git a/lib/telnet.c b/lib/telnet.c index 3153a392f6..843a6e5b00 100644 --- a/lib/telnet.c +++ b/lib/telnet.c @@ -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; diff --git a/lib/tftp.c b/lib/tftp.c index 58e9696fb2..c39222d9c3 100644 --- a/lib/tftp.c +++ b/lib/tftp.c @@ -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; diff --git a/lib/transfer.c b/lib/transfer.c index bd6be821dc..5407ba4477 100644 --- a/lib/transfer.c +++ b/lib/transfer.c @@ -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); diff --git a/lib/transfer.h b/lib/transfer.h index 3da5e24451..78473b1f0d 100644 --- a/lib/transfer.h +++ b/lib/transfer.h @@ -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); diff --git a/lib/url.c b/lib/url.c index 98fc8ce44f..fe5c3f38da 100644 --- 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) { diff --git a/lib/url.h b/lib/url.h index 82d869c5ff..6c2f1e144c 100644 --- 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. diff --git a/lib/urldata.h b/lib/urldata.h index 20a0a1cd5d..062348233a 100644 --- a/lib/urldata.h +++ b/lib/urldata.h @@ -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; diff --git a/lib/vquic/curl_ngtcp2.c b/lib/vquic/curl_ngtcp2.c index b9eaa1e98e..881f06760d 100644 --- a/lib/vquic/curl_ngtcp2.c +++ b/lib/vquic/curl_ngtcp2.c @@ -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); diff --git a/lib/vquic/curl_osslq.c b/lib/vquic/curl_osslq.c index 4e6e325295..44a12526f0 100644 --- a/lib/vquic/curl_osslq.c +++ b/lib/vquic/curl_osslq.c @@ -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; } diff --git a/lib/vquic/curl_quiche.c b/lib/vquic/curl_quiche.c index feceaf6369..4ee12dc005 100644 --- a/lib/vquic/curl_quiche.c +++ b/lib/vquic/curl_quiche.c @@ -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, " diff --git a/lib/vquic/vquic.c b/lib/vquic/vquic.c index 88c1b72266..be35a96021 100644 --- a/lib/vquic/vquic.c +++ b/lib/vquic/vquic.c @@ -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, diff --git a/lib/vquic/vquic_int.h b/lib/vquic/vquic_int.h index cd8e8989d2..efe4e2c816 100644 --- a/lib/vquic/vquic_int.h +++ b/lib/vquic/vquic_int.h @@ -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, diff --git a/lib/vssh/libssh.c b/lib/vssh/libssh.c index cd3a7d6622..e03ad5a968 100644 --- a/lib/vssh/libssh.c +++ b/lib/vssh/libssh.c @@ -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; diff --git a/lib/vssh/libssh2.c b/lib/vssh/libssh2.c index 4437724fcf..ba2a9375d7 100644 --- a/lib/vssh/libssh2.c +++ b/lib/vssh/libssh2.c @@ -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; diff --git a/lib/vtls/gtls.c b/lib/vtls/gtls.c index a98ff4a620..bdea5e58e3 100644 --- a/lib/vtls/gtls.c +++ b/lib/vtls/gtls.c @@ -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) diff --git a/lib/vtls/openssl.c b/lib/vtls/openssl.c index 444fbc4e04..a7f169d641 100644 --- a/lib/vtls/openssl.c +++ b/lib/vtls/openssl.c @@ -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; diff --git a/lib/vtls/schannel.c b/lib/vtls/schannel.c index 6159d90ab5..8a6550d8bf 100644 --- a/lib/vtls/schannel.c +++ b/lib/vtls/schannel.c @@ -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 " diff --git a/lib/vtls/vtls.c b/lib/vtls/vtls.c index 696c7fbaf8..171e3731e8 100644 --- a/lib/vtls/vtls.c +++ b/lib/vtls/vtls.c @@ -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? */ diff --git a/lib/vtls/wolfssl.c b/lib/vtls/wolfssl.c index 0c47e9c2ee..8a845a6ba1 100644 --- a/lib/vtls/wolfssl.c +++ b/lib/vtls/wolfssl.c @@ -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; } diff --git a/lib/ws.c b/lib/ws.c index 38d4195879..bde13419e6 100644 --- 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; diff --git a/tests/data/test2500 b/tests/data/test2500 index f08e6f19fa..3c5b15cea5 100644 --- a/tests/data/test2500 +++ b/tests/data/test2500 @@ -72,7 +72,7 @@ via: 1.1 nghttpx s/^server: nghttpx.*\r?\n// -Allocations: 150 +Allocations: 155 Maximum allocated: 1800000 diff --git a/tests/unit/unit1303.c b/tests/unit/unit1303.c index 9bed1ebff6..1c3f0cc639 100644 --- a/tests/unit/unit1303.c +++ b/tests/unit/unit1303.c @@ -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); } diff --git a/tests/unit/unit1399.c b/tests/unit/unit1399.c index ca632eb2d6..e02bf08bd9 100644 --- a/tests/unit/unit1399.c +++ b/tests/unit/unit1399.c @@ -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; diff --git a/tests/unit/unit3216.c b/tests/unit/unit3216.c index 0f9d27492c..f65355f362 100644 --- a/tests/unit/unit3216.c +++ b/tests/unit/unit3216.c @@ -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 }