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
This commit is contained in:
Stefan Eissing
2025-12-15 14:28:09 +01:00
committed by Daniel Stenberg
parent 425a2aa1af
commit 2de22a00c7
67 changed files with 598 additions and 458 deletions

View File

@@ -68,6 +68,7 @@ INTERNALDOCS = \
internals/SCORECARD.md \
internals/SPLAY.md \
internals/STRPARSE.md \
internals/TIME-KEEPING.md \
internals/TLS-SESSIONS.md \
internals/UINT_SETS.md \
internals/WEBSOCKET.md

View File

@@ -0,0 +1,82 @@
<!--
Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
SPDX-License-Identifier: curl
-->
# Keeping Time
Transfers need the current time to handle timeouts and keep a record of events. The current
time function is `curlx_now()` and it uses a **monotonic** clock on most platforms. This
ensures that time only ever increases (the timestamps it gives are however not the "real"
world clock).
The simplest handling of transfer time would be to just always call `curlx_now()`. However
there is a performance penalty to that - varying by platform - so this is not a desirable
strategy. Processing thousands of transfers in a loop needs a smarter approach.
## Initial Approach (now historic)
The loop processing functions called `curlx_now()` at the beginning and then passed
a pointer to the `struct curltime now` to functions to save them the calls. Passing
this pointer down to all functions possibly involved was not done as this pollutes
the internal APIs.
So, some functions continued to call `curlx_now()` on their own while others used the
passed pointer *to a timestamp in the past*. This led to a transfer experiencing *jumps*
in time, reversing cause and effect. On fast systems, this was mostly not noticeable. On
slow machines or in CI, this led to rare and annoying test failures.
(Especially when we added assertions that the reported "timeline" of a transfer was
in the correct order: *queue -> nameloopup -> connect -> appconnect ->...*.)
## Revised Approach
The strategy of handling transfer's time is now:
* Keep a "now" timestamp in `data->progress.now`.
* Perform time checks and event recording using `data->progress.now`.
* Set `data->progress.now` at the start of API calls (e.g. `curl_multi_perform()`, etc.).
* Set `data->progress.now` when recorded events happen (for precision).
* Set `data->progress.now` on multi state changes.
* Set `data->progress.now` in `pingpong` timeout handling, since `pingpong` is old and not always non-blocking.
In addition to *setting* `data->progress.now` this timestamp can be *advanced* using 2 new methods:
* `Curl_pgrs_now_at_least(data, &now)`: code that has a "now" timestamp can progress the `data`'s own "now" to be at least as new. If `data->progress.now` is already newer, no change is done. A transfer never goes **back**.
* `Curl_pgrs_now_update(data1, data2)`: update the "now" in `data1` to be at least as new as the one in `data2`. If it already is newer, nothing changes.
### Time Advancing Loops
This advancing is used in the following way in loop like `curl_multi_perform()`:
```C
struct curltime now = curlx_now(); /* start of API call */
forall data in transfers {
Curl_pgrs_set_at_least(data, now);
progress(data); /* may update "now" */
now = data->progress.now;
}
```
Transfers that update their "now" pass that timestamp to the next transfer processed.
### Transfers triggering other transfers
In HTTP/2 and HTTP/3 processing, incoming data causes actions on transfers other than
the calling one. The protocols may receive data for any transfer on the connection and need
to dispatch it:
* a Close/Reset comes in for another transfer. That transfer is marked as "dirty", making sure it is processed in a timely manner.
* Response Data arrives: this data is written out to the client. Before this is done, the "now" timestamp is updated via `Curl_pgrs_now_update(data, calling)` from the "calling" transfer.
## Blocking Operations
We still have places in `libcurl` where we do blocking operations. We should always use `Curl_pgrs_now_set(data)` afterwards since we cannot be sure how much time has passed. Since loop processing passed an updated "now" to the next transfer, a delay due to blocking is passed on.
There are other places where we may lose track of time:
* Cache/Pool Locks: no "now" updates happen after a lock has been acquired. These locks should not be kept for a longer time.
* User Callbacks: no "now" updates happen after callbacks have been invoked. The expectation is that those do not take long.
Should these assumptions prove wrong, we need to add updates.

View File

@@ -311,7 +311,7 @@ CURLcode Curl_async_is_resolved(struct Curl_easy *data,
/* This is only set to non-zero if the timer was started. */
(ares->happy_eyeballs_dns_time.tv_sec ||
ares->happy_eyeballs_dns_time.tv_usec) &&
(curlx_timediff_ms(curlx_now(), ares->happy_eyeballs_dns_time) >=
(curlx_timediff_ms(data->progress.now, ares->happy_eyeballs_dns_time) >=
HAPPY_EYEBALLS_DNS_TIMEOUT)) {
/* Remember that the EXPIRE_HAPPY_EYEBALLS_DNS timer is no longer
running. */
@@ -390,12 +390,11 @@ CURLcode Curl_async_await(struct Curl_easy *data,
struct async_ares_ctx *ares = &data->state.async.ares;
CURLcode result = CURLE_OK;
timediff_t timeout_ms;
struct curltime now = curlx_now();
DEBUGASSERT(entry);
*entry = NULL; /* clear on entry */
timeout_ms = Curl_timeleft_ms(data, &now, TRUE);
timeout_ms = Curl_timeleft_ms(data, TRUE);
if(timeout_ms < 0) {
/* already expired! */
connclose(data->conn, "Timed out before name resolve started");
@@ -439,15 +438,15 @@ CURLcode Curl_async_await(struct Curl_easy *data,
if(Curl_pgrsUpdate(data))
result = CURLE_ABORTED_BY_CALLBACK;
else {
struct curltime now2 = curlx_now();
timediff_t elapsed_ms = curlx_timediff_ms(now2, now); /* spent time */
struct curltime now = curlx_now(); /* update in loop */
timediff_t elapsed_ms = curlx_timediff_ms(now, data->progress.now);
if(elapsed_ms <= 0)
timeout_ms -= 1; /* always deduct at least 1 */
else if(elapsed_ms > timeout_ms)
timeout_ms = -1;
else
timeout_ms -= elapsed_ms;
now = now2; /* for next loop */
Curl_pgrs_now_at_least(data, &now);
}
if(timeout_ms < 0)
result = CURLE_OPERATION_TIMEDOUT;
@@ -583,7 +582,7 @@ static void async_ares_hostbyname_cb(void *user_data,
timeout to prevent it. After all, we do not even know where in the
c-ares retry cycle each request is.
*/
ares->happy_eyeballs_dns_time = curlx_now();
ares->happy_eyeballs_dns_time = data->progress.now;
Curl_expire(data, HAPPY_EYEBALLS_DNS_TIMEOUT, EXPIRE_HAPPY_EYEBALLS_DNS);
}
}

View File

@@ -442,7 +442,7 @@ static bool async_thrdd_init(struct Curl_easy *data,
/* passing addr_ctx to the thread adds a reference */
addr_ctx->ref_count = 2;
addr_ctx->start = curlx_now();
addr_ctx->start = data->progress.now;
#ifdef HAVE_GETADDRINFO
addr_ctx->thread_hnd = Curl_thread_create(getaddrinfo_thread, addr_ctx);
@@ -655,7 +655,7 @@ CURLcode Curl_async_is_resolved(struct Curl_easy *data,
else {
/* poll for name lookup done with exponential backoff up to 250ms */
/* should be fine even if this converts to 32-bit */
timediff_t elapsed = curlx_timediff_ms(curlx_now(),
timediff_t elapsed = curlx_timediff_ms(data->progress.now,
data->progress.t_startsingle);
if(elapsed < 0)
elapsed = 0;
@@ -706,7 +706,7 @@ CURLcode Curl_async_pollset(struct Curl_easy *data, struct easy_pollset *ps)
result = Curl_pollset_add_in(data, ps, thrdd->addr->sock_pair[0]);
#else
timediff_t milli;
timediff_t ms = curlx_timediff_ms(curlx_now(), thrdd->addr->start);
timediff_t ms = curlx_timediff_ms(data->progress.now, thrdd->addr->start);
if(ms < 3)
milli = 0;
else if(ms <= 50)

View File

@@ -528,7 +528,7 @@ static CURLcode H1_CONNECT(struct Curl_cfilter *cf,
do {
if(Curl_timeleft_ms(data, NULL, TRUE) < 0) {
if(Curl_timeleft_ms(data, TRUE) < 0) {
failf(data, "Proxy CONNECT aborted due to timeout");
result = CURLE_OPERATION_TIMEDOUT;
goto out;

View File

@@ -1088,7 +1088,7 @@ static CURLcode cf_h2_proxy_connect(struct Curl_cfilter *cf,
}
DEBUGASSERT(ts->authority);
if(Curl_timeleft_ms(data, NULL, TRUE) < 0) {
if(Curl_timeleft_ms(data, TRUE) < 0) {
failf(data, "Proxy CONNECT aborted due to timeout");
result = CURLE_OPERATION_TIMEDOUT;
goto out;

View File

@@ -149,7 +149,7 @@ static void cf_hc_baller_init(struct cf_hc_baller *b,
struct Curl_cfilter *save = cf->next;
cf->next = NULL;
b->started = curlx_now();
b->started = data->progress.now;
switch(b->alpn_id) {
case ALPN_h3:
transport = TRNSPRT_QUIC;
@@ -212,11 +212,11 @@ static CURLcode baller_connected(struct Curl_cfilter *cf,
if(reply_ms >= 0)
CURL_TRC_CF(data, cf, "connect+handshake %s: %dms, 1st data: %dms",
winner->name,
(int)curlx_timediff_ms(curlx_now(),
(int)curlx_timediff_ms(data->progress.now,
winner->started), reply_ms);
else
CURL_TRC_CF(data, cf, "deferred handshake %s: %dms",
winner->name, (int)curlx_timediff_ms(curlx_now(),
winner->name, (int)curlx_timediff_ms(data->progress.now,
winner->started));
/* install the winning filter below this one. */
@@ -293,7 +293,6 @@ static CURLcode cf_hc_connect(struct Curl_cfilter *cf,
bool *done)
{
struct cf_hc_ctx *ctx = cf->ctx;
struct curltime now;
CURLcode result = CURLE_OK;
size_t i, failed_ballers;
@@ -303,14 +302,13 @@ static CURLcode cf_hc_connect(struct Curl_cfilter *cf,
}
*done = FALSE;
now = curlx_now();
switch(ctx->state) {
case CF_HC_INIT:
DEBUGASSERT(!cf->next);
for(i = 0; i < ctx->baller_count; i++)
DEBUGASSERT(!ctx->ballers[i].cf);
CURL_TRC_CF(data, cf, "connect, init");
ctx->started = now;
ctx->started = data->progress.now;
cf_hc_baller_init(&ctx->ballers[0], cf, data, ctx->ballers[0].transport);
if(ctx->baller_count > 1) {
Curl_expire(data, ctx->soft_eyeballs_timeout_ms, EXPIRE_ALPN_EYEBALLS);
@@ -329,7 +327,7 @@ static CURLcode cf_hc_connect(struct Curl_cfilter *cf,
}
}
if(time_to_start_next(cf, data, 1, now)) {
if(time_to_start_next(cf, data, 1, data->progress.now)) {
cf_hc_baller_init(&ctx->ballers[1], cf, data, ctx->ballers[1].transport);
}

View File

@@ -353,7 +353,6 @@ static CURLcode cf_ip_ballers_run(struct cf_ip_ballers *bs,
CURLcode result = CURLE_OK;
struct cf_ip_attempt *a = NULL, **panchor;
bool do_more;
struct curltime now;
timediff_t next_expire_ms;
int i, inconclusive, ongoing;
@@ -361,7 +360,6 @@ static CURLcode cf_ip_ballers_run(struct cf_ip_ballers *bs,
return CURLE_OK;
evaluate:
now = curlx_now();
ongoing = inconclusive = 0;
/* check if a running baller connects now */
@@ -398,7 +396,7 @@ evaluate:
/* no attempt connected yet, start another one? */
if(!ongoing) {
if(!bs->started.tv_sec && !bs->started.tv_usec)
bs->started = now;
bs->started = data->progress.now;
do_more = TRUE;
}
else {
@@ -408,8 +406,8 @@ evaluate:
more_possible = cf_ai_iter_has_more(&bs->ipv6_iter);
#endif
do_more = more_possible &&
(curlx_timediff_ms(now, bs->last_attempt_started) >=
bs->attempt_delay_ms);
(curlx_timediff_ms(data->progress.now, bs->last_attempt_started) >=
bs->attempt_delay_ms);
if(do_more)
CURL_TRC_CF(data, cf, "happy eyeballs timeout expired, "
"start next attempt");
@@ -447,7 +445,7 @@ evaluate:
while(*panchor)
panchor = &((*panchor)->next);
*panchor = a;
bs->last_attempt_started = now;
bs->last_attempt_started = data->progress.now;
bs->last_attempt_ai_family = ai_family;
/* and run everything again */
goto evaluate;
@@ -455,7 +453,8 @@ evaluate:
else if(inconclusive) {
/* tried all addresses, no success but some where inconclusive.
* Let's restart the inconclusive ones. */
timediff_t since_ms = curlx_timediff_ms(now, bs->last_attempt_started);
timediff_t since_ms =
curlx_timediff_ms(data->progress.now, bs->last_attempt_started);
timediff_t delay_ms = bs->attempt_delay_ms - since_ms;
if(delay_ms <= 0) {
CURL_TRC_CF(data, cf, "all attempts inconclusive, restarting one");
@@ -468,7 +467,7 @@ evaluate:
CURL_TRC_CF(data, cf, "restarted baller %d -> %d", i, result);
if(result) /* serious failure */
goto out;
bs->last_attempt_started = now;
bs->last_attempt_started = data->progress.now;
goto evaluate;
}
DEBUGASSERT(0); /* should not come here */
@@ -500,10 +499,10 @@ out:
bool more_possible;
/* when do we need to be called again? */
next_expire_ms = Curl_timeleft_ms(data, &now, TRUE);
next_expire_ms = Curl_timeleft_ms(data, TRUE);
if(next_expire_ms <= 0) {
failf(data, "Connection timeout after %" FMT_OFF_T " ms",
curlx_timediff_ms(now, data->progress.t_startsingle));
curlx_timediff_ms(data->progress.now, data->progress.t_startsingle));
return CURLE_OPERATION_TIMEDOUT;
}
@@ -514,7 +513,8 @@ out:
#endif
if(more_possible) {
timediff_t expire_ms, elapsed_ms;
elapsed_ms = curlx_timediff_ms(now, bs->last_attempt_started);
elapsed_ms =
curlx_timediff_ms(data->progress.now, bs->last_attempt_started);
expire_ms = CURLMAX(bs->attempt_delay_ms - elapsed_ms, 0);
next_expire_ms = CURLMIN(next_expire_ms, expire_ms);
if(next_expire_ms <= 0) {
@@ -675,7 +675,7 @@ static CURLcode is_connected(struct Curl_cfilter *cf,
proxy_name ? "via " : "",
proxy_name ? proxy_name : "",
proxy_name ? " " : "",
curlx_timediff_ms(curlx_now(), data->progress.t_startsingle),
curlx_timediff_ms(data->progress.now, data->progress.t_startsingle),
curl_easy_strerror(result));
}
@@ -700,14 +700,14 @@ static CURLcode start_connect(struct Curl_cfilter *cf,
if(!dns)
return CURLE_FAILED_INIT;
if(Curl_timeleft_ms(data, NULL, TRUE) < 0) {
if(Curl_timeleft_ms(data, TRUE) < 0) {
/* a precaution, no need to continue if time already is up */
failf(data, "Connection time-out");
return CURLE_OPERATION_TIMEDOUT;
}
CURL_TRC_CF(data, cf, "init ip ballers for transport %u", ctx->transport);
ctx->started = curlx_now();
ctx->started = data->progress.now;
return cf_ip_ballers_init(&ctx->ballers, cf->conn->ip_version,
dns->addr, ctx->cf_create, ctx->transport,
data->set.happy_eyeballs_timeout);

View File

@@ -1074,7 +1074,7 @@ static CURLcode cf_socket_open(struct Curl_cfilter *cf,
(void)data;
DEBUGASSERT(ctx->sock == CURL_SOCKET_BAD);
ctx->started_at = curlx_now();
ctx->started_at = data->progress.now;
#ifdef SOCK_NONBLOCK
/* Do not tuck SOCK_NONBLOCK into socktype when opensocket callback is set
* because we would not know how socketype is about to be used in the
@@ -1201,7 +1201,7 @@ out:
}
else if(isconnected) {
set_local_ip(cf, data);
ctx->connected_at = curlx_now();
ctx->connected_at = data->progress.now;
cf->connected = TRUE;
}
CURL_TRC_CF(data, cf, "cf_socket_open() -> %d, fd=%" FMT_SOCKET_T,
@@ -1320,7 +1320,7 @@ static CURLcode cf_tcp_connect(struct Curl_cfilter *cf,
else if(rc == CURL_CSELECT_OUT || cf->conn->bits.tcp_fastopen) {
if(verifyconnect(ctx->sock, &ctx->error)) {
/* we are connected with TCP, awesome! */
ctx->connected_at = curlx_now();
ctx->connected_at = data->progress.now;
set_local_ip(cf, data);
*done = TRUE;
cf->connected = TRUE;
@@ -1396,13 +1396,13 @@ static CURLcode cf_socket_adjust_pollset(struct Curl_cfilter *cf,
#define SIO_IDEAL_SEND_BACKLOG_QUERY 0x4004747B
#endif
static void win_update_sndbuf_size(struct cf_socket_ctx *ctx)
static void win_update_sndbuf_size(struct Curl_easy *data,
struct cf_socket_ctx *ctx)
{
ULONG ideal;
DWORD ideallen;
struct curltime n = curlx_now();
if(curlx_timediff_ms(n, ctx->last_sndbuf_query_at) > 1000) {
if(curlx_timediff_ms(data->progress.now, ctx->last_sndbuf_query_at) > 1000) {
if(!WSAIoctl(ctx->sock, SIO_IDEAL_SEND_BACKLOG_QUERY, 0, 0,
&ideal, sizeof(ideal), &ideallen, 0, 0) &&
ideal != ctx->sndbuf_size &&
@@ -1410,7 +1410,7 @@ static void win_update_sndbuf_size(struct cf_socket_ctx *ctx)
(const char *)&ideal, sizeof(ideal))) {
ctx->sndbuf_size = ideal;
}
ctx->last_sndbuf_query_at = n;
ctx->last_sndbuf_query_at = data->progress.now;
}
}
@@ -1491,7 +1491,7 @@ static CURLcode cf_socket_send(struct Curl_cfilter *cf, struct Curl_easy *data,
#ifdef USE_WINSOCK
if(!result)
win_update_sndbuf_size(ctx);
win_update_sndbuf_size(data, ctx);
#endif
CURL_TRC_CF(data, cf, "send(len=%zu) -> %d, %zu",
@@ -1557,7 +1557,7 @@ static CURLcode cf_socket_recv(struct Curl_cfilter *cf, struct Curl_easy *data,
CURL_TRC_CF(data, cf, "recv(len=%zu) -> %d, %zu", len, result, *pnread);
if(!result && !ctx->got_first_byte) {
ctx->first_byte_at = curlx_now();
ctx->first_byte_at = data->progress.now;
ctx->got_first_byte = TRUE;
}
return result;
@@ -1990,23 +1990,21 @@ static timediff_t cf_tcp_accept_timeleft(struct Curl_cfilter *cf,
struct cf_socket_ctx *ctx = cf->ctx;
timediff_t timeout_ms = DEFAULT_ACCEPT_TIMEOUT;
timediff_t other_ms;
struct curltime now;
#ifndef CURL_DISABLE_FTP
if(data->set.accepttimeout > 0)
timeout_ms = data->set.accepttimeout;
#endif
now = curlx_now();
/* check if the generic timeout possibly is set shorter */
other_ms = Curl_timeleft_ms(data, &now, FALSE);
other_ms = Curl_timeleft_ms(data, FALSE);
if(other_ms && (other_ms < timeout_ms))
/* note that this also works fine for when other_ms happens to be negative
due to it already having elapsed */
timeout_ms = other_ms;
else {
/* subtract elapsed time */
timeout_ms -= curlx_timediff_ms(now, ctx->started_at);
timeout_ms -= curlx_timediff_ms(data->progress.now, ctx->started_at);
if(!timeout_ms)
/* avoid returning 0 as that means no timeout! */
timeout_ms = -1;
@@ -2142,7 +2140,7 @@ static CURLcode cf_tcp_accept_connect(struct Curl_cfilter *cf,
cf_tcp_set_accepted_remote_ip(cf, data);
set_local_ip(cf, data);
ctx->active = TRUE;
ctx->connected_at = curlx_now();
ctx->connected_at = data->progress.now;
cf->connected = TRUE;
CURL_TRC_CF(data, cf, "accepted_set(sock=%" FMT_SOCKET_T
", remote=%s port=%d)",
@@ -2208,7 +2206,7 @@ CURLcode Curl_conn_tcp_listen_set(struct Curl_easy *data,
goto out;
Curl_conn_cf_add(data, conn, sockindex, cf);
ctx->started_at = curlx_now();
ctx->started_at = data->progress.now;
conn->sock[sockindex] = ctx->sock;
set_local_ip(cf, data);
CURL_TRC_CF(data, cf, "set filter for listen socket fd=%" FMT_SOCKET_T

View File

@@ -169,7 +169,6 @@ CURLcode Curl_conn_shutdown(struct Curl_easy *data, int sockindex, bool *done)
struct Curl_cfilter *cf;
CURLcode result = CURLE_OK;
timediff_t timeout_ms;
struct curltime now;
DEBUGASSERT(data->conn);
@@ -187,14 +186,13 @@ CURLcode Curl_conn_shutdown(struct Curl_easy *data, int sockindex, bool *done)
}
*done = FALSE;
now = curlx_now();
if(!Curl_shutdown_started(data, sockindex)) {
CURL_TRC_M(data, "shutdown start on%s connection",
sockindex ? " secondary" : "");
Curl_shutdown_start(data, sockindex, 0, &now);
Curl_shutdown_start(data, sockindex, 0);
}
else {
timeout_ms = Curl_shutdown_timeleft(data->conn, sockindex, &now);
timeout_ms = Curl_shutdown_timeleft(data, data->conn, sockindex);
if(timeout_ms < 0) {
/* info message, since this might be regarded as acceptable */
infof(data, "shutdown timeout");
@@ -504,7 +502,7 @@ CURLcode Curl_conn_connect(struct Curl_easy *data,
* socket and ip related information. */
cf_cntrl_update_info(data, data->conn);
conn_report_connect_stats(cf, data);
data->conn->keepalive = curlx_now();
data->conn->keepalive = data->progress.now;
#ifndef CURL_DISABLE_VERBOSE_STRINGS
result = cf_verboseconnect(data, cf);
#endif
@@ -520,7 +518,7 @@ CURLcode Curl_conn_connect(struct Curl_easy *data,
goto out;
else {
/* check allowed time left */
const timediff_t timeout_ms = Curl_timeleft_ms(data, NULL, TRUE);
const timediff_t timeout_ms = Curl_timeleft_ms(data, TRUE);
curl_socket_t sockfd = Curl_conn_cf_get_socket(cf, data);
int rc;

View File

@@ -208,6 +208,7 @@ void Curl_cpool_destroy(struct cpool *cpool)
cpool->share ? "[SHARE] " : "", cpool->num_conn);
/* Move all connections to the shutdown list */
sigpipe_init(&pipe_st);
Curl_pgrs_now_set(cpool->idata);
CPOOL_LOCK(cpool, cpool->idata);
conn = cpool_get_first(cpool);
while(conn) {
@@ -275,23 +276,22 @@ static struct cpool_bundle *cpool_add_bundle(struct cpool *cpool,
}
static struct connectdata *
cpool_bundle_get_oldest_idle(struct cpool_bundle *bundle)
cpool_bundle_get_oldest_idle(struct cpool_bundle *bundle,
struct curltime *pnow)
{
struct Curl_llist_node *curr;
timediff_t highscore = -1;
timediff_t score;
struct curltime now;
struct connectdata *oldest_idle = NULL;
struct connectdata *conn;
now = curlx_now();
curr = Curl_llist_head(&bundle->conns);
while(curr) {
conn = Curl_node_elem(curr);
if(!CONN_INUSE(conn)) {
/* Set higher score for the age passed since the connection was used */
score = curlx_timediff_ms(now, conn->lastused);
score = curlx_timediff_ms(*pnow, conn->lastused);
if(score > highscore) {
highscore = score;
@@ -303,18 +303,17 @@ cpool_bundle_get_oldest_idle(struct cpool_bundle *bundle)
return oldest_idle;
}
static struct connectdata *cpool_get_oldest_idle(struct cpool *cpool)
static struct connectdata *cpool_get_oldest_idle(struct cpool *cpool,
struct curltime *pnow)
{
struct Curl_hash_iterator iter;
struct Curl_llist_node *curr;
struct Curl_hash_element *he;
struct connectdata *oldest_idle = NULL;
struct cpool_bundle *bundle;
struct curltime now;
timediff_t highscore = -1;
timediff_t score;
now = curlx_now();
Curl_hash_start_iterate(&cpool->dest2bundle, &iter);
for(he = Curl_hash_next_element(&iter); he;
@@ -328,7 +327,7 @@ static struct connectdata *cpool_get_oldest_idle(struct cpool *cpool)
if(CONN_INUSE(conn) || conn->bits.close || conn->connect_only)
continue;
/* Set higher score for the age passed since the connection was used */
score = curlx_timediff_ms(now, conn->lastused);
score = curlx_timediff_ms(*pnow, conn->lastused);
if(score > highscore) {
highscore = score;
oldest_idle = conn;
@@ -359,6 +358,7 @@ int Curl_cpool_check_limits(struct Curl_easy *data,
if(!dest_limit && !total_limit)
return CPOOL_LIMIT_OK;
Curl_pgrs_now_update(cpool->idata, data);
CPOOL_LOCK(cpool, cpool->idata);
if(dest_limit) {
size_t live;
@@ -378,7 +378,8 @@ int Curl_cpool_check_limits(struct Curl_easy *data,
struct connectdata *oldest_idle = NULL;
/* The bundle is full. Extract the oldest connection that may
* be removed now, if there is one. */
oldest_idle = cpool_bundle_get_oldest_idle(bundle);
oldest_idle = cpool_bundle_get_oldest_idle(bundle,
&data->progress.now);
if(!oldest_idle)
break;
/* disconnect the old conn and continue */
@@ -409,7 +410,8 @@ int Curl_cpool_check_limits(struct Curl_easy *data,
break;
}
else {
struct connectdata *oldest_idle = cpool_get_oldest_idle(cpool);
struct connectdata *oldest_idle =
cpool_get_oldest_idle(cpool, &data->progress.now);
if(!oldest_idle)
break;
/* disconnect the old conn and continue */
@@ -429,6 +431,7 @@ int Curl_cpool_check_limits(struct Curl_easy *data,
out:
CPOOL_UNLOCK(cpool, cpool->idata);
Curl_pgrs_now_update(data, cpool->idata);
return result;
}
@@ -538,7 +541,7 @@ bool Curl_cpool_conn_now_idle(struct Curl_easy *data,
maxconnects = data->multi->maxconnects;
}
conn->lastused = curlx_now(); /* it was used up until now */
conn->lastused = data->progress.now; /* it was used up until now */
if(cpool && maxconnects) {
/* may be called form a callback already under lock */
bool do_lock = !CPOOL_IS_LOCKED(cpool);
@@ -548,7 +551,7 @@ bool Curl_cpool_conn_now_idle(struct Curl_easy *data,
infof(data, "Connection pool is full, closing the oldest of %zu/%u",
cpool->num_conn, maxconnects);
oldest_idle = cpool_get_oldest_idle(cpool);
oldest_idle = cpool_get_oldest_idle(cpool, &data->progress.now);
kept = (oldest_idle != conn);
if(oldest_idle) {
Curl_conn_terminate(data, oldest_idle, FALSE);
@@ -635,6 +638,7 @@ static void cpool_discard_conn(struct cpool *cpool,
* If we do a shutdown for an aborted transfer, the server might think
* it was successful otherwise (for example an ftps: upload). This is
* not what we want. */
Curl_pgrs_now_update(cpool->idata, data);
if(aborted)
done = TRUE;
if(!done) {
@@ -700,16 +704,12 @@ void Curl_conn_terminate(struct Curl_easy *data,
CPOOL_UNLOCK(cpool, data);
}
struct cpool_reaper_ctx {
struct curltime now;
};
static int cpool_reap_dead_cb(struct Curl_easy *data,
struct connectdata *conn, void *param)
{
struct cpool_reaper_ctx *rctx = param;
(void)param;
if((!CONN_INUSE(conn) && conn->bits.no_reuse) ||
Curl_conn_seems_dead(conn, data, &rctx->now)) {
Curl_conn_seems_dead(conn, data)) {
/* stop the iteration here, pass back the connection that was pruned */
Curl_conn_terminate(data, conn, FALSE);
return 1;
@@ -727,20 +727,18 @@ static int cpool_reap_dead_cb(struct Curl_easy *data,
void Curl_cpool_prune_dead(struct Curl_easy *data)
{
struct cpool *cpool = cpool_get_instance(data);
struct cpool_reaper_ctx rctx;
timediff_t elapsed;
if(!cpool)
return;
rctx.now = curlx_now();
CPOOL_LOCK(cpool, data);
elapsed = curlx_timediff_ms(rctx.now, cpool->last_cleanup);
elapsed = curlx_timediff_ms(data->progress.now, cpool->last_cleanup);
if(elapsed >= 1000L) {
while(cpool_foreach(data, cpool, &rctx, cpool_reap_dead_cb))
while(cpool_foreach(data, cpool, NULL, cpool_reap_dead_cb))
;
cpool->last_cleanup = rctx.now;
cpool->last_cleanup = data->progress.now;
}
CPOOL_UNLOCK(cpool, data);
}
@@ -754,16 +752,15 @@ static int conn_upkeep(struct Curl_easy *data,
return 0; /* continue iteration */
}
CURLcode Curl_cpool_upkeep(void *data)
CURLcode Curl_cpool_upkeep(struct Curl_easy *data)
{
struct cpool *cpool = cpool_get_instance(data);
struct curltime now = curlx_now();
if(!cpool)
return CURLE_OK;
CPOOL_LOCK(cpool, data);
cpool_foreach(data, cpool, &now, conn_upkeep);
cpool_foreach(data, cpool, &data->progress.now, conn_upkeep);
CPOOL_UNLOCK(cpool, data);
return CURLE_OK;
}

View File

@@ -139,7 +139,7 @@ void Curl_cpool_prune_dead(struct Curl_easy *data);
/**
* Perform upkeep actions on connections in the transfer's pool.
*/
CURLcode Curl_cpool_upkeep(void *data);
CURLcode Curl_cpool_upkeep(struct Curl_easy *data);
typedef void Curl_cpool_conn_do_cb(struct connectdata *conn,
struct Curl_easy *data,

View File

@@ -109,18 +109,15 @@ enum alpnid Curl_str2alpnid(const struct Curl_str *cstr)
* infinite time left). If the value is negative, the timeout time has already
* elapsed.
* @param data the transfer to check on
* @param nowp timestamp to use for calculation, NULL to use curlx_now()
* @param duringconnect TRUE iff connect timeout is also taken into account.
* @unittest: 1303
*/
timediff_t Curl_timeleft_ms(struct Curl_easy *data,
struct curltime *nowp,
bool duringconnect)
{
timediff_t timeleft_ms = 0;
timediff_t ctimeleft_ms = 0;
timediff_t ctimeout_ms;
struct curltime now;
/* The duration of a connect and the total transfer are calculated from two
different time-stamps. It can end up with the total timeout being reached
@@ -130,14 +127,9 @@ timediff_t Curl_timeleft_ms(struct Curl_easy *data,
if((!data->set.timeout || data->set.connect_only) && !duringconnect)
return 0; /* no timeout in place or checked, return "no limit" */
if(!nowp) {
now = curlx_now();
nowp = &now;
}
if(data->set.timeout) {
timeleft_ms = data->set.timeout -
curlx_timediff_ms(*nowp, data->progress.t_startop);
curlx_timediff_ms(data->progress.now, data->progress.t_startop);
if(!timeleft_ms)
timeleft_ms = -1; /* 0 is "no limit", fake 1 ms expiry */
}
@@ -147,7 +139,7 @@ timediff_t Curl_timeleft_ms(struct Curl_easy *data,
ctimeout_ms = (data->set.connecttimeout > 0) ?
data->set.connecttimeout : DEFAULT_CONNECT_TIMEOUT;
ctimeleft_ms = ctimeout_ms -
curlx_timediff_ms(*nowp, data->progress.t_startsingle);
curlx_timediff_ms(data->progress.now, data->progress.t_startsingle);
if(!ctimeleft_ms)
ctimeleft_ms = -1; /* 0 is "no limit", fake 1 ms expiry */
if(!timeleft_ms)
@@ -158,61 +150,47 @@ timediff_t Curl_timeleft_ms(struct Curl_easy *data,
}
void Curl_shutdown_start(struct Curl_easy *data, int sockindex,
int timeout_ms, struct curltime *nowp)
int timeout_ms)
{
struct curltime now;
struct connectdata *conn = data->conn;
DEBUGASSERT(conn);
if(!nowp) {
now = curlx_now();
nowp = &now;
}
conn->shutdown.start[sockindex] = *nowp;
conn->shutdown.start[sockindex] = data->progress.now;
conn->shutdown.timeout_ms = (timeout_ms > 0) ?
(timediff_t)timeout_ms :
((data->set.shutdowntimeout > 0) ?
data->set.shutdowntimeout : DEFAULT_SHUTDOWN_TIMEOUT_MS);
/* Set a timer, unless we operate on the admin handle */
if(data->mid)
Curl_expire_ex(data, nowp, conn->shutdown.timeout_ms,
EXPIRE_SHUTDOWN);
Curl_expire_ex(data, conn->shutdown.timeout_ms, EXPIRE_SHUTDOWN);
}
timediff_t Curl_shutdown_timeleft(struct connectdata *conn, int sockindex,
struct curltime *nowp)
timediff_t Curl_shutdown_timeleft(struct Curl_easy *data,
struct connectdata *conn,
int sockindex)
{
struct curltime now;
timediff_t left_ms;
if(!conn->shutdown.start[sockindex].tv_sec ||
(conn->shutdown.timeout_ms <= 0))
return 0; /* not started or no limits */
if(!nowp) {
now = curlx_now();
nowp = &now;
}
left_ms = conn->shutdown.timeout_ms -
curlx_timediff_ms(*nowp, conn->shutdown.start[sockindex]);
curlx_timediff_ms(data->progress.now,
conn->shutdown.start[sockindex]);
return left_ms ? left_ms : -1;
}
timediff_t Curl_conn_shutdown_timeleft(struct connectdata *conn,
struct curltime *nowp)
timediff_t Curl_conn_shutdown_timeleft(struct Curl_easy *data,
struct connectdata *conn)
{
timediff_t left_ms = 0, ms;
struct curltime now;
int i;
for(i = 0; conn->shutdown.timeout_ms && (i < 2); ++i) {
if(!conn->shutdown.start[i].tv_sec)
continue;
if(!nowp) {
now = curlx_now();
nowp = &now;
}
ms = Curl_shutdown_timeleft(conn, i, nowp);
ms = Curl_shutdown_timeleft(data, conn, i);
if(ms && (!left_ms || ms < left_ms))
left_ms = ms;
}

View File

@@ -39,7 +39,6 @@ enum alpnid Curl_str2alpnid(const struct Curl_str *str);
/* generic function that returns how much time there is left to run, according
to the timeouts set */
timediff_t Curl_timeleft_ms(struct Curl_easy *data,
struct curltime *nowp,
bool duringconnect);
#define DEFAULT_CONNECT_TIMEOUT 300000 /* milliseconds == five minutes */
@@ -47,17 +46,18 @@ timediff_t Curl_timeleft_ms(struct Curl_easy *data,
#define DEFAULT_SHUTDOWN_TIMEOUT_MS (2 * 1000)
void Curl_shutdown_start(struct Curl_easy *data, int sockindex,
int timeout_ms, struct curltime *nowp);
int timeout_ms);
/* return how much time there is left to shutdown the connection at
* sockindex. Returns 0 if there is no limit or shutdown has not started. */
timediff_t Curl_shutdown_timeleft(struct connectdata *conn, int sockindex,
struct curltime *nowp);
timediff_t Curl_shutdown_timeleft(struct Curl_easy *data,
struct connectdata *conn,
int sockindex);
/* return how much time there is left to shutdown the connection.
* Returns 0 if there is no limit or shutdown has not started. */
timediff_t Curl_conn_shutdown_timeleft(struct connectdata *conn,
struct curltime *nowp);
timediff_t Curl_conn_shutdown_timeleft(struct Curl_easy *data,
struct connectdata *conn);
void Curl_shutdown_clear(struct Curl_easy *data, int sockindex);

View File

@@ -232,8 +232,6 @@ static void cshutdn_perform(struct cshutdn *cshutdn,
struct Curl_llist_node *e = Curl_llist_head(&cshutdn->list);
struct Curl_llist_node *enext;
struct connectdata *conn;
struct curltime *nowp = NULL;
struct curltime now;
timediff_t next_expire_ms = 0, ms;
bool done;
@@ -253,11 +251,7 @@ static void cshutdn_perform(struct cshutdn *cshutdn,
else {
/* idata has one timer list, but maybe more than one connection.
* Set EXPIRE_SHUTDOWN to the smallest time left for all. */
if(!nowp) {
now = curlx_now();
nowp = &now;
}
ms = Curl_conn_shutdown_timeleft(conn, nowp);
ms = Curl_conn_shutdown_timeleft(data, conn);
if(ms && ms < next_expire_ms)
next_expire_ms = ms;
}
@@ -265,14 +259,14 @@ static void cshutdn_perform(struct cshutdn *cshutdn,
}
if(next_expire_ms)
Curl_expire_ex(data, nowp, next_expire_ms, EXPIRE_SHUTDOWN);
Curl_expire_ex(data, next_expire_ms, EXPIRE_SHUTDOWN);
}
static void cshutdn_terminate_all(struct cshutdn *cshutdn,
struct Curl_easy *data,
int timeout_ms)
{
struct curltime started = curlx_now();
struct curltime started = data->progress.now;
struct Curl_llist_node *e;
SIGPIPE_VARIABLE(pipe_st);
@@ -295,7 +289,8 @@ static void cshutdn_terminate_all(struct cshutdn *cshutdn,
}
/* wait for activity, timeout or "nothing" */
spent_ms = curlx_timediff_ms(curlx_now(), started);
Curl_pgrs_now_set(data); /* update in loop */
spent_ms = curlx_timediff_ms(data->progress.now, started);
if(spent_ms >= (timediff_t)timeout_ms) {
CURL_TRC_M(data, "[SHUTDOWN] shutdown finished, %s",
(timeout_ms > 0) ? "timeout" : "best effort done");

View File

@@ -314,12 +314,11 @@ void Curl_trc_easy_timers(struct Curl_easy *data)
if(CURL_TRC_TIMER_is_verbose(data)) {
struct Curl_llist_node *e = Curl_llist_head(&data->state.timeoutlist);
if(e) {
struct curltime now = curlx_now();
while(e) {
struct time_node *n = Curl_node_elem(e);
e = Curl_node_next(e);
CURL_TRC_TIMER(data, n->eid, "expires in %" FMT_TIMEDIFF_T "ns",
curlx_timediff_us(n->time, now));
curlx_timediff_us(n->time, data->progress.now));
}
}
}

View File

@@ -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;

View File

@@ -996,6 +996,7 @@ CURL *curl_easy_duphandle(CURL *d)
if(dupset(outcurl, data))
goto fail;
Curl_pgrs_now_set(outcurl); /* start of API call */
outcurl->progress.hide = data->progress.hide;
outcurl->progress.callback = data->progress.callback;
@@ -1155,6 +1156,7 @@ CURLcode curl_easy_pause(CURL *d, int action)
if(Curl_is_in_callback(data))
recursive = TRUE;
Curl_pgrs_now_set(data); /* start of API call */
recv_paused = Curl_xfer_recv_is_paused(data);
recv_paused_new = (action & CURLPAUSE_RECV);
send_paused = Curl_xfer_send_is_paused(data);
@@ -1179,7 +1181,7 @@ CURLcode curl_easy_pause(CURL *d, int action)
Curl_multi_mark_dirty(data); /* make it run */
/* On changes, tell application to update its timers. */
if(changed) {
if(Curl_update_timer(data->multi) && !result)
if(Curl_update_timer(data->multi, &data->progress.now) && !result)
result = CURLE_ABORTED_BY_CALLBACK;
}
}

View File

@@ -315,7 +315,6 @@ static CURLcode file_upload(struct Curl_easy *data,
CURLcode result = CURLE_OK;
char *xfer_ulbuf;
size_t xfer_ulblen;
curl_off_t bytecount = 0;
struct_stat file_stat;
const char *sendbuf;
bool eos = FALSE;
@@ -405,9 +404,7 @@ static CURLcode file_upload(struct Curl_easy *data,
result = CURLE_SEND_ERROR;
break;
}
bytecount += nwritten;
Curl_pgrsSetUploadCounter(data, bytecount);
Curl_pgrs_upload_inc(data, nwritten);
result = Curl_pgrsCheck(data);
}

View File

@@ -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)

View File

@@ -195,7 +195,7 @@ static CURLcode gopher_do(struct Curl_easy *data, bool *done)
else
break;
timeout_ms = Curl_timeleft_ms(data, NULL, FALSE);
timeout_ms = Curl_timeleft_ms(data, FALSE);
if(timeout_ms < 0) {
result = CURLE_OPERATION_TIMEDOUT;
break;

View File

@@ -253,7 +253,6 @@ static void dnscache_unlock(struct Curl_easy *data,
void Curl_dnscache_prune(struct Curl_easy *data)
{
struct Curl_dnscache *dnscache = dnscache_get(data);
struct curltime now;
/* the timeout may be set -1 (forever) */
timediff_t timeout_ms = data->set.dns_cache_timeout_ms;
@@ -263,11 +262,10 @@ void Curl_dnscache_prune(struct Curl_easy *data)
dnscache_lock(data, dnscache);
now = curlx_now();
do {
/* Remove outdated and unused entries from the hostcache */
timediff_t oldest_ms = dnscache_prune(&dnscache->entries, timeout_ms, now);
timediff_t oldest_ms =
dnscache_prune(&dnscache->entries, timeout_ms, data->progress.now);
if(Curl_hash_count(&dnscache->entries) > MAX_DNS_CACHE_SIZE)
/* prune the ones over half this age */
@@ -333,7 +331,7 @@ static struct Curl_dns_entry *fetch_addr(struct Curl_easy *data,
/* See whether the returned entry is stale. Done before we release lock */
struct dnscache_prune_data user;
user.now = curlx_now();
user.now = data->progress.now;
user.max_age_ms = data->set.dns_cache_timeout_ms;
user.oldest_ms = 0;
@@ -522,7 +520,7 @@ Curl_dnscache_mk_entry(struct Curl_easy *data,
dns->timestamp.tv_usec = 0; /* an entry that never goes stale */
}
else {
dns->timestamp = curlx_now();
dns->timestamp = data->progress.now;
}
dns->hostport = port;
if(hostlen)
@@ -1138,7 +1136,7 @@ clean_up:
the time we spent until now! */
if(prev_alarm) {
/* there was an alarm() set before us, now put it back */
timediff_t elapsed_secs = curlx_timediff_ms(curlx_now(),
timediff_t elapsed_secs = curlx_timediff_ms(data->progress.now,
data->conn->created) / 1000;
/* the alarm period is counted in even number of seconds */

View File

@@ -4949,7 +4949,7 @@ static CURLcode cr_exp100_read(struct Curl_easy *data,
DEBUGF(infof(data, "cr_exp100_read, start AWAITING_CONTINUE, "
"timeout %dms", data->set.expect_100_timeout));
ctx->state = EXP100_AWAITING_CONTINUE;
ctx->start = curlx_now();
ctx->start = data->progress.now;
Curl_expire(data, data->set.expect_100_timeout, EXPIRE_100_TIMEOUT);
*nread = 0;
*eos = FALSE;
@@ -4960,7 +4960,7 @@ static CURLcode cr_exp100_read(struct Curl_easy *data,
*eos = FALSE;
return CURLE_READ_ERROR;
case EXP100_AWAITING_CONTINUE:
ms = curlx_timediff_ms(curlx_now(), ctx->start);
ms = curlx_timediff_ms(data->progress.now, ctx->start);
if(ms < data->set.expect_100_timeout) {
DEBUGF(infof(data, "cr_exp100_read, AWAITING_CONTINUE, not expired"));
*nread = 0;

View File

@@ -37,6 +37,7 @@
#include "select.h"
#include "curlx/base64.h"
#include "multiif.h"
#include "progress.h"
#include "url.h"
#include "urlapi-int.h"
#include "cfilters.h"
@@ -305,7 +306,8 @@ static void h2_stream_hash_free(unsigned int id, void *stream)
static int32_t cf_h2_get_desired_local_win(struct Curl_cfilter *cf,
struct Curl_easy *data)
{
curl_off_t avail = Curl_rlimit_avail(&data->progress.dl.rlimit, curlx_now());
curl_off_t avail =
Curl_rlimit_avail(&data->progress.dl.rlimit, &data->progress.now);
(void)cf;
if(avail < CURL_OFF_T_MAX) { /* limit in place */
@@ -1422,7 +1424,7 @@ static int on_data_chunk_recv(nghttp2_session *session, uint8_t flags,
struct Curl_cfilter *cf = userp;
struct cf_h2_ctx *ctx = cf->ctx;
struct h2_stream_ctx *stream;
struct Curl_easy *data_s;
struct Curl_easy *data_s, *calling = CF_DATA_CURRENT(cf);
(void)flags;
DEBUGASSERT(stream_id); /* should never be a zero stream ID here */
@@ -1443,6 +1445,8 @@ static int on_data_chunk_recv(nghttp2_session *session, uint8_t flags,
stream = H2_STREAM_CTX(ctx, data_s);
if(!stream)
return NGHTTP2_ERR_CALLBACK_FAILURE;
if(calling)
Curl_pgrs_now_update(data_s, calling);
h2_xfer_write_resp(cf, data_s, stream, (const char *)mem, len, FALSE);

View File

@@ -1995,7 +1995,7 @@ static CURLcode imap_setup_connection(struct Curl_easy *data,
Curl_sasl_init(&imapc->sasl, data, &saslimap);
curlx_dyn_init(&imapc->dyn, DYN_IMAP_CMD);
Curl_pp_init(pp);
Curl_pp_init(pp, &data->progress.now);
if(Curl_conn_meta_set(conn, CURL_META_IMAP_CONN, imapc, imap_conn_dtor))
return CURLE_OUT_OF_MEMORY;

View File

@@ -186,7 +186,7 @@ static CURLcode mqtt_send(struct Curl_easy *data,
result = Curl_xfer_send(data, buf, len, FALSE, &n);
if(result)
return result;
mq->lastTime = curlx_now();
mq->lastTime = data->progress.now;
Curl_debug(data, CURLINFO_HEADER_OUT, buf, n);
if(len != n) {
size_t nsend = len - n;
@@ -770,7 +770,7 @@ MQTT_SUBACK_COMING:
}
/* we received something */
mq->lastTime = curlx_now();
mq->lastTime = data->progress.now;
/* if QoS is set, message contains packet id */
result = Curl_client_write(data, CLIENTWRITE_BODY, buffer, nread);
@@ -800,7 +800,7 @@ static CURLcode mqtt_do(struct Curl_easy *data, bool *done)
if(!mq)
return CURLE_FAILED_INIT;
mq->lastTime = curlx_now();
mq->lastTime = data->progress.now;
mq->pingsent = FALSE;
result = mqtt_connect(data);
@@ -839,7 +839,7 @@ static CURLcode mqtt_ping(struct Curl_easy *data)
if(mqtt->state == MQTT_FIRST &&
!mq->pingsent &&
data->set.upkeep_interval_ms > 0) {
struct curltime t = curlx_now();
struct curltime t = data->progress.now;
timediff_t diff = curlx_timediff_ms(t, mq->lastTime);
if(diff > data->set.upkeep_interval_ms) {
@@ -898,7 +898,7 @@ static CURLcode mqtt_doing(struct Curl_easy *data, bool *done)
Curl_debug(data, CURLINFO_HEADER_IN, (const char *)&mq->firstbyte, 1);
/* we received something */
mq->lastTime = curlx_now();
mq->lastTime = data->progress.now;
/* remember the first byte */
mq->npacket = 0;

View File

@@ -99,6 +99,7 @@ static CURLMcode add_next_timeout(struct curltime now,
struct Curl_multi *multi,
struct Curl_easy *d);
static void multi_timeout(struct Curl_multi *multi,
struct curltime *pnow,
struct curltime *expire_time,
long *timeout_ms);
static void process_pending_handles(struct Curl_multi *multi);
@@ -165,6 +166,8 @@ static void mstate(struct Curl_easy *data, CURLMstate state
CURL_TRC_M(data, "-> [%s]", CURL_MSTATE_NAME(state));
#endif
/* really switching state */
Curl_pgrs_now_set(data);
data->mstate = state;
switch(state) {
case MSTATE_DONE:
@@ -414,6 +417,7 @@ CURLMcode curl_multi_add_handle(CURLM *m, CURL *d)
CURLMcode rc;
struct Curl_multi *multi = m;
struct Curl_easy *data = d;
/* First, make some basic checks that the CURLM handle is a good handle */
if(!GOOD_MULTI_HANDLE(multi))
return CURLM_BAD_HANDLE;
@@ -445,6 +449,7 @@ CURLMcode curl_multi_add_handle(CURLM *m, CURL *d)
Curl_uint32_bset_clear(&multi->msgsent);
}
Curl_pgrs_now_set(data); /* start of API call */
if(data->multi_easy) {
/* if this easy handle was previously used for curl_easy_perform(), there
is a private multi handle here that we can kill */
@@ -498,7 +503,7 @@ CURLMcode curl_multi_add_handle(CURLM *m, CURL *d)
/* Necessary in event based processing, where dirty handles trigger
* a timeout callback invocation. */
rc = Curl_update_timer(multi);
rc = Curl_update_timer(multi, &data->progress.now);
if(rc) {
data->multi = NULL; /* not anymore */
Curl_uint32_tbl_remove(&multi->xfers, data->mid);
@@ -779,6 +784,7 @@ CURLMcode curl_multi_remove_handle(CURLM *m, CURL *d)
if(multi->in_callback)
return CURLM_RECURSIVE_API_CALL;
Curl_pgrs_now_set(data); /* start of API call */
premature = (data->mstate < MSTATE_COMPLETED);
/* If the 'state' is not INIT or COMPLETED, we might need to do something
@@ -877,7 +883,7 @@ CURLMcode curl_multi_remove_handle(CURLM *m, CURL *d)
process_pending_handles(multi);
if(removed_timer) {
rc = Curl_update_timer(multi);
rc = Curl_update_timer(multi, &data->progress.now);
if(rc)
return rc;
}
@@ -945,11 +951,11 @@ static CURLcode multi_adjust_pollset(struct Curl_easy *data,
CURLcode result = CURLE_OK;
if(ps->n) {
struct curltime now = curlx_now();
bool send_blocked, recv_blocked;
bool send_blocked =
(Curl_rlimit_avail(&data->progress.dl.rlimit, &data->progress.now) <= 0);
bool recv_blocked =
(Curl_rlimit_avail(&data->progress.ul.rlimit, &data->progress.now) <= 0);
recv_blocked = (Curl_rlimit_avail(&data->progress.dl.rlimit, now) <= 0);
send_blocked = (Curl_rlimit_avail(&data->progress.ul.rlimit, now) <= 0);
if(send_blocked || recv_blocked) {
int i;
for(i = 0; i <= SECONDARYSOCKET; ++i) {
@@ -1220,6 +1226,7 @@ CURLMcode curl_multi_fdset(CURLM *m,
struct Curl_multi *multi = m;
struct easy_pollset ps;
unsigned int i, mid;
struct curltime now = curlx_now(); /* start of API call */
(void)exc_fd_set;
if(!GOOD_MULTI_HANDLE(multi))
@@ -1238,6 +1245,7 @@ CURLMcode curl_multi_fdset(CURLM *m,
continue;
}
Curl_pgrs_now_at_least(data, &now);
Curl_multi_pollset(data, &ps);
for(i = 0; i < ps.n; i++) {
if(!FDSET_SOCK(ps.sockets[i]))
@@ -1279,6 +1287,7 @@ CURLMcode curl_multi_waitfds(CURLM *m,
struct Curl_multi *multi = m;
struct easy_pollset ps;
unsigned int need = 0, mid;
struct curltime now = curlx_now(); /* start of API call */
if(!ufds && (size || !fd_count))
return CURLM_BAD_FUNCTION_ARGUMENT;
@@ -1300,6 +1309,7 @@ CURLMcode curl_multi_waitfds(CURLM *m,
Curl_uint32_bset_remove(&multi->dirty, mid);
continue;
}
Curl_pgrs_now_at_least(data, &now);
Curl_multi_pollset(data, &ps);
need += Curl_waitfds_add_ps(&cwfds, &ps);
} while(Curl_uint32_bset_next(&multi->process, mid, &mid));
@@ -1353,6 +1363,7 @@ static CURLMcode multi_wait(struct Curl_multi *multi,
unsigned int curl_nfds = 0; /* how many pfds are for curl transfers */
struct Curl_easy *data = NULL;
CURLMcode result = CURLM_OK;
struct curltime now = curlx_now(); /* start of API call */
uint32_t mid;
#ifdef USE_WINSOCK
@@ -1385,11 +1396,13 @@ static CURLMcode multi_wait(struct Curl_multi *multi,
Curl_uint32_bset_remove(&multi->dirty, mid);
continue;
}
Curl_pgrs_now_at_least(data, &now);
Curl_multi_pollset(data, &ps);
if(Curl_pollfds_add_ps(&cpfds, &ps)) {
result = CURLM_OUT_OF_MEMORY;
goto out;
}
now = data->progress.now;
} while(Curl_uint32_bset_next(&multi->process, mid, &mid));
}
@@ -1450,7 +1463,7 @@ static CURLMcode multi_wait(struct Curl_multi *multi,
* poll. Collecting the sockets may install new timers by protocols
* and connection filters.
* Use the shorter one of the internal and the caller requested timeout. */
multi_timeout(multi, &expire_time, &timeout_internal);
multi_timeout(multi, &now, &expire_time, &timeout_internal);
if((timeout_internal >= 0) && (timeout_internal < (long)timeout_ms))
timeout_ms = (int)timeout_internal;
@@ -1571,7 +1584,7 @@ static CURLMcode multi_wait(struct Curl_multi *multi,
long sleep_ms = 0;
/* Avoid busy-looping when there is nothing particular to wait for */
multi_timeout(multi, &expire_time, &sleep_ms);
multi_timeout(multi, &now, &expire_time, &sleep_ms);
if(sleep_ms) {
if(sleep_ms > timeout_ms)
sleep_ms = timeout_ms;
@@ -1764,12 +1777,12 @@ static CURLcode multi_do_more(struct Curl_easy *data, int *complete)
* Check whether a timeout occurred, and handle it if it did
*/
static bool multi_handle_timeout(struct Curl_easy *data,
struct curltime *now,
bool *stream_error,
CURLcode *result)
{
bool connect_timeout = data->mstate < MSTATE_DO;
timediff_t timeout_ms = Curl_timeleft_ms(data, now, connect_timeout);
timediff_t timeout_ms =
Curl_timeleft_ms(data, connect_timeout);
if(timeout_ms < 0) {
/* Handle timed out */
struct curltime since;
@@ -1779,22 +1792,23 @@ static bool multi_handle_timeout(struct Curl_easy *data,
since = data->progress.t_startop;
if(data->mstate == MSTATE_RESOLVING)
failf(data, "Resolving timed out after %" FMT_TIMEDIFF_T
" milliseconds", curlx_timediff_ms(*now, since));
" milliseconds", curlx_timediff_ms(data->progress.now, since));
else if(data->mstate == MSTATE_CONNECTING)
failf(data, "Connection timed out after %" FMT_TIMEDIFF_T
" milliseconds", curlx_timediff_ms(*now, since));
" milliseconds", curlx_timediff_ms(data->progress.now, since));
else {
struct SingleRequest *k = &data->req;
if(k->size != -1) {
failf(data, "Operation timed out after %" FMT_TIMEDIFF_T
" milliseconds with %" FMT_OFF_T " out of %"
FMT_OFF_T " bytes received",
curlx_timediff_ms(*now, since), k->bytecount, k->size);
curlx_timediff_ms(data->progress.now, since),
k->bytecount, k->size);
}
else {
failf(data, "Operation timed out after %" FMT_TIMEDIFF_T
" milliseconds with %" FMT_OFF_T " bytes received",
curlx_timediff_ms(*now, since), k->bytecount);
curlx_timediff_ms(data->progress.now, since), k->bytecount);
}
}
*result = CURLE_OPERATION_TIMEDOUT;
@@ -1920,14 +1934,16 @@ static CURLcode multi_follow(struct Curl_easy *data,
return CURLE_TOO_MANY_REDIRECTS;
}
static CURLcode mspeed_check(struct Curl_easy *data, struct curltime now)
static CURLcode mspeed_check(struct Curl_easy *data)
{
timediff_t recv_wait_ms = 0;
timediff_t send_wait_ms = 0;
/* check if our send/recv limits require idle waits */
send_wait_ms = Curl_rlimit_wait_ms(&data->progress.ul.rlimit, now);
recv_wait_ms = Curl_rlimit_wait_ms(&data->progress.dl.rlimit, now);
send_wait_ms =
Curl_rlimit_wait_ms(&data->progress.ul.rlimit, &data->progress.now);
recv_wait_ms =
Curl_rlimit_wait_ms(&data->progress.dl.rlimit, &data->progress.now);
if(send_wait_ms || recv_wait_ms) {
if(data->mstate != MSTATE_RATELIMITING) {
@@ -1947,7 +1963,6 @@ static CURLcode mspeed_check(struct Curl_easy *data, struct curltime now)
}
static CURLMcode state_performing(struct Curl_easy *data,
struct curltime *nowp,
bool *stream_errorp,
CURLcode *resultp)
{
@@ -1957,11 +1972,11 @@ static CURLMcode state_performing(struct Curl_easy *data,
CURLcode result = *resultp = CURLE_OK;
*stream_errorp = FALSE;
if(mspeed_check(data, *nowp) == CURLE_AGAIN)
if(mspeed_check(data) == CURLE_AGAIN)
return CURLM_OK;
/* read/write data if it is ready to do so */
result = Curl_sendrecv(data, nowp);
result = Curl_sendrecv(data);
if(data->req.done || (result == CURLE_RECV_ERROR)) {
/* If CURLE_RECV_ERROR happens early enough, we assume it was a race
@@ -2079,8 +2094,7 @@ static CURLMcode state_performing(struct Curl_easy *data,
}
}
else { /* not errored, not done */
*nowp = curlx_now();
mspeed_check(data, *nowp);
mspeed_check(data);
}
curlx_free(newurl);
*resultp = result;
@@ -2225,7 +2239,6 @@ end:
}
static CURLMcode state_ratelimiting(struct Curl_easy *data,
struct curltime *nowp,
CURLcode *resultp)
{
CURLcode result = CURLE_OK;
@@ -2243,7 +2256,7 @@ static CURLMcode state_ratelimiting(struct Curl_easy *data,
multi_done(data, result, TRUE);
}
else {
if(!mspeed_check(data, *nowp))
if(!mspeed_check(data))
rc = CURLM_CALL_MULTI_PERFORM;
}
*resultp = result;
@@ -2302,7 +2315,6 @@ static CURLMcode state_resolving(struct Curl_multi *multi,
static CURLMcode state_connect(struct Curl_multi *multi,
struct Curl_easy *data,
struct curltime *nowp,
CURLcode *resultp)
{
/* Connect. We want to get a connection identifier filled in. This state can
@@ -2326,7 +2338,6 @@ static CURLMcode state_connect(struct Curl_multi *multi,
process_pending_handles(data->multi);
if(!result) {
*nowp = curlx_now();
if(async)
/* We are now waiting for an asynchronous name lookup */
multistate(data, MSTATE_RESOLVING);
@@ -2354,7 +2365,6 @@ static CURLMcode state_connect(struct Curl_multi *multi,
}
static CURLMcode multi_runsingle(struct Curl_multi *multi,
struct curltime *nowp,
struct Curl_easy *data)
{
struct Curl_message *msg = NULL;
@@ -2393,8 +2403,6 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi,
(HTTP/2), or the full connection for older protocols */
bool stream_error = FALSE;
rc = CURLM_OK;
/* update at start for continuous increase when looping */
*nowp = curlx_now();
if(multi_ischanged(multi, TRUE)) {
CURL_TRC_M(data, "multi changed, check CONNECT_PEND queue");
@@ -2412,7 +2420,7 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi,
/* Wait for the connect state as only then is the start time stored, but
we must not check already completed handles */
if((data->mstate >= MSTATE_CONNECT) && (data->mstate < MSTATE_COMPLETED) &&
multi_handle_timeout(data, nowp, &stream_error, &result))
multi_handle_timeout(data, &stream_error, &result))
/* Skip the statemachine and go directly to error handling section. */
goto statemachine_end;
@@ -2432,7 +2440,7 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi,
case MSTATE_SETUP:
/* Transitional state. Setup things for a new transfer. The handle
can come back to this state on a redirect. */
*nowp = Curl_pgrsTime(data, TIMER_STARTSINGLE);
Curl_pgrsTime(data, TIMER_STARTSINGLE);
if(data->set.timeout)
Curl_expire(data, data->set.timeout, EXPIRE_TIMEOUT);
if(data->set.connecttimeout)
@@ -2445,7 +2453,7 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi,
FALLTHROUGH();
case MSTATE_CONNECT:
rc = state_connect(multi, data, nowp, &result);
rc = state_connect(multi, data, &result);
break;
case MSTATE_RESOLVING:
@@ -2596,11 +2604,11 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi,
break;
case MSTATE_RATELIMITING: /* limit-rate exceeded in either direction */
rc = state_ratelimiting(data, nowp, &result);
rc = state_ratelimiting(data, &result);
break;
case MSTATE_PERFORMING:
rc = state_performing(data, nowp, &stream_error, &result);
rc = state_performing(data, &stream_error, &result);
break;
case MSTATE_DONE:
@@ -2655,7 +2663,7 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi,
* (i.e. CURLM_CALL_MULTI_PERFORM == TRUE) then we should do that before
* declaring the connection timed out as we may almost have a completed
* connection. */
multi_handle_timeout(data, nowp, &stream_error, &result);
multi_handle_timeout(data, &stream_error, &result);
}
statemachine_end:
@@ -2756,18 +2764,15 @@ statemachine_end:
return rc;
}
CURLMcode curl_multi_perform(CURLM *m, int *running_handles)
static CURLMcode multi_perform(struct Curl_multi *multi,
struct curltime *pnow,
int *running_handles)
{
CURLMcode returncode = CURLM_OK;
struct Curl_tree *t = NULL;
struct curltime now = curlx_now();
struct Curl_multi *multi = m;
uint32_t mid;
SIGPIPE_VARIABLE(pipe_st);
if(!GOOD_MULTI_HANDLE(multi))
return CURLM_BAD_HANDLE;
if(multi->in_callback)
return CURLM_RECURSIVE_API_CALL;
@@ -2788,15 +2793,17 @@ CURLMcode curl_multi_perform(CURLM *m, int *running_handles)
continue;
}
sigpipe_apply(data, &pipe_st);
result = multi_runsingle(multi, &now, data);
Curl_pgrs_now_at_least(data, pnow);
result = multi_runsingle(multi, data);
*pnow = data->progress.now; /* in case transfer updated */
if(result)
returncode = result;
} while(Curl_uint32_bset_next(&multi->process, mid, &mid));
}
sigpipe_restore(&pipe_st);
if(multi_ischanged(m, TRUE))
process_pending_handles(m);
if(multi_ischanged(multi, TRUE))
process_pending_handles(multi);
if(!returncode)
returncode = Curl_mntfy_dispatch_all(multi);
@@ -2812,15 +2819,15 @@ CURLMcode curl_multi_perform(CURLM *m, int *running_handles)
* been handled!
*/
do {
multi->timetree = Curl_splaygetbest(now, multi->timetree, &t);
multi->timetree = Curl_splaygetbest(*pnow, multi->timetree, &t);
if(t) {
/* the removed may have another timeout in queue */
struct Curl_easy *data = Curl_splayget(t);
(void)add_next_timeout(now, multi, data);
(void)add_next_timeout(*pnow, multi, data);
if(data->mstate == MSTATE_PENDING) {
bool stream_unused;
CURLcode result_unused;
if(multi_handle_timeout(data, &now, &stream_unused, &result_unused)) {
if(multi_handle_timeout(data, &stream_unused, &result_unused)) {
infof(data, "PENDING handle timeout");
move_pending_to_connect(multi, data);
}
@@ -2834,11 +2841,22 @@ CURLMcode curl_multi_perform(CURLM *m, int *running_handles)
}
if(CURLM_OK >= returncode)
returncode = Curl_update_timer(multi);
returncode = Curl_update_timer(multi, pnow);
return returncode;
}
CURLMcode curl_multi_perform(CURLM *m, int *running_handles)
{
struct curltime now = curlx_now(); /* start of API call */
struct Curl_multi *multi = m;
if(!GOOD_MULTI_HANDLE(multi))
return CURLM_BAD_HANDLE;
multi = m;
return multi_perform(multi, &now, running_handles);
}
CURLMcode curl_multi_cleanup(CURLM *m)
{
struct Curl_multi *multi = m;
@@ -3103,7 +3121,9 @@ static CURLMcode multi_run_dirty(struct multi_run_ctx *mrc)
mrc->run_xfers++;
sigpipe_apply(data, &mrc->pipe_st);
/* runsingle() clears the dirty mid */
result = multi_runsingle(multi, &mrc->now, data);
Curl_pgrs_now_at_least(data, &mrc->now);
result = multi_runsingle(multi, data);
mrc->now = data->progress.now; /* in case transfer updated */
if(CURLM_OK >= result) {
/* reassess event handling of data */
@@ -3135,16 +3155,17 @@ static CURLMcode multi_socket(struct Curl_multi *multi,
(void)ev_bitmask;
memset(&mrc, 0, sizeof(mrc));
mrc.multi = multi;
mrc.now = curlx_now();
mrc.now = curlx_now(); /* start of API call */
sigpipe_init(&mrc.pipe_st);
if(checkall) {
/* *perform() deals with running_handles on its own */
result = curl_multi_perform(multi, running_handles);
result = multi_perform(multi, &mrc.now, running_handles);
if(result != CURLM_BAD_HANDLE) {
/* Reassess event status of all active transfers */
result = Curl_multi_ev_assess_xfer_bset(multi, &multi->process);
result = Curl_multi_ev_assess_xfer_bset(multi, &multi->process,
&mrc.now);
}
goto out;
}
@@ -3173,7 +3194,6 @@ static CURLMcode multi_socket(struct Curl_multi *multi,
* to set a 0 timeout and call us again, we run them here.
* Do that only once or it might be unfair to transfers on other
* sockets. */
mrc.now = curlx_now();
multi_mark_expired_as_dirty(&mrc);
result = multi_run_dirty(&mrc);
}
@@ -3193,7 +3213,7 @@ out:
}
if(CURLM_OK >= result)
result = Curl_update_timer(multi);
result = Curl_update_timer(multi, &mrc.now);
return result;
}
@@ -3346,6 +3366,7 @@ static bool multi_has_dirties(struct Curl_multi *multi)
}
static void multi_timeout(struct Curl_multi *multi,
struct curltime *pnow,
struct curltime *expire_time,
long *timeout_ms)
{
@@ -3360,14 +3381,11 @@ static void multi_timeout(struct Curl_multi *multi,
}
if(multi_has_dirties(multi)) {
*expire_time = curlx_now();
*expire_time = *pnow;
*timeout_ms = 0;
return;
}
else if(multi->timetree) {
/* we have a tree of expire times */
struct curltime now = curlx_now();
/* splay the lowest to the bottom */
multi->timetree = Curl_splay(tv_zero, multi->timetree);
/* this will not return NULL from a non-empty tree, but some compilers
@@ -3377,9 +3395,9 @@ static void multi_timeout(struct Curl_multi *multi,
/* 'multi->timetree' will be non-NULL here but the compilers sometimes
yell at us if we assume so */
if(multi->timetree &&
curlx_timediff_us(multi->timetree->key, now) > 0) {
curlx_timediff_us(multi->timetree->key, *pnow) > 0) {
/* some time left before expiration */
timediff_t diff_ms = curlx_timediff_ceil_ms(multi->timetree->key, now);
timediff_t diff_ms = curlx_timediff_ceil_ms(multi->timetree->key, *pnow);
#ifndef CURL_DISABLE_VERBOSE_STRINGS
data = Curl_splayget(multi->timetree);
#endif
@@ -3419,6 +3437,7 @@ CURLMcode curl_multi_timeout(CURLM *m,
{
struct curltime expire_time;
struct Curl_multi *multi = m;
struct curltime now = curlx_now(); /* start of API call */
/* First, make some basic checks that the CURLM handle is a good handle */
if(!GOOD_MULTI_HANDLE(multi))
@@ -3427,7 +3446,7 @@ CURLMcode curl_multi_timeout(CURLM *m,
if(multi->in_callback)
return CURLM_RECURSIVE_API_CALL;
multi_timeout(multi, &expire_time, timeout_ms);
multi_timeout(multi, &now, &expire_time, timeout_ms);
return CURLM_OK;
}
@@ -3435,7 +3454,8 @@ CURLMcode curl_multi_timeout(CURLM *m,
* Tell the application it should update its timers, if it subscribes to the
* update timer callback.
*/
CURLMcode Curl_update_timer(struct Curl_multi *multi)
CURLMcode Curl_update_timer(struct Curl_multi *multi,
struct curltime *pnow)
{
struct curltime expire_ts;
long timeout_ms;
@@ -3444,7 +3464,7 @@ CURLMcode Curl_update_timer(struct Curl_multi *multi)
if(!multi->timer_cb || multi->dead)
return CURLM_OK;
multi_timeout(multi, &expire_ts, &timeout_ms);
multi_timeout(multi, pnow, &expire_ts, &timeout_ms);
if(timeout_ms < 0 && multi->last_timeout_ms < 0) {
/* nothing to do */
@@ -3552,7 +3572,6 @@ static CURLMcode multi_addtimeout(struct Curl_easy *data,
}
void Curl_expire_ex(struct Curl_easy *data,
const struct curltime *nowp,
timediff_t milli, expire_id id)
{
struct Curl_multi *multi = data->multi;
@@ -3566,7 +3585,7 @@ void Curl_expire_ex(struct Curl_easy *data,
DEBUGASSERT(id < EXPIRE_LAST);
set = *nowp;
set = data->progress.now;
set.tv_sec += (time_t)(milli / 1000); /* may be a 64 to 32-bit conversion */
set.tv_usec += (int)(milli % 1000) * 1000;
@@ -3580,7 +3599,7 @@ void Curl_expire_ex(struct Curl_easy *data,
/* Add it to the timer list. It must stay in the list until it has expired
in case we need to recompute the minimum timer later. */
multi_addtimeout(data, &set, id, nowp);
multi_addtimeout(data, &set, id, &data->progress.now);
if(curr_expire->tv_sec || curr_expire->tv_usec) {
/* This means that the struct is added as a node in the splay tree.
@@ -3624,8 +3643,7 @@ void Curl_expire_ex(struct Curl_easy *data,
*/
void Curl_expire(struct Curl_easy *data, timediff_t milli, expire_id id)
{
struct curltime now = curlx_now();
Curl_expire_ex(data, &now, milli, id);
Curl_expire_ex(data, milli, id);
}
/*

View File

@@ -33,6 +33,7 @@
#include "multiif.h"
#include "curlx/timeval.h"
#include "multi_ev.h"
#include "progress.h"
#include "select.h"
#include "uint-bset.h"
#include "uint-spbset.h"
@@ -480,7 +481,8 @@ static struct easy_pollset *mev_get_last_pollset(struct Curl_easy *data,
return NULL;
}
static CURLMcode mev_assess(struct Curl_multi *multi, struct Curl_easy *data,
static CURLMcode mev_assess(struct Curl_multi *multi,
struct Curl_easy *data,
struct connectdata *conn)
{
struct easy_pollset ps, *last_ps;
@@ -536,7 +538,8 @@ CURLMcode Curl_multi_ev_assess_conn(struct Curl_multi *multi,
}
CURLMcode Curl_multi_ev_assess_xfer_bset(struct Curl_multi *multi,
struct uint32_bset *set)
struct uint32_bset *set,
struct curltime *pnow)
{
uint32_t mid;
CURLMcode result = CURLM_OK;
@@ -544,8 +547,10 @@ CURLMcode Curl_multi_ev_assess_xfer_bset(struct Curl_multi *multi,
if(multi && multi->socket_cb && Curl_uint32_bset_first(set, &mid)) {
do {
struct Curl_easy *data = Curl_multi_get_easy(multi, mid);
if(data)
if(data) {
Curl_pgrs_now_at_least(data, pnow);
result = Curl_multi_ev_assess_xfer(multi, data);
}
} while(!result && Curl_uint32_bset_next(set, mid, &mid));
}
return result;

View File

@@ -55,7 +55,8 @@ CURLMcode Curl_multi_ev_assess_xfer(struct Curl_multi *multi,
struct Curl_easy *data);
/* Assess all easy handles on the list */
CURLMcode Curl_multi_ev_assess_xfer_bset(struct Curl_multi *multi,
struct uint32_bset *set);
struct uint32_bset *set,
struct curltime *pnow);
/* Assess the connection by getting its current pollset */
CURLMcode Curl_multi_ev_assess_conn(struct Curl_multi *multi,
struct Curl_easy *data,

View File

@@ -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);

View File

@@ -47,26 +47,30 @@ timediff_t Curl_pp_state_timeout(struct Curl_easy *data,
timediff_t timeout_ms; /* in milliseconds */
timediff_t response_time = data->set.server_response_timeout ?
data->set.server_response_timeout : RESP_TIMEOUT;
struct curltime now = curlx_now();
/* if CURLOPT_SERVER_RESPONSE_TIMEOUT is set, use that to determine
remaining time, or use pp->response because SERVER_RESPONSE_TIMEOUT is
supposed to govern the response for any given server response, not for
the time from connect to the given server response. */
/* pingpong can spend some time processing, always update
* the transfer timestamp before checking timeouts. */
Curl_pgrs_now_set(data);
/* Without a requested timeout, we only wait 'response_time' seconds for the
full response to arrive before we bail out */
timeout_ms = response_time - curlx_timediff_ms(now, pp->response);
timeout_ms = response_time -
curlx_timediff_ms(data->progress.now, pp->response);
if(data->set.timeout && !disconnecting) {
/* if timeout is requested, find out how much overall remains */
timediff_t timeout2_ms = Curl_timeleft_ms(data, &now, FALSE);
timediff_t timeout2_ms = Curl_timeleft_ms(data, FALSE);
/* pick the lowest number */
timeout_ms = CURLMIN(timeout_ms, timeout2_ms);
}
if(disconnecting) {
timediff_t total_left_ms = Curl_timeleft_ms(data, NULL, FALSE);
timediff_t total_left_ms = Curl_timeleft_ms(data, FALSE);
timeout_ms = CURLMIN(timeout_ms, CURLMAX(total_left_ms, 0));
}
@@ -135,11 +139,11 @@ CURLcode Curl_pp_statemach(struct Curl_easy *data,
}
/* initialize stuff to prepare for reading a fresh new response */
void Curl_pp_init(struct pingpong *pp)
void Curl_pp_init(struct pingpong *pp, struct curltime *pnow)
{
DEBUGASSERT(!pp->initialised);
pp->nread_resp = 0;
pp->response = curlx_now(); /* start response time-out now! */
pp->response = *pnow; /* start response time-out */
pp->pending_resp = TRUE;
curlx_dyn_init(&pp->sendbuf, DYN_PINGPPONG_CMD);
curlx_dyn_init(&pp->recvbuf, DYN_PINGPPONG_CMD);
@@ -208,7 +212,7 @@ CURLcode Curl_pp_vsendf(struct Curl_easy *data,
else {
pp->sendthis = NULL;
pp->sendleft = pp->sendsize = 0;
pp->response = curlx_now();
pp->response = data->progress.now;
}
return CURLE_OK;
@@ -399,7 +403,7 @@ CURLcode Curl_pp_flushsend(struct Curl_easy *data,
else {
pp->sendthis = NULL;
pp->sendleft = pp->sendsize = 0;
pp->response = curlx_now();
pp->response = data->progress.now;
}
return CURLE_OK;
}

View File

@@ -87,7 +87,7 @@ CURLcode Curl_pp_statemach(struct Curl_easy *data, struct pingpong *pp,
bool block, bool disconnecting);
/* initialize stuff to prepare for reading a fresh new response */
void Curl_pp_init(struct pingpong *pp);
void Curl_pp_init(struct pingpong *pp, struct curltime *pnow);
/* Returns timeout in ms. 0 or negative number means the timeout has already
triggered */

View File

@@ -1297,7 +1297,7 @@ static CURLcode pop3_connect(struct Curl_easy *data, bool *done)
Curl_sasl_init(&pop3c->sasl, data, &saslpop3);
/* Initialise the pingpong layer */
Curl_pp_init(pp);
Curl_pp_init(pp, &data->progress.now);
/* Parse the URL options */
result = pop3_parse_url_options(conn);

View File

@@ -141,6 +141,25 @@ UNITTEST CURLcode pgrs_speedcheck(struct Curl_easy *data,
return CURLE_OK;
}
void Curl_pgrs_now_set(struct Curl_easy *data)
{
data->progress.now = curlx_now();
}
void Curl_pgrs_now_at_least(struct Curl_easy *data, struct curltime *pts)
{
if((pts->tv_sec > data->progress.now.tv_sec) ||
((pts->tv_sec == data->progress.now.tv_sec) &&
(pts->tv_usec > data->progress.now.tv_usec))) {
data->progress.now = *pts;
}
}
void Curl_pgrs_now_update(struct Curl_easy *data, struct Curl_easy *other)
{
Curl_pgrs_now_at_least(data, &other->progress.now);
}
/*
New proposed interface, 9th of February 2000:
@@ -174,7 +193,7 @@ int Curl_pgrsDone(struct Curl_easy *data)
void Curl_pgrsReset(struct Curl_easy *data)
{
Curl_pgrsSetUploadCounter(data, 0);
Curl_pgrsSetDownloadCounter(data, 0);
data->progress.dl.cur_size = 0;
Curl_pgrsSetUploadSize(data, -1);
Curl_pgrsSetDownloadSize(data, -1);
data->progress.speeder_c = 0; /* reset speed records */
@@ -288,19 +307,17 @@ void Curl_pgrsTimeWas(struct Curl_easy *data, timerid timer,
*
* @unittest: 1399
*/
struct curltime Curl_pgrsTime(struct Curl_easy *data, timerid timer)
void Curl_pgrsTime(struct Curl_easy *data, timerid timer)
{
struct curltime now = curlx_now();
Curl_pgrsTimeWas(data, timer, now);
return now;
Curl_pgrs_now_set(data); /* update on real progress */
Curl_pgrsTimeWas(data, timer, data->progress.now);
}
void Curl_pgrsStartNow(struct Curl_easy *data)
{
struct Progress *p = &data->progress;
p->speeder_c = 0; /* reset the progress meter display */
p->start = curlx_now();
p->start = data->progress.now;
p->is_t_startransfer_set = FALSE;
p->dl.cur_size = 0;
p->ul.cur_size = 0;
@@ -309,12 +326,20 @@ void Curl_pgrsStartNow(struct Curl_easy *data)
p->ul_size_known = FALSE;
}
/*
* Set the number of downloaded bytes so far.
*/
void Curl_pgrsSetDownloadCounter(struct Curl_easy *data, curl_off_t size)
void Curl_pgrs_download_inc(struct Curl_easy *data, size_t delta)
{
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);
}

View File

@@ -44,12 +44,24 @@ typedef enum {
TIMER_LAST /* must be last */
} timerid;
#define CURL_PGRS_NOW_MONOTONIC
/* Set current time in data->progress.now */
void Curl_pgrs_now_set(struct Curl_easy *data);
/* Advance `now` timestamp at least to given timestamp.
* No effect it data's `now` is already later than `pts`. */
void Curl_pgrs_now_at_least(struct Curl_easy *data, struct curltime *pts);
/* `data` progressing continues after `other` processing. Advance `data`s
* now timestamp to at least `other's` timestamp. */
void Curl_pgrs_now_update(struct Curl_easy *data, struct Curl_easy *other);
int Curl_pgrsDone(struct Curl_easy *data);
void Curl_pgrsStartNow(struct Curl_easy *data);
void Curl_pgrsSetDownloadSize(struct Curl_easy *data, curl_off_t size);
void Curl_pgrsSetUploadSize(struct Curl_easy *data, curl_off_t size);
void Curl_pgrsSetDownloadCounter(struct Curl_easy *data, curl_off_t size);
void Curl_pgrs_download_inc(struct Curl_easy *data, size_t delta);
void Curl_pgrs_upload_inc(struct Curl_easy *data, size_t delta);
void Curl_pgrsSetUploadCounter(struct Curl_easy *data, curl_off_t size);
/* perform progress update, invoking callbacks at intervals */
@@ -68,7 +80,7 @@ void Curl_pgrsReset(struct Curl_easy *data);
/* Reset sizes for up- and download. */
void Curl_pgrsResetTransferSizes(struct Curl_easy *data);
struct curltime Curl_pgrsTime(struct Curl_easy *data, timerid timer);
void Curl_pgrsTime(struct Curl_easy *data, timerid timer);
/**
* Update progress timer with the elapsed time from its start to `timestamp`.
* This allows updating timers later and is used by happy eyeballing, where

View File

@@ -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;

View File

@@ -62,14 +62,14 @@ struct Curl_rlimit {
void Curl_rlimit_init(struct Curl_rlimit *r,
curl_off_t rate_per_s,
curl_off_t burst_per_s,
struct curltime ts);
struct curltime *pts);
/* Start ratelimiting with the given timestamp. Resets available tokens. */
void Curl_rlimit_start(struct Curl_rlimit *r, struct curltime ts);
void Curl_rlimit_start(struct Curl_rlimit *r, struct curltime *pts);
/* How many milliseconds to wait until token are available again. */
timediff_t Curl_rlimit_wait_ms(struct Curl_rlimit *r,
struct curltime ts);
struct curltime *pts);
/* Return if rate limiting of tokens is active */
bool Curl_rlimit_active(struct Curl_rlimit *r);
@@ -77,16 +77,16 @@ bool Curl_rlimit_is_blocked(struct Curl_rlimit *r);
/* Return how many tokens are available to spend, may be negative */
curl_off_t Curl_rlimit_avail(struct Curl_rlimit *r,
struct curltime ts);
struct curltime *pts);
/* Drain tokens from the ratelimit, return how many are now available. */
void Curl_rlimit_drain(struct Curl_rlimit *r,
size_t tokens,
struct curltime ts);
struct curltime *pts);
/* Block/unblock ratelimiting. A blocked ratelimit has 0 tokens available. */
void Curl_rlimit_block(struct Curl_rlimit *r,
bool activate,
struct curltime ts);
struct curltime *pts);
#endif /* HEADER_Curl_rlimit_H */

View File

@@ -90,7 +90,7 @@ CURLcode Curl_req_soft_reset(struct SingleRequest *req,
CURLcode Curl_req_start(struct SingleRequest *req,
struct Curl_easy *data)
{
req->start = curlx_now();
req->start = data->progress.now;
return Curl_req_soft_reset(req, data);
}
@@ -219,7 +219,7 @@ static CURLcode xfer_send(struct Curl_easy *data,
size_t body_len = *pnwritten - hds_len;
Curl_debug(data, CURLINFO_DATA_OUT, buf + hds_len, body_len);
data->req.writebytecount += body_len;
Curl_pgrsSetUploadCounter(data, data->req.writebytecount);
Curl_pgrs_upload_inc(data, body_len);
}
}
}

View File

@@ -230,7 +230,7 @@ static CURLcode cw_download_write(struct Curl_easy *data,
if(!ctx->started_response &&
!(type & (CLIENTWRITE_INFO | CLIENTWRITE_CONNECT))) {
Curl_pgrsTime(data, TIMER_STARTTRANSFER);
Curl_rlimit_start(&data->progress.dl.rlimit, curlx_now());
Curl_rlimit_start(&data->progress.dl.rlimit, &data->progress.now);
ctx->started_response = TRUE;
}
@@ -303,9 +303,10 @@ static CURLcode cw_download_write(struct Curl_easy *data,
}
/* Update stats, write and report progress */
Curl_rlimit_drain(&data->progress.dl.rlimit, nwrite, curlx_now());
data->req.bytecount += nwrite;
Curl_pgrsSetDownloadCounter(data, data->req.bytecount);
if(nwrite) {
data->req.bytecount += nwrite;
Curl_pgrs_download_inc(data, nwrite);
}
if(excess_len) {
if(!data->req.ignorebody) {
@@ -1201,13 +1202,13 @@ CURLcode Curl_client_read(struct Curl_easy *data, char *buf, size_t blen,
DEBUGASSERT(data->req.reader_stack);
}
if(!data->req.reader_started) {
Curl_rlimit_start(&data->progress.ul.rlimit, curlx_now());
Curl_rlimit_start(&data->progress.ul.rlimit, &data->progress.now);
data->req.reader_started = TRUE;
}
if(Curl_rlimit_active(&data->progress.ul.rlimit)) {
curl_off_t ul_avail =
Curl_rlimit_avail(&data->progress.ul.rlimit, curlx_now());
Curl_rlimit_avail(&data->progress.ul.rlimit, &data->progress.now);
if(ul_avail <= 0) {
result = CURLE_OK;
*eos = FALSE;
@@ -1218,8 +1219,6 @@ CURLcode Curl_client_read(struct Curl_easy *data, char *buf, size_t blen,
}
result = Curl_creader_read(data, data->req.reader_stack, buf, blen,
nread, eos);
if(!result)
Curl_rlimit_drain(&data->progress.ul.rlimit, *nread, curlx_now());
out:
CURL_TRC_READ(data, "client_read(len=%zu) -> %d, nread=%zu, eos=%d",

View File

@@ -2813,7 +2813,8 @@ static CURLcode setopt_offt(struct Curl_easy *data, CURLoption option,
if(offt < 0)
return CURLE_BAD_FUNCTION_ARGUMENT;
s->max_send_speed = offt;
Curl_rlimit_init(&data->progress.ul.rlimit, offt, offt, curlx_now());
Curl_rlimit_init(&data->progress.ul.rlimit, offt, offt,
&data->progress.now);
break;
case CURLOPT_MAX_RECV_SPEED_LARGE:
/*
@@ -2823,7 +2824,8 @@ static CURLcode setopt_offt(struct Curl_easy *data, CURLoption option,
if(offt < 0)
return CURLE_BAD_FUNCTION_ARGUMENT;
s->max_recv_speed = offt;
Curl_rlimit_init(&data->progress.dl.rlimit, offt, offt, curlx_now());
Curl_rlimit_init(&data->progress.dl.rlimit, offt, offt,
&data->progress.now);
break;
case CURLOPT_RESUME_FROM_LARGE:
/*

View File

@@ -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

View File

@@ -1441,7 +1441,7 @@ static CURLcode smtp_connect(struct Curl_easy *data, bool *done)
Curl_sasl_init(&smtpc->sasl, data, &saslsmtp);
/* Initialise the pingpong layer */
Curl_pp_init(&smtpc->pp);
Curl_pp_init(&smtpc->pp, &data->progress.now);
/* Parse the URL options */
result = smtp_parse_url_options(data->conn, smtpc);

View File

@@ -133,7 +133,7 @@ CURLcode Curl_blockread_all(struct Curl_cfilter *cf,
*pnread = 0;
for(;;) {
timediff_t timeout_ms = Curl_timeleft_ms(data, NULL, TRUE);
timediff_t timeout_ms = Curl_timeleft_ms(data, TRUE);
if(timeout_ms < 0) {
/* we already got the timeout */
return CURLE_OPERATION_TIMEDOUT;

View File

@@ -1325,11 +1325,8 @@ static CURLcode telnet_do(struct Curl_easy *data, bool *done)
timediff_t interval_ms;
struct pollfd pfd[2];
int poll_cnt;
curl_off_t total_dl = 0;
curl_off_t total_ul = 0;
ssize_t snread;
#endif
struct curltime now;
bool keepon = TRUE;
char buffer[4 * 1024];
struct TELNET *tn;
@@ -1511,8 +1508,9 @@ static CURLcode telnet_do(struct Curl_easy *data, bool *done)
} /* switch */
if(data->set.timeout) {
now = curlx_now();
if(curlx_timediff_ms(now, conn->created) >= data->set.timeout) {
Curl_pgrs_now_set(data);
if(curlx_timediff_ms(data->progress.now, conn->created) >=
data->set.timeout) {
failf(data, "Time-out");
result = CURLE_OPERATION_TIMEDOUT;
keepon = FALSE;
@@ -1581,8 +1579,7 @@ static CURLcode telnet_do(struct Curl_easy *data, bool *done)
break;
}
total_dl += nread;
Curl_pgrsSetDownloadCounter(data, total_dl);
Curl_pgrs_download_inc(data, nread);
result = telrcv(data, tn, (unsigned char *)buffer, nread);
if(result) {
keepon = FALSE;
@@ -1622,8 +1619,7 @@ static CURLcode telnet_do(struct Curl_easy *data, bool *done)
keepon = FALSE;
break;
}
total_ul += snread;
Curl_pgrsSetUploadCounter(data, total_ul);
Curl_pgrs_upload_inc(data, (size_t)snread);
}
else if(snread < 0)
keepon = FALSE;
@@ -1632,8 +1628,9 @@ static CURLcode telnet_do(struct Curl_easy *data, bool *done)
} /* poll switch statement */
if(data->set.timeout) {
now = curlx_now();
if(curlx_timediff_ms(now, conn->created) >= data->set.timeout) {
Curl_pgrs_now_set(data);
if(curlx_timediff_ms(data->progress.now, conn->created) >=
data->set.timeout) {
failf(data, "Time-out");
result = CURLE_OPERATION_TIMEDOUT;
keepon = FALSE;

View File

@@ -205,7 +205,7 @@ static CURLcode tftp_set_timeouts(struct tftp_conn *state)
bool start = (state->state == TFTP_STATE_START);
/* Compute drop-dead time */
timeout_ms = Curl_timeleft_ms(state->data, NULL, start);
timeout_ms = Curl_timeleft_ms(state->data, start);
if(timeout_ms < 0) {
/* time-out, bail out, go home */
@@ -793,7 +793,7 @@ static CURLcode tftp_tx(struct tftp_conn *state, tftp_event_t event)
}
/* Update the progress meter */
k->writebytecount += state->sbytes;
Curl_pgrsSetUploadCounter(data, k->writebytecount);
Curl_pgrs_upload_inc(data, state->sbytes);
break;
case TFTP_EVENT_TIMEOUT:
@@ -1192,7 +1192,7 @@ static timediff_t tftp_state_timeout(struct tftp_conn *state,
if(event)
*event = TFTP_EVENT_NONE;
timeout_ms = Curl_timeleft_ms(state->data, NULL,
timeout_ms = Curl_timeleft_ms(state->data,
(state->state == TFTP_STATE_START));
if(timeout_ms < 0) {
state->error = TFTP_ERR_TIMEOUT;

View File

@@ -261,7 +261,7 @@ static CURLcode sendrecv_dl(struct Curl_easy *data,
if(bytestoread && Curl_rlimit_active(&data->progress.dl.rlimit)) {
curl_off_t dl_avail = Curl_rlimit_avail(&data->progress.dl.rlimit,
curlx_now());
&data->progress.now);
/* DEBUGF(infof(data, "dl_rlimit, available=%" FMT_OFF_T, dl_avail));
*/
/* In case of rate limited downloads: if this loop already got
@@ -364,12 +364,11 @@ static CURLcode sendrecv_ul(struct Curl_easy *data)
* Curl_sendrecv() is the low-level function to be called when data is to
* be read and written to/from the connection.
*/
CURLcode Curl_sendrecv(struct Curl_easy *data, struct curltime *nowp)
CURLcode Curl_sendrecv(struct Curl_easy *data)
{
struct SingleRequest *k = &data->req;
CURLcode result = CURLE_OK;
DEBUGASSERT(nowp);
if(Curl_xfer_is_blocked(data)) {
result = CURLE_OK;
goto out;
@@ -395,18 +394,20 @@ CURLcode Curl_sendrecv(struct Curl_easy *data, struct curltime *nowp)
goto out;
if(k->keepon) {
if(Curl_timeleft_ms(data, nowp, FALSE) < 0) {
if(Curl_timeleft_ms(data, FALSE) < 0) {
if(k->size != -1) {
failf(data, "Operation timed out after %" FMT_TIMEDIFF_T
" milliseconds with %" FMT_OFF_T " out of %"
FMT_OFF_T " bytes received",
curlx_timediff_ms(*nowp, data->progress.t_startsingle),
curlx_timediff_ms(data->progress.now,
data->progress.t_startsingle),
k->bytecount, k->size);
}
else {
failf(data, "Operation timed out after %" FMT_TIMEDIFF_T
" milliseconds with %" FMT_OFF_T " bytes received",
curlx_timediff_ms(*nowp, data->progress.t_startsingle),
curlx_timediff_ms(data->progress.now,
data->progress.t_startsingle),
k->bytecount);
}
result = CURLE_OPERATION_TIMEDOUT;
@@ -902,7 +903,7 @@ bool Curl_xfer_recv_is_paused(struct Curl_easy *data)
CURLcode Curl_xfer_pause_send(struct Curl_easy *data, bool enable)
{
CURLcode result = CURLE_OK;
Curl_rlimit_block(&data->progress.ul.rlimit, enable, curlx_now());
Curl_rlimit_block(&data->progress.ul.rlimit, enable, &data->progress.now);
if(!enable && Curl_creader_is_paused(data))
result = Curl_creader_unpause(data);
Curl_pgrsSendPause(data, enable);
@@ -912,7 +913,7 @@ CURLcode Curl_xfer_pause_send(struct Curl_easy *data, bool enable)
CURLcode Curl_xfer_pause_recv(struct Curl_easy *data, bool enable)
{
CURLcode result = CURLE_OK;
Curl_rlimit_block(&data->progress.dl.rlimit, enable, curlx_now());
Curl_rlimit_block(&data->progress.dl.rlimit, enable, &data->progress.now);
if(!enable && Curl_cwriter_is_paused(data))
result = Curl_cwriter_unpause(data);
Curl_conn_ev_data_pause(data, enable);

View File

@@ -34,7 +34,7 @@ void Curl_init_CONNECT(struct Curl_easy *data);
CURLcode Curl_pretransfer(struct Curl_easy *data);
CURLcode Curl_sendrecv(struct Curl_easy *data, struct curltime *nowp);
CURLcode Curl_sendrecv(struct Curl_easy *data);
CURLcode Curl_retry_request(struct Curl_easy *data, char **url);
bool Curl_meets_timecondition(struct Curl_easy *data, time_t timeofdoc);

View File

@@ -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) {

View File

@@ -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.

View File

@@ -802,6 +802,7 @@ struct pgrs_dir {
};
struct Progress {
struct curltime now; /* current time of processing */
time_t lastshow; /* time() of the last displayed progress meter or NULL to
force redraw at next call */
struct pgrs_dir ul;

View File

@@ -384,12 +384,13 @@ struct pkt_io_ctx {
ngtcp2_path_storage ps;
};
static void pktx_update_time(struct pkt_io_ctx *pktx,
static void pktx_update_time(struct Curl_easy *data,
struct pkt_io_ctx *pktx,
struct Curl_cfilter *cf)
{
struct cf_ngtcp2_ctx *ctx = cf->ctx;
vquic_ctx_update_time(&ctx->q);
vquic_ctx_update_time(data, &ctx->q);
pktx->ts = (ngtcp2_tstamp)ctx->q.last_op.tv_sec * NGTCP2_SECONDS +
(ngtcp2_tstamp)ctx->q.last_op.tv_usec * NGTCP2_MICROSECONDS;
}
@@ -398,10 +399,14 @@ static void pktx_init(struct pkt_io_ctx *pktx,
struct Curl_cfilter *cf,
struct Curl_easy *data)
{
struct cf_ngtcp2_ctx *ctx = cf->ctx;
pktx->cf = cf;
pktx->data = data;
ngtcp2_path_storage_zero(&pktx->ps);
pktx_update_time(pktx, cf);
vquic_ctx_set_time(data, &ctx->q);
pktx->ts = (ngtcp2_tstamp)ctx->q.last_op.tv_sec * NGTCP2_SECONDS +
(ngtcp2_tstamp)ctx->q.last_op.tv_usec * NGTCP2_MICROSECONDS;
}
static int cb_h3_acked_req_body(nghttp3_conn *conn, int64_t stream_id,
@@ -500,7 +505,8 @@ static int cf_ngtcp2_handshake_completed(ngtcp2_conn *tconn, void *user_data)
if(!ctx || !data)
return NGHTTP3_ERR_CALLBACK_FAILURE;
ctx->handshake_at = curlx_now();
Curl_pgrs_now_set(data); /* real change */
ctx->handshake_at = data->progress.now;
ctx->tls_handshake_complete = TRUE;
Curl_vquic_report_handshake(&ctx->tls, cf, data);
@@ -900,7 +906,7 @@ static CURLcode check_and_set_expiry(struct Curl_cfilter *cf,
pktx = &local_pktx;
}
else {
pktx_update_time(pktx, cf);
pktx_update_time(data, pktx, cf);
}
expiry = ngtcp2_conn_get_expiry(ctx->qconn);
@@ -1046,14 +1052,13 @@ static void cf_ngtcp2_ack_stream(struct Curl_cfilter *cf,
struct h3_stream_ctx *stream)
{
struct cf_ngtcp2_ctx *ctx = cf->ctx;
struct curltime now = curlx_now();
curl_off_t avail;
uint64_t ack_len = 0;
/* How many byte to ack on the stream? */
/* how much does rate limiting allow us to acknowledge? */
avail = Curl_rlimit_avail(&data->progress.dl.rlimit, now);
avail = Curl_rlimit_avail(&data->progress.dl.rlimit, &data->progress.now);
if(avail == CURL_OFF_T_MAX) { /* no rate limit, ack all */
ack_len = stream->download_unacked;
}
@@ -1077,6 +1082,7 @@ static int cb_h3_recv_data(nghttp3_conn *conn, int64_t stream3_id,
struct Curl_cfilter *cf = user_data;
struct cf_ngtcp2_ctx *ctx = cf->ctx;
struct Curl_easy *data = stream_user_data;
struct Curl_easy *calling = CF_DATA_CURRENT(cf);
struct h3_stream_ctx *stream = H3_STREAM_CTX(ctx, data);
(void)conn;
@@ -1084,6 +1090,8 @@ static int cb_h3_recv_data(nghttp3_conn *conn, int64_t stream3_id,
if(!stream)
return NGHTTP3_ERR_CALLBACK_FAILURE;
if(calling)
Curl_pgrs_now_update(data, calling);
h3_xfer_write_resp(cf, data, stream, (const char *)buf, blen, FALSE);
CURL_TRC_CF(data, cf, "[%" PRId64 "] DATA len=%zu", stream->id, blen);
@@ -1740,19 +1748,30 @@ denied:
return result;
}
struct cf_ngtcp2_recv_ctx {
struct pkt_io_ctx *pktx;
size_t pkt_count;
};
static CURLcode cf_ngtcp2_recv_pkts(const unsigned char *buf, size_t buflen,
size_t gso_size,
struct sockaddr_storage *remote_addr,
socklen_t remote_addrlen, int ecn,
void *userp)
{
struct pkt_io_ctx *pktx = userp;
struct cf_ngtcp2_recv_ctx *rctx = userp;
struct pkt_io_ctx *pktx = rctx->pktx;
struct cf_ngtcp2_ctx *ctx = pktx->cf->ctx;
ngtcp2_pkt_info pi;
ngtcp2_path path;
size_t offset, pktlen;
int rv;
if(!rctx->pkt_count) {
pktx_update_time(pktx->data, pktx, pktx->cf);
ngtcp2_path_storage_zero(&pktx->ps);
}
if(ecn)
CURL_TRC_CF(pktx->data, pktx->cf, "vquic_recv(len=%zu, gso=%zu, ecn=%x)",
buflen, gso_size, ecn);
@@ -1763,6 +1782,7 @@ static CURLcode cf_ngtcp2_recv_pkts(const unsigned char *buf, size_t buflen,
pi.ecn = (uint8_t)ecn;
for(offset = 0; offset < buflen; offset += gso_size) {
rctx->pkt_count++;
pktlen = ((offset + gso_size) <= buflen) ? gso_size : (buflen - offset);
rv = ngtcp2_conn_read_pkt(ctx->qconn, &path, &pi,
buf + offset, pktlen, pktx->ts);
@@ -1787,23 +1807,22 @@ static CURLcode cf_progress_ingress(struct Curl_cfilter *cf,
{
struct cf_ngtcp2_ctx *ctx = cf->ctx;
struct pkt_io_ctx local_pktx;
struct cf_ngtcp2_recv_ctx rctx;
CURLcode result = CURLE_OK;
if(!pktx) {
pktx_init(&local_pktx, cf, data);
pktx = &local_pktx;
}
else {
pktx_update_time(pktx, cf);
ngtcp2_path_storage_zero(&pktx->ps);
}
result = Curl_vquic_tls_before_recv(&ctx->tls, cf, data);
if(result)
return result;
rctx.pktx = pktx;
rctx.pkt_count = 0;
return vquic_recv_packets(cf, data, &ctx->q, 1000,
cf_ngtcp2_recv_pkts, pktx);
cf_ngtcp2_recv_pkts, &rctx);
}
/**
@@ -1929,7 +1948,7 @@ static CURLcode cf_progress_egress(struct Curl_cfilter *cf,
pktx = &local_pktx;
}
else {
pktx_update_time(pktx, cf);
pktx_update_time(data, pktx, cf);
ngtcp2_path_storage_zero(&pktx->ps);
}
@@ -2016,7 +2035,7 @@ static CURLcode cf_progress_egress(struct Curl_cfilter *cf,
}
return curlcode;
}
pktx_update_time(pktx, cf);
pktx_update_time(data, pktx, cf);
ngtcp2_conn_update_pkt_tx_time(ctx->qconn, pktx->ts);
}
return CURLE_OK;
@@ -2529,7 +2548,7 @@ static CURLcode cf_connect_start(struct Curl_cfilter *cf,
ctx->qlogfd = qfd; /* -1 if failure above */
quic_settings(ctx, data, pktx);
result = vquic_ctx_init(&ctx->q);
result = vquic_ctx_init(data, &ctx->q);
if(result)
return result;
@@ -2598,7 +2617,6 @@ static CURLcode cf_ngtcp2_connect(struct Curl_cfilter *cf,
struct cf_ngtcp2_ctx *ctx = cf->ctx;
CURLcode result = CURLE_OK;
struct cf_call_data save;
struct curltime now;
struct pkt_io_ctx pktx;
if(cf->connected) {
@@ -2614,13 +2632,12 @@ static CURLcode cf_ngtcp2_connect(struct Curl_cfilter *cf,
}
*done = FALSE;
now = curlx_now();
pktx_init(&pktx, cf, data);
CF_DATA_SAVE(save, cf, data);
if(!ctx->qconn) {
ctx->started_at = now;
ctx->started_at = data->progress.now;
result = cf_connect_start(cf, data, &pktx);
if(result)
goto out;
@@ -2794,7 +2811,8 @@ static bool cf_ngtcp2_conn_is_alive(struct Curl_cfilter *cf,
* it will close the connection when it expires. */
rp = ngtcp2_conn_get_remote_transport_params(ctx->qconn);
if(rp && rp->max_idle_timeout) {
timediff_t idletime_ms = curlx_timediff_ms(curlx_now(), ctx->q.last_io);
timediff_t idletime_ms =
curlx_timediff_ms(data->progress.now, ctx->q.last_io);
if(idletime_ms > 0) {
uint64_t max_idle_ms =
(uint64_t)(rp->max_idle_timeout / NGTCP2_MILLISECONDS);

View File

@@ -1161,7 +1161,7 @@ static CURLcode cf_osslq_ctx_start(struct Curl_cfilter *cf,
if(result)
goto out;
result = vquic_ctx_init(&ctx->q);
result = vquic_ctx_init(data, &ctx->q);
if(result)
goto out;
@@ -1748,7 +1748,6 @@ static CURLcode cf_osslq_connect(struct Curl_cfilter *cf,
struct cf_osslq_ctx *ctx = cf->ctx;
CURLcode result = CURLE_OK;
struct cf_call_data save;
struct curltime now;
int err;
if(cf->connected) {
@@ -1764,11 +1763,10 @@ static CURLcode cf_osslq_connect(struct Curl_cfilter *cf,
}
*done = FALSE;
now = curlx_now();
CF_DATA_SAVE(save, cf, data);
if(!ctx->tls.ossl.ssl) {
ctx->started_at = now;
ctx->started_at = data->progress.now;
result = cf_osslq_ctx_start(cf, data);
if(result)
goto out;
@@ -1778,7 +1776,7 @@ static CURLcode cf_osslq_connect(struct Curl_cfilter *cf,
int readable = SOCKET_READABLE(ctx->q.sockfd, 0);
if(readable > 0 && (readable & CURL_CSELECT_IN)) {
ctx->got_first_byte = TRUE;
ctx->first_byte_at = curlx_now();
ctx->first_byte_at = data->progress.now;
}
}
@@ -1799,14 +1797,14 @@ static CURLcode cf_osslq_connect(struct Curl_cfilter *cf,
/* if not recorded yet, take the timestamp before we called
* SSL_do_handshake() as the time we received the first packet. */
ctx->got_first_byte = TRUE;
ctx->first_byte_at = now;
ctx->first_byte_at = data->progress.now;
}
/* Record the handshake complete with a new time stamp. */
now = curlx_now();
ctx->handshake_at = now;
ctx->q.last_io = now;
Curl_pgrs_now_set(data);
ctx->handshake_at = data->progress.now;
ctx->q.last_io = data->progress.now;
CURL_TRC_CF(data, cf, "handshake complete after %" FMT_TIMEDIFF_T "ms",
curlx_timediff_ms(now, ctx->started_at));
curlx_timediff_ms(data->progress.now, ctx->started_at));
result = cf_osslq_verify_peer(cf, data);
if(!result) {
CURL_TRC_CF(data, cf, "peer verified");
@@ -1818,17 +1816,17 @@ static CURLcode cf_osslq_connect(struct Curl_cfilter *cf,
int detail = SSL_get_error(ctx->tls.ossl.ssl, err);
switch(detail) {
case SSL_ERROR_WANT_READ:
ctx->q.last_io = now;
ctx->q.last_io = data->progress.now;
CURL_TRC_CF(data, cf, "QUIC SSL_connect() -> WANT_RECV");
goto out;
case SSL_ERROR_WANT_WRITE:
ctx->q.last_io = now;
ctx->q.last_io = data->progress.now;
CURL_TRC_CF(data, cf, "QUIC SSL_connect() -> WANT_SEND");
result = CURLE_OK;
goto out;
#ifdef SSL_ERROR_WANT_ASYNC
case SSL_ERROR_WANT_ASYNC:
ctx->q.last_io = now;
ctx->q.last_io = data->progress.now;
CURL_TRC_CF(data, cf, "QUIC SSL_connect() -> WANT_ASYNC");
result = CURLE_OK;
goto out;
@@ -2242,7 +2240,7 @@ static bool cf_osslq_conn_is_alive(struct Curl_cfilter *cf,
goto out;
}
CURL_TRC_CF(data, cf, "negotiated idle timeout: %" PRIu64 "ms", idle_ms);
idletime = curlx_timediff_ms(curlx_now(), ctx->q.last_io);
idletime = curlx_timediff_ms(data->progress.now, ctx->q.last_io);
if(idle_ms && idletime > 0 && (uint64_t)idletime > idle_ms)
goto out;
}

View File

@@ -499,6 +499,7 @@ static void cf_quiche_recv_body(struct Curl_cfilter *cf,
}
static void cf_quiche_process_ev(struct Curl_cfilter *cf,
struct Curl_easy *calling,
struct Curl_easy *data,
struct h3_stream_ctx *stream,
quiche_h3_event *ev)
@@ -506,6 +507,7 @@ static void cf_quiche_process_ev(struct Curl_cfilter *cf,
if(!stream)
return;
Curl_pgrs_now_update(data, calling);
switch(quiche_h3_event_type(ev)) {
case QUICHE_H3_EVENT_HEADERS: {
struct cb_ctx cb_ctx;
@@ -561,6 +563,7 @@ struct cf_quich_disp_ctx {
uint64_t stream_id;
struct Curl_cfilter *cf;
struct Curl_multi *multi;
struct Curl_easy *calling;
quiche_h3_event *ev;
};
@@ -572,7 +575,7 @@ static bool cf_quiche_disp_event(uint32_t mid, void *val, void *user_data)
if(stream->id == dctx->stream_id) {
struct Curl_easy *sdata = Curl_multi_get_easy(dctx->multi, mid);
if(sdata)
cf_quiche_process_ev(dctx->cf, sdata, stream, dctx->ev);
cf_quiche_process_ev(dctx->cf, dctx->calling, sdata, stream, dctx->ev);
return FALSE; /* stop iterating */
}
return TRUE;
@@ -599,7 +602,7 @@ static CURLcode cf_poll_events(struct Curl_cfilter *cf,
stream = H3_STREAM_CTX(ctx, data);
if(stream && stream->id == (uint64_t)rv) {
/* event for calling transfer */
cf_quiche_process_ev(cf, data, stream, ev);
cf_quiche_process_ev(cf, data, data, stream, ev);
quiche_h3_event_free(ev);
if(stream->xfer_result)
return stream->xfer_result;
@@ -610,6 +613,7 @@ static CURLcode cf_poll_events(struct Curl_cfilter *cf,
struct cf_quich_disp_ctx dctx;
dctx.stream_id = (uint64_t)rv;
dctx.cf = cf;
dctx.calling = data;
dctx.multi = data->multi;
dctx.ev = ev;
Curl_uint32_hash_visit(&ctx->streams, cf_quiche_disp_event, &dctx);
@@ -865,7 +869,7 @@ static CURLcode cf_quiche_recv(struct Curl_cfilter *cf, struct Curl_easy *data,
*pnread = 0;
(void)buf;
(void)blen;
vquic_ctx_update_time(&ctx->q);
vquic_ctx_update_time(data, &ctx->q);
if(!stream)
return CURLE_RECV_ERROR;
@@ -1075,7 +1079,7 @@ static CURLcode cf_quiche_send(struct Curl_cfilter *cf, struct Curl_easy *data,
CURLcode result;
*pnwritten = 0;
vquic_ctx_update_time(&ctx->q);
vquic_ctx_update_time(data, &ctx->q);
result = cf_process_ingress(cf, data);
if(result)
@@ -1233,7 +1237,7 @@ static CURLcode cf_quiche_ctx_open(struct Curl_cfilter *cf,
DEBUGASSERT(ctx->q.sockfd != CURL_SOCKET_BAD);
DEBUGASSERT(ctx->initialized);
result = vquic_ctx_init(&ctx->q);
result = vquic_ctx_init(data, &ctx->q);
if(result)
return result;
@@ -1352,7 +1356,7 @@ static CURLcode cf_quiche_connect(struct Curl_cfilter *cf,
}
*done = FALSE;
vquic_ctx_update_time(&ctx->q);
vquic_ctx_update_time(data, &ctx->q);
if(!ctx->qconn) {
result = cf_quiche_ctx_open(cf, data);
@@ -1434,7 +1438,7 @@ static CURLcode cf_quiche_shutdown(struct Curl_cfilter *cf,
int err;
ctx->shutdown_started = TRUE;
vquic_ctx_update_time(&ctx->q);
vquic_ctx_update_time(data, &ctx->q);
err = quiche_conn_close(ctx->qconn, TRUE, 0, NULL, 0);
if(err) {
CURL_TRC_CF(data, cf, "error %d adding shutdown packet, "

View File

@@ -41,6 +41,7 @@
#include "curl_osslq.h"
#include "curl_quiche.h"
#include "../multiif.h"
#include "../progress.h"
#include "../rand.h"
#include "vquic.h"
#include "vquic_int.h"
@@ -75,7 +76,8 @@ void Curl_quic_ver(char *p, size_t len)
#endif
}
CURLcode vquic_ctx_init(struct cf_quic_ctx *qctx)
CURLcode vquic_ctx_init(struct Curl_easy *data,
struct cf_quic_ctx *qctx)
{
Curl_bufq_init2(&qctx->sendbuf, NW_CHUNK_SIZE, NW_SEND_CHUNKS,
BUFQ_OPT_SOFT_LIMIT);
@@ -94,7 +96,7 @@ CURLcode vquic_ctx_init(struct cf_quic_ctx *qctx)
}
}
#endif
vquic_ctx_update_time(qctx);
vquic_ctx_set_time(data, qctx);
return CURLE_OK;
}
@@ -104,9 +106,17 @@ void vquic_ctx_free(struct cf_quic_ctx *qctx)
Curl_bufq_free(&qctx->sendbuf);
}
void vquic_ctx_update_time(struct cf_quic_ctx *qctx)
void vquic_ctx_set_time(struct Curl_easy *data,
struct cf_quic_ctx *qctx)
{
qctx->last_op = curlx_now();
qctx->last_op = data->progress.now;
}
void vquic_ctx_update_time(struct Curl_easy *data,
struct cf_quic_ctx *qctx)
{
Curl_pgrs_now_set(data);
qctx->last_op = data->progress.now;
}
static CURLcode send_packet_no_gso(struct Curl_cfilter *cf,

View File

@@ -54,10 +54,15 @@ struct cf_quic_ctx {
#define H3_STREAM_CTX(ctx, data) \
(data ? Curl_uint32_hash_get(&(ctx)->streams, (data)->mid) : NULL)
CURLcode vquic_ctx_init(struct cf_quic_ctx *qctx);
CURLcode vquic_ctx_init(struct Curl_easy *data,
struct cf_quic_ctx *qctx);
void vquic_ctx_free(struct cf_quic_ctx *qctx);
void vquic_ctx_update_time(struct cf_quic_ctx *qctx);
void vquic_ctx_set_time(struct Curl_easy *data,
struct cf_quic_ctx *qctx);
void vquic_ctx_update_time(struct Curl_easy *data,
struct cf_quic_ctx *qctx);
void vquic_push_blocked_pkt(struct Curl_cfilter *cf,
struct cf_quic_ctx *qctx,

View File

@@ -2500,7 +2500,7 @@ static CURLcode myssh_block_statemach(struct Curl_easy *data,
if(result)
break;
left_ms = Curl_timeleft_ms(data, NULL, FALSE);
left_ms = Curl_timeleft_ms(data, FALSE);
if(left_ms < 0) {
failf(data, "Operation timed out");
return CURLE_OPERATION_TIMEDOUT;

View File

@@ -3121,13 +3121,13 @@ static CURLcode ssh_block_statemach(struct Curl_easy *data,
bool disconnect)
{
CURLcode result = CURLE_OK;
struct curltime dis = curlx_now();
struct curltime start = data->progress.now;
while((sshc->state != SSH_STOP) && !result) {
bool block;
timediff_t left_ms = 1000;
struct curltime now = curlx_now();
Curl_pgrs_now_set(data); /* timeout disconnect */
result = ssh_statemachine(data, sshc, sshp, &block);
if(result)
break;
@@ -3137,13 +3137,13 @@ static CURLcode ssh_block_statemach(struct Curl_easy *data,
if(result)
break;
left_ms = Curl_timeleft_ms(data, NULL, FALSE);
left_ms = Curl_timeleft_ms(data, FALSE);
if(left_ms < 0) {
failf(data, "Operation timed out");
return CURLE_OPERATION_TIMEDOUT;
}
}
else if(curlx_timediff_ms(now, dis) > 1000) {
else if(curlx_timediff_ms(data->progress.now, start) > 1000) {
/* disconnect timeout */
failf(data, "Disconnect timed out");
result = CURLE_OK;

View File

@@ -411,7 +411,7 @@ CURLcode Curl_gtls_shared_creds_create(struct Curl_easy *data,
}
shared->refcount = 1;
shared->time = curlx_now();
shared->time = data->progress.now;
*pcreds = shared;
return CURLE_OK;
}
@@ -562,8 +562,7 @@ static bool gtls_shared_creds_expired(const struct Curl_easy *data,
const struct gtls_shared_creds *sc)
{
const struct ssl_general_config *cfg = &data->set.general_ssl;
struct curltime now = curlx_now();
timediff_t elapsed_ms = curlx_timediff_ms(now, sc->time);
timediff_t elapsed_ms = curlx_timediff_ms(data->progress.now, sc->time);
timediff_t timeout_ms = cfg->ca_cache_timeout * (timediff_t)1000;
if(timeout_ms < 0)

View File

@@ -3219,8 +3219,7 @@ static bool ossl_cached_x509_store_expired(const struct Curl_easy *data,
if(cfg->ca_cache_timeout < 0)
return FALSE;
else {
struct curltime now = curlx_now();
timediff_t elapsed_ms = curlx_timediff_ms(now, mb->time);
timediff_t elapsed_ms = curlx_timediff_ms(data->progress.now, mb->time);
timediff_t timeout_ms = cfg->ca_cache_timeout * (timediff_t)1000;
return elapsed_ms >= timeout_ms;
@@ -3305,7 +3304,7 @@ static void ossl_set_cached_x509_store(struct Curl_cfilter *cf,
curlx_free(share->CAfile);
}
share->time = curlx_now();
share->time = data->progress.now;
share->store = store;
share->store_is_empty = is_empty;
share->CAfile = CAfile;

View File

@@ -1806,7 +1806,7 @@ schannel_recv_renegotiate(struct Curl_cfilter *cf, struct Curl_easy *data,
remaining = MAX_RENEG_BLOCK_TIME - elapsed;
if(blocking) {
timeout_ms = Curl_timeleft_ms(data, NULL, FALSE);
timeout_ms = Curl_timeleft_ms(data, FALSE);
if(timeout_ms < 0) {
result = CURLE_OPERATION_TIMEDOUT;
@@ -1959,7 +1959,7 @@ static CURLcode schannel_send(struct Curl_cfilter *cf, struct Curl_easy *data,
while(len > *pnwritten) {
size_t this_write = 0;
int what;
timediff_t timeout_ms = Curl_timeleft_ms(data, NULL, FALSE);
timediff_t timeout_ms = Curl_timeleft_ms(data, FALSE);
if(timeout_ms < 0) {
/* we already got the timeout */
failf(data, "schannel: timed out sending data "

View File

@@ -1370,8 +1370,10 @@ static CURLcode ssl_cf_connect(struct Curl_cfilter *cf,
if(!result && *done) {
cf->connected = TRUE;
if(connssl->state == ssl_connection_complete)
connssl->handshake_done = curlx_now();
if(connssl->state == ssl_connection_complete) {
Curl_pgrs_now_set(data);
connssl->handshake_done = data->progress.now;
}
/* Connection can be deferred when sending early data */
DEBUGASSERT(connssl->state == ssl_connection_complete ||
connssl->state == ssl_connection_deferred);
@@ -1839,7 +1841,7 @@ static CURLcode vtls_shutdown_blocking(struct Curl_cfilter *cf,
*done = FALSE;
while(!result && !*done && loop--) {
timeout_ms = Curl_shutdown_timeleft(cf->conn, cf->sockindex, NULL);
timeout_ms = Curl_shutdown_timeleft(data, cf->conn, cf->sockindex);
if(timeout_ms < 0) {
/* no need to continue if time is already up */
@@ -1886,7 +1888,7 @@ CURLcode Curl_ssl_cfilter_remove(struct Curl_easy *data,
if(cf->cft == &Curl_cft_ssl) {
bool done;
CURL_TRC_CF(data, cf, "shutdown and remove SSL, start");
Curl_shutdown_start(data, sockindex, 0, NULL);
Curl_shutdown_start(data, sockindex, 0);
result = vtls_shutdown_blocking(cf, data, send_shutdown, &done);
Curl_shutdown_clear(data, sockindex);
if(!result && !done) /* blocking failed? */

View File

@@ -727,8 +727,7 @@ static bool wssl_cached_x509_store_expired(const struct Curl_easy *data,
const struct wssl_x509_share *mb)
{
const struct ssl_general_config *cfg = &data->set.general_ssl;
struct curltime now = curlx_now();
timediff_t elapsed_ms = curlx_timediff_ms(now, mb->time);
timediff_t elapsed_ms = curlx_timediff_ms(data->progress.now, mb->time);
timediff_t timeout_ms = cfg->ca_cache_timeout * (timediff_t)1000;
if(timeout_ms < 0)
@@ -811,7 +810,7 @@ static void wssl_set_cached_x509_store(struct Curl_cfilter *cf,
curlx_free(share->CAfile);
}
share->time = curlx_now();
share->time = data->progress.now;
share->store = store;
share->CAfile = CAfile;
}

View File

@@ -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;

View File

@@ -72,7 +72,7 @@ via: 1.1 nghttpx
s/^server: nghttpx.*\r?\n//
</stripfile>
<limits>
Allocations: 150
Allocations: 155
Maximum allocated: 1800000
</limits>
</verify>

View File

@@ -147,7 +147,8 @@ static CURLcode test_unit1303(const char *arg)
timediff_t timeout;
NOW(run[i].now_s, run[i].now_us);
TIMEOUTS(run[i].timeout_ms, run[i].connecttimeout_ms);
timeout = Curl_timeleft_ms(easy, &now, run[i].connecting);
easy->progress.now = now;
timeout = Curl_timeleft_ms(easy, run[i].connecting);
if(timeout != run[i].result)
fail(run[i].comment);
}

View File

@@ -78,6 +78,7 @@ static CURLcode test_unit1399(const char *arg)
struct Curl_easy data;
struct curltime now = curlx_now();
data.progress.now = now;
data.progress.t_nslookup = 0;
data.progress.t_connect = 0;
data.progress.t_appconnect = 0;

View File

@@ -33,71 +33,72 @@ static CURLcode test_unit3216(const char *arg)
/* A ratelimit that is unlimited */
ts = curlx_now();
Curl_rlimit_init(&r, 0, 0, ts);
fail_unless(Curl_rlimit_avail(&r, ts) == CURL_OFF_T_MAX, "inf");
Curl_rlimit_drain(&r, 1000000, ts);
fail_unless(Curl_rlimit_avail(&r, ts) == CURL_OFF_T_MAX, "drain keep inf");
fail_unless(Curl_rlimit_wait_ms(&r, ts) == 0, "inf never waits");
Curl_rlimit_init(&r, 0, 0, &ts);
fail_unless(Curl_rlimit_avail(&r, &ts) == CURL_OFF_T_MAX, "inf");
Curl_rlimit_drain(&r, 1000000, &ts);
fail_unless(Curl_rlimit_avail(&r, &ts) == CURL_OFF_T_MAX, "drain keep inf");
fail_unless(Curl_rlimit_wait_ms(&r, &ts) == 0, "inf never waits");
Curl_rlimit_block(&r, TRUE, ts);
fail_unless(Curl_rlimit_avail(&r, ts) == 0, "inf blocked to 0");
Curl_rlimit_drain(&r, 1000000, ts);
fail_unless(Curl_rlimit_avail(&r, ts) == 0, "blocked inf");
Curl_rlimit_block(&r, FALSE, ts);
fail_unless(Curl_rlimit_avail(&r, ts) == CURL_OFF_T_MAX,
Curl_rlimit_block(&r, TRUE, &ts);
fail_unless(Curl_rlimit_avail(&r, &ts) == 0, "inf blocked to 0");
Curl_rlimit_drain(&r, 1000000, &ts);
fail_unless(Curl_rlimit_avail(&r, &ts) == 0, "blocked inf");
Curl_rlimit_block(&r, FALSE, &ts);
fail_unless(Curl_rlimit_avail(&r, &ts) == CURL_OFF_T_MAX,
"unblocked unlimited");
/* A ratelimit that give 10 tokens per second */
ts = curlx_now();
Curl_rlimit_init(&r, 10, 0, ts);
fail_unless(Curl_rlimit_avail(&r, ts) == 10, "initial 10");
Curl_rlimit_drain(&r, 5, ts);
fail_unless(Curl_rlimit_avail(&r, ts) == 5, "drain to 5");
Curl_rlimit_drain(&r, 3, ts);
fail_unless(Curl_rlimit_avail(&r, ts) == 2, "drain to 2");
Curl_rlimit_init(&r, 10, 0, &ts);
fail_unless(Curl_rlimit_avail(&r, &ts) == 10, "initial 10");
Curl_rlimit_drain(&r, 5, &ts);
fail_unless(Curl_rlimit_avail(&r, &ts) == 5, "drain to 5");
Curl_rlimit_drain(&r, 3, &ts);
fail_unless(Curl_rlimit_avail(&r, &ts) == 2, "drain to 2");
ts.tv_usec += 1000; /* 1ms */
Curl_rlimit_drain(&r, 3, ts);
fail_unless(Curl_rlimit_avail(&r, ts) == -1, "drain to -1");
fail_unless(Curl_rlimit_wait_ms(&r, ts) == 999, "wait 999ms");
Curl_rlimit_drain(&r, 3, &ts);
fail_unless(Curl_rlimit_avail(&r, &ts) == -1, "drain to -1");
fail_unless(Curl_rlimit_wait_ms(&r, &ts) == 999, "wait 999ms");
ts.tv_usec += 1000; /* 1ms */
fail_unless(Curl_rlimit_wait_ms(&r, ts) == 998, "wait 998ms");
fail_unless(Curl_rlimit_wait_ms(&r, &ts) == 998, "wait 998ms");
ts.tv_sec += 1;
fail_unless(Curl_rlimit_avail(&r, ts) == 9, "10 inc per sec");
fail_unless(Curl_rlimit_avail(&r, &ts) == 9, "10 inc per sec");
ts.tv_sec += 1;
fail_unless(Curl_rlimit_avail(&r, ts) == 19, "10 inc per sec(2)");
fail_unless(Curl_rlimit_avail(&r, &ts) == 19, "10 inc per sec(2)");
Curl_rlimit_block(&r, TRUE, curlx_now());
fail_unless(Curl_rlimit_avail(&r, curlx_now()) == 0, "10 blocked to 0");
Curl_rlimit_block(&r, FALSE, curlx_now());
fail_unless(Curl_rlimit_avail(&r, curlx_now()) == 10, "unblocked 10");
ts = curlx_now();
Curl_rlimit_block(&r, TRUE, &ts);
fail_unless(Curl_rlimit_avail(&r, &ts) == 0, "10 blocked to 0");
Curl_rlimit_block(&r, FALSE, &ts);
fail_unless(Curl_rlimit_avail(&r, &ts) == 10, "unblocked 10");
/* A ratelimit that give 10 tokens per second, max burst 15/s */
ts = curlx_now();
Curl_rlimit_init(&r, 10, 15, ts);
fail_unless(Curl_rlimit_avail(&r, ts) == 10, "initial 10");
Curl_rlimit_drain(&r, 5, ts);
fail_unless(Curl_rlimit_avail(&r, ts) == 5, "drain to 5");
Curl_rlimit_drain(&r, 3, ts);
fail_unless(Curl_rlimit_avail(&r, ts) == 2, "drain to 2");
Curl_rlimit_drain(&r, 3, ts);
fail_unless(Curl_rlimit_avail(&r, ts) == -1, "drain to -1");
Curl_rlimit_init(&r, 10, 15, &ts);
fail_unless(Curl_rlimit_avail(&r, &ts) == 10, "initial 10");
Curl_rlimit_drain(&r, 5, &ts);
fail_unless(Curl_rlimit_avail(&r, &ts) == 5, "drain to 5");
Curl_rlimit_drain(&r, 3, &ts);
fail_unless(Curl_rlimit_avail(&r, &ts) == 2, "drain to 2");
Curl_rlimit_drain(&r, 3, &ts);
fail_unless(Curl_rlimit_avail(&r, &ts) == -1, "drain to -1");
ts.tv_sec += 1;
fail_unless(Curl_rlimit_avail(&r, ts) == 9, "10 inc per sec");
fail_unless(Curl_rlimit_avail(&r, &ts) == 9, "10 inc per sec");
ts.tv_sec += 1;
fail_unless(Curl_rlimit_avail(&r, ts) == 15, "10/15 burst limit");
fail_unless(Curl_rlimit_avail(&r, &ts) == 15, "10/15 burst limit");
ts.tv_sec += 1;
fail_unless(Curl_rlimit_avail(&r, ts) == 15, "10/15 burst limit(2)");
Curl_rlimit_drain(&r, 15, ts);
fail_unless(Curl_rlimit_avail(&r, ts) == 0, "drain to 0");
fail_unless(Curl_rlimit_wait_ms(&r, ts) == 1000, "wait 1 sec");
fail_unless(Curl_rlimit_avail(&r, &ts) == 15, "10/15 burst limit(2)");
Curl_rlimit_drain(&r, 15, &ts);
fail_unless(Curl_rlimit_avail(&r, &ts) == 0, "drain to 0");
fail_unless(Curl_rlimit_wait_ms(&r, &ts) == 1000, "wait 1 sec");
ts.tv_usec += 500000; /* half a sec, cheating on second carry */
fail_unless(Curl_rlimit_avail(&r, ts) == 0, "0 after 0.5 sec");
fail_unless(Curl_rlimit_wait_ms(&r, ts) == 500, "wait 0.5 sec");
fail_unless(Curl_rlimit_avail(&r, &ts) == 0, "0 after 0.5 sec");
fail_unless(Curl_rlimit_wait_ms(&r, &ts) == 500, "wait 0.5 sec");
ts.tv_sec += 1;
fail_unless(Curl_rlimit_avail(&r, ts) == 10, "10 after 1.5 sec");
fail_unless(Curl_rlimit_wait_ms(&r, ts) == 0, "wait 0");
fail_unless(Curl_rlimit_avail(&r, &ts) == 10, "10 after 1.5 sec");
fail_unless(Curl_rlimit_wait_ms(&r, &ts) == 0, "wait 0");
ts.tv_usec += 500000; /* half a sec, cheating on second carry */
fail_unless(Curl_rlimit_avail(&r, ts) == 15, "10 after 2 sec");
fail_unless(Curl_rlimit_avail(&r, &ts) == 15, "10 after 2 sec");
UNITTEST_END_SIMPLE
}