multi: timeout improvements

- Move expire timeout code from multi into splay.c
- keep a "time_base" timestamp to calculate timediff_t for
  actual timeout values. Unfortunately this means our
  timeouts will go wrong after ~500,000 years of continuous
  operations...
- use timediff_t as key in splay instead of curltime
- use timediff_t in transfers expire times instead of curltime
- re-comment splay.c for better understanding how it works
- replace splay nodes double-linked "same" list with a single link,
  we almost never have duplicate keys
- keep transfer `mid` in splay nodes instead of the transfer pointer
- keep registered bit in splay node for tracking instead of separate
  bit in transfer
- adapt unit1309.c to changes in timediff_t and mid

Closes #22584
This commit is contained in:
Stefan Eissing 2026-08-14 14:55:49 +02:00 committed by Daniel Stenberg
parent d854ab4673
commit 3d6d93a6be
No known key found for this signature in database
GPG key ID: 5CC908FDB71E12C2
19 changed files with 600 additions and 458 deletions

View file

@ -38,10 +38,10 @@ static void splayprint(struct Curl_tree *t, int d, char output)
curl_mprintf(" ");
if(output) {
curl_mprintf("%ld.%ld[%d]", (long)t->key.tv_sec, (long)t->key.tv_usec, i);
curl_mprintf("0.%ld[%d]", (long)t->key, i);
}
for(count = 0, node = t->samen; node != t; node = node->samen, count++)
for(count = 0, node = t->same; node; node = node->same, count++)
;
if(output) {
@ -63,67 +63,96 @@ static CURLcode test_unit1309(const char *arg)
struct Curl_tree *root, *removed;
struct Curl_tree nodes[NUM_NODES * 3];
size_t storage[NUM_NODES * 3];
int rc;
int i, j;
struct curltime tv_now = { 0, 0 };
size_t i, j;
timediff_t tv_now = 0, timeout_last;
root = NULL; /* the empty tree */
/* add nodes */
for(i = 0; i < NUM_NODES; i++) {
struct curltime key;
timediff_t key;
key.tv_sec = 0;
key.tv_usec = (541 * i) % 1023;
storage[i] = key.tv_usec;
Curl_splayset(&nodes[i], &storage[i]);
root = Curl_splayinsert(&key, root, &nodes[i]);
key = (541 * i) % 1023;
root = Curl_splayinsert(key, root, &nodes[i], (uint32_t)key);
fail_unless(nodes[i].registered, "node should have been registered");
}
puts("Result:");
splayprint(root, 0, 1);
for(i = 0; i < NUM_NODES; i++) {
int rem = (i + 7) % NUM_NODES;
size_t rem = (i + 7) % NUM_NODES;
curl_mprintf("Tree look:\n");
splayprint(root, 0, 1);
curl_mprintf("remove pointer %d, payload %zu\n", rem,
*(size_t *)Curl_splayget(&nodes[rem]));
curl_mprintf("remove node %d, payload %u\n", (int)rem,
Curl_splayget(&nodes[rem]));
rc = Curl_splayremove(root, &nodes[rem], &root);
if(rc) {
/* failed! */
curl_mprintf("remove %d failed!\n", rem);
curl_mprintf("remove %d failed!\n", (int)rem);
fail("remove");
}
fail_unless(!nodes[rem].registered, "node should not be registered");
rc = Curl_splayremove(root, &nodes[rem], &root);
if(!rc) {
/* failed! */
curl_mprintf("double remove %d did not fail!\n", (int)rem);
fail("double remove");
}
}
fail_unless(!root, "tree not empty after removing all nodes");
/* rebuild tree */
for(i = 0; i < NUM_NODES; i++) {
struct curltime key;
timediff_t key;
key.tv_sec = 0;
key.tv_usec = (541 * i) % 1023;
key = (541 * i) % 1023;
/* add some nodes with the same key */
for(j = 0; j <= i % 3; j++) {
storage[(i * 3) + j] = (key.tv_usec * 10) + j;
Curl_splayset(&nodes[(i * 3) + j], &storage[(i * 3) + j]);
root = Curl_splayinsert(&key, root, &nodes[(i * 3) + j]);
root = Curl_splayinsert(key, root, &nodes[(i * 3) + j],
(uint32_t)(key * 10 + j));
}
}
removed = NULL;
for(i = 0; i <= 1100; i += 100) {
curl_mprintf("Removing nodes not larger than %d\n", i);
tv_now.tv_usec = i;
root = Curl_splaygetbest(&tv_now, root, &removed);
curl_mprintf("Removing nodes not larger than %d\n", (int)i);
tv_now = i;
root = Curl_splaygetbest(tv_now, root, &removed);
while(removed) {
curl_mprintf("removed payload %zu[%zu]\n",
*(size_t *)Curl_splayget(removed) / 10,
*(size_t *)Curl_splayget(removed) % 10);
root = Curl_splaygetbest(&tv_now, root, &removed);
curl_mprintf("removed payload %u[%u]\n",
Curl_splayget(removed) / 10,
Curl_splayget(removed) % 10);
root = Curl_splaygetbest(tv_now, root, &removed);
}
}
fail_unless(!root, "tree not empty when it should be");
/* rebuild tree with duplicate values */
for(i = 0; i < NUM_NODES; i++) {
timediff_t key = (541 * i) % 128;
root = Curl_splayinsert(key, root, &nodes[i], (uint32_t)i);
}
removed = NULL;
timeout_last = -1;
for(i = 0; i <= 128; i += 32) {
curl_mprintf("Removing nodes not larger than %d\n", (int)i);
root = Curl_splaygetbest(i, root, &removed);
while(removed) {
curl_mprintf("removed payload %u[timeout=%d]\n",
Curl_splayget(removed), (int)removed->key);
if(removed->key < timeout_last) {
/* failed! */
curl_mprintf("remove timeout %d is smaller than last %d!\n",
(int)removed->key, (int)timeout_last);
fail("wrong timeout order");
}
timeout_last = removed->key;
root = Curl_splaygetbest(i, root, &removed);
}
}

View file

@ -48,7 +48,7 @@ static void checksize(const char *name, size_t size, size_t allowed)
These sizes were chosen with platforms with 64-bit pointers in mind. */
#define MAX_CURL_EASY 5370
#define MAX_CONNECTDATA 1300
#define MAX_CURL_MULTI 920
#define MAX_CURL_MULTI 928
#define MAX_CURL_HTTPPOST 112
#define MAX_CURL_SLIST 16
#define MAX_CURL_KHKEY 24