mirror of
https://github.com/curl/curl.git
synced 2026-04-14 21:31:42 +03:00
time-keeping: keep timestamp in multi, always update
Always use curlx_now() when calling Curl_pgrs_now(data). Tests with the "manual" updates to now proved differ more then 100ms in parallel testing. Add `curlx_nowp()` to set current time into a struct curltime. Add `curlx_ptimediff_ms() and friends, passing pointers. Update documentation. Closes #19998
This commit is contained in:
parent
308c347c8b
commit
b4be1f271e
61 changed files with 471 additions and 502 deletions
|
|
@ -11,10 +11,6 @@ time function is `curlx_now()` and it uses a **monotonic** clock on most platfor
|
|||
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
|
||||
|
|
@ -34,49 +30,16 @@ in the correct order: *queue -> nameloopup -> connect -> appconnect ->...*.)
|
|||
|
||||
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.
|
||||
* Keep a "now" timestamp in the multi handle. Keep a fallback "now" timestamp in the easy handle.
|
||||
* Always use `Curl_pgrs_now(data)` to get the current time of a transfer.
|
||||
* Do not use `curlx_now()` directly for transfer handling (exceptions apply for loops).
|
||||
|
||||
In addition to *setting* `data->progress.now` this timestamp can be *advanced* using 2 new methods:
|
||||
This has the following advantages:
|
||||
|
||||
* `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.
|
||||
* No need to pass a `struct curltime` around or pass a pointer to an outdated timestamp to other functions.
|
||||
* No need to calculate the exact `now` until it is really used.
|
||||
* Passing a `const` pointer is better than struct passing. Updating and passing a pointer to the same memory location for all transfers is even better.
|
||||
|
||||
### Time Advancing Loops
|
||||
Caveats:
|
||||
|
||||
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.
|
||||
* do not store the pointer returned by `Curl_pgrs_now(data)` anywhere that outlives the current code invocation.
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue