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
No known key found for this signature in database
GPG key ID: 5CC908FDB71E12C2
67 changed files with 598 additions and 458 deletions

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