cwist
Modern C web framework with libttak runtime
Loading...
Searching...
No Matches
big_dumb_reply.c
Go to the documentation of this file.
1#define _POSIX_C_SOURCE 200809L
6#include <cwist/core/macros.h>
7#include <ttak/mem/epoch.h>
8#ifdef CWIST_DB
9#include <sqlite3.h>
10#endif
11#include <stdlib.h>
12#include <string.h>
13#include <stdio.h>
14#include <limits.h>
15
30#define BDR_BUCKETS 1024
31#define BDR_GC_SWEEP 8
32#define BDR_JANITOR_PERIOD 64
33#define BDR_DEFAULT_MAX_BYTES CWIST_MIB(32)
34#define BDR_DEFAULT_ENTRY_TTL 300
35#define BDR_DEFAULT_REVALIDATE_HITS 100000
36
40static const uint8_t BDR_KEY[16] = {0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
41 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f};
42
43/* --- Blob lifetime -------------------------------------------------------- */
44
53static void bdr_blob_free_cb(void *ptr) {
54 bdr_blob_t *blob = (bdr_blob_t *)ptr;
55 if (!blob) return;
56 /* Read the fields before releasing: for inline blobs mem == blob, so the
57 * free below clobbers the very bytes the branch would inspect (glibc
58 * writes its tcache links into freed chunks). */
59 void *mem = blob->mem;
60 void (*free_fn)(void *) = blob->free_fn;
61 free_fn(mem);
62 if (mem != ptr) cwist_free(blob);
63}
64
69static void bdr_blob_retire(bdr_blob_t *blob) {
70 if (blob) ttak_epoch_retire(blob, bdr_blob_free_cb);
71}
72
78static bdr_blob_t *bdr_blob_learn(const void *data, size_t len) {
79 bdr_blob_t *blob = cwist_alloc(sizeof(bdr_blob_t) + len);
80 if (!blob) return NULL;
81 blob->mem = blob;
82 blob->free_fn = cwist_free;
83 blob->len = len;
84 blob->data = (const unsigned char *)(blob + 1);
85 memcpy((void *)blob->data, data, len);
86 return blob;
87}
88
95static bdr_blob_t *bdr_blob_wrap(void *mem, size_t len, void (*free_fn)(void *)) {
96 bdr_blob_t *blob = cwist_alloc(sizeof(bdr_blob_t));
97 if (!blob) return NULL;
98 blob->mem = mem;
99 blob->free_fn = free_fn ? free_fn : cwist_free;
100 blob->len = len;
101 blob->data = (const unsigned char *)mem;
102 return blob;
103}
104
110static void bdr_bytes_add(cwist_bdr_t *bdr, size_t len) {
112}
113
119static void bdr_bytes_sub(cwist_bdr_t *bdr, size_t len) {
120 size_t cur = atomic_load_explicit(&bdr->current_bytes, memory_order_relaxed);
121 while (cur > 0) {
122 size_t next = cur >= len ? cur - len : 0;
123 if (atomic_compare_exchange_weak_explicit(&bdr->current_bytes, &cur, next,
125 return;
126 }
127}
128
139static void bdr_entry_publish(cwist_bdr_t *bdr, bdr_entry_t *entry, bdr_blob_t *blob) {
141 if (blob) bdr_bytes_add(bdr, blob->len);
142 if (old) {
143 bdr_bytes_sub(bdr, old->len);
144 bdr_blob_retire(old);
145 }
146}
147
148/* --- Lookup helpers ------------------------------------------------------- */
149
156static uint64_t bdr_hash(const char *method, const char *path) {
157 uint64_t h = siphash24((const void *)path, strlen(path), BDR_KEY);
158 h ^= (uint64_t)(method[0]);
159 return h;
160}
161
169static uint64_t bdr_hash_n(const char *method, const char *path, size_t path_len) {
170 uint64_t h = siphash24((const void *)path, path_len, BDR_KEY);
171 h ^= (uint64_t)(method[0]);
172 return h;
173}
174
181static uint64_t bdr_hash_data(const void *data, size_t len) {
182 return siphash24(data, len, BDR_KEY);
183}
184
197static bool bdr_entry_should_decay(const cwist_bdr_t *bdr, const bdr_entry_t *entry, time_t now) {
198 if (!bdr || !entry) return false;
200 if (bdr->max_entry_age_sec > 0 && created > 0) {
201 if (now - (time_t)created > bdr->max_entry_age_sec) return true;
202 }
203 if (atomic_load_explicit(&entry->is_stable, memory_order_relaxed) && bdr->revalidate_hits > 0 &&
204 atomic_load_explicit(&entry->hits, memory_order_relaxed) >= bdr->revalidate_hits) {
205 return true;
206 }
207 return false;
208}
209
220static bdr_entry_t *bdr_find(cwist_bdr_t *bdr, uint64_t req_h) {
221 size_t idx = req_h % bdr->bucket_count;
223 while (curr) {
224 if (curr->request_hash == req_h &&
226 return curr;
227 }
229 }
230 return NULL;
231}
232
245static bdr_entry_t *bdr_find_or_insert(cwist_bdr_t *bdr, uint64_t req_h) {
246 bdr_entry_t *found = bdr_find(bdr, req_h);
247 if (found) return found;
248
249 bdr_entry_t *entry = cwist_alloc(sizeof(bdr_entry_t));
250 if (!entry) return NULL;
251 entry->request_hash = req_h;
252 atomic_init(&entry->response_hash, 0);
253 atomic_init(&entry->is_stable, false);
254 atomic_init(&entry->retired, false);
255 atomic_init(&entry->blob, NULL);
256 atomic_init(&entry->hits, 0);
258 entry->revalidate = NULL;
259 entry->revalidate_arg = NULL;
260 entry->retire_next = NULL;
261
262 size_t idx = req_h % bdr->bucket_count;
264 do {
265 /* A concurrent insert of the same key wins; ours becomes garbage. */
266 bdr_entry_t *race = bdr_find(bdr, req_h);
267 if (race) {
268 cwist_free(entry);
269 return race;
270 }
272 } while (!atomic_compare_exchange_weak_explicit(&bdr->buckets[idx], &head, entry,
274 return entry;
275}
276
277/* --- Janitor (mutex-serialized, runs every BDR_JANITOR_PERIOD learns) ----- */
278
291static void bdr_retire_entry(cwist_bdr_t *bdr, size_t idx, bdr_entry_t *prev, bdr_entry_t *entry) {
293 if (prev) {
296 } else {
297 atomic_store_explicit(&bdr->buckets[idx],
300 }
301 bdr_entry_publish(bdr, entry, NULL);
302 entry->retire_next = bdr->retired_entries;
303 bdr->retired_entries = entry;
304}
305
314static void bdr_sweep(cwist_bdr_t *bdr, size_t steps) {
315 if (!bdr || bdr->bucket_count == 0 || steps == 0) return;
316 time_t now = time(NULL);
317 for (size_t i = 0; i < steps; ++i) {
318 size_t idx = bdr->gc_cursor % bdr->bucket_count;
319 bdr->gc_cursor = (bdr->gc_cursor + 1) % bdr->bucket_count;
320 bdr_entry_t *prev = NULL;
322 while (curr) {
325 bdr_entry_should_decay(bdr, curr, now)) {
326 bdr_retire_entry(bdr, idx, prev, curr);
327 } else {
328 prev = curr;
329 }
330 curr = next;
331 }
332 }
333}
334
345static bool bdr_trim_oldest(cwist_bdr_t *bdr, time_t now) {
346 size_t victim_idx = SIZE_MAX;
350
351 for (size_t i = 0; i < bdr->bucket_count; ++i) {
352 bdr_entry_t *prev = NULL;
354 while (curr) {
358 if (!victim || created < oldest) {
359 victim = curr;
360 victim_prev = prev;
361 victim_idx = i;
362 oldest = created;
363 }
364 }
365 prev = curr;
367 }
368 }
369
370 if (!victim) return false;
371 bdr_retire_entry(bdr, victim_idx, victim_prev, victim);
372 return true;
373}
374
386static void bdr_check_ram(cwist_bdr_t *bdr) {
387#if defined(__EMSCRIPTEN__) || !defined(CWIST_DB)
388 /* No SQLite disk fallback in the WASM core or when the DB tier is
389 * compiled out (CWIST_DB=0); the in-RAM cache stands alone. */
390 (void)bdr;
391 return;
392#else
393 if (atomic_load_explicit(&bdr->is_disk_mode, memory_order_relaxed)) return;
394
396 printf("[BDR] Low RAM. Switching to Disk Cache.\n");
397 if (sqlite3_open("cwist_bdr_fallback.db", &bdr->disk_db) == SQLITE_OK) {
398 char *err = NULL;
399 sqlite3_exec(bdr->disk_db,
400 "CREATE TABLE IF NOT EXISTS bdr (hash INTEGER PRIMARY KEY, blob BLOB);",
401 NULL, NULL, &err);
402 if (err) sqlite3_free(err);
403
404 sqlite3_exec(bdr->disk_db, "BEGIN TRANSACTION;", NULL, NULL, NULL);
405 for (size_t i = 0; i < bdr->bucket_count; i++) {
407 while (curr) {
412 if (blob) {
414 sqlite3_prepare_v2(bdr->disk_db,
415 "INSERT INTO bdr (hash, blob) VALUES (?, ?);", -1,
416 &stmt, NULL);
417 sqlite3_bind_int64(stmt, 1, curr->request_hash);
418 sqlite3_bind_blob(stmt, 2, blob->data, (int)blob->len, SQLITE_STATIC);
421 }
422 }
425 bdr_entry_publish(bdr, curr, NULL);
426 curr->retire_next = bdr->retired_entries;
427 bdr->retired_entries = curr;
428 }
429 curr = next;
430 }
432 }
433 sqlite3_exec(bdr->disk_db, "COMMIT;", NULL, NULL, NULL);
434 atomic_store_explicit(&bdr->is_disk_mode, true, memory_order_release);
435 }
436 }
437#endif
438}
439
450static void bdr_janitor_tick(cwist_bdr_t *bdr) {
452 if (n % BDR_JANITOR_PERIOD != 0) return;
453 if (pthread_mutex_trylock(&bdr->lock) != 0) return; /* another tick holds it */
454 bdr_check_ram(bdr);
455 bdr_sweep(bdr, BDR_GC_SWEEP);
456 if (bdr->max_bytes > 0) {
457 time_t now = time(NULL);
458 while (atomic_load_explicit(&bdr->current_bytes, memory_order_relaxed) > bdr->max_bytes) {
459 if (!bdr_trim_oldest(bdr, now)) break;
460 }
461 }
463}
464
465/* --- Lifecycle ------------------------------------------------------------ */
466
477 if (!bdr) return NULL;
478 if (pthread_mutex_init(&bdr->lock, NULL) != 0) {
480 return NULL;
481 }
482 bdr->bucket_count = BDR_BUCKETS;
483 bdr->buckets = cwist_alloc_array(BDR_BUCKETS, sizeof(bdr_entry_t *));
484 if (!bdr->buckets) {
487 return NULL;
488 }
489 bdr->latency_threshold_ms = 10;
490 atomic_init(&bdr->current_bytes, 0);
491 bdr->max_bytes = BDR_DEFAULT_MAX_BYTES;
492 bdr->max_entry_age_sec = BDR_DEFAULT_ENTRY_TTL;
493 bdr->revalidate_hits = BDR_DEFAULT_REVALIDATE_HITS;
494 bdr->gc_cursor = 0;
495 atomic_init(&bdr->put_count, 0);
496 bdr->retired_entries = NULL;
497 bdr->disk_db = NULL;
498 atomic_init(&bdr->is_disk_mode, false);
499 return bdr;
500}
501
512 if (!bdr) return;
513 pthread_mutex_lock(&bdr->lock);
514 for (size_t i = 0; i < bdr->bucket_count; i++) {
515 bdr_entry_t *curr = bdr->buckets[i];
516 while (curr) {
517 bdr_entry_t *next = curr->next;
518 if (curr == NULL) break;
519 if (curr->blob != NULL)
520 bdr_blob_free_cb(atomic_load_explicit(&curr->blob, memory_order_relaxed));
522 curr = next;
523 }
524 }
525 while (bdr->retired_entries) {
526 bdr_entry_t *next = bdr->retired_entries->retire_next;
527 bdr_blob_free_cb(atomic_load_explicit(&bdr->retired_entries->blob, memory_order_relaxed));
528 cwist_free(bdr->retired_entries);
529 bdr->retired_entries = next;
530 }
531 cwist_free(bdr->buckets);
532#if !defined(__EMSCRIPTEN__) && defined(CWIST_DB)
533 if (bdr->disk_db) {
534 sqlite3_close(bdr->disk_db);
535 remove("cwist_bdr_fallback.db");
536 }
537#endif
541}
542
543/* --- Read path ------------------------------------------------------------ */
544
559static const void *bdr_serve_hit(cwist_bdr_t *bdr, uint64_t req_h, bdr_entry_t *hint,
560 size_t *out_len, bdr_blob_t **out_pin) {
562 bdr_entry_t *entry = hint;
563 if (entry &&
565 entry->request_hash != req_h)) {
566 entry = NULL;
567 }
568 if (!entry) entry = bdr_find(bdr, req_h);
569 if (!entry) {
571 return NULL;
572 }
574
575 /* Hit-time revalidation: a true report swaps in the callback's buffer
576 * with one pointer exchange; the predecessor retires across an epoch. */
578 if (hook) {
579 void *fresh = NULL;
580 size_t fresh_len = 0;
581 void (*fresh_free)(void *) = NULL;
582 if (hook(entry->revalidate_arg, &fresh, &fresh_len, &fresh_free) && fresh &&
583 fresh_len > 0) {
584 bdr_blob_t *nb = bdr_blob_wrap(fresh, fresh_len, fresh_free);
585 if (nb) {
586 bdr_entry_publish(bdr, entry, nb);
587 } else if (fresh_free) {
589 }
590 }
591 }
592
594 if (!blob || !atomic_load_explicit(&entry->is_stable, memory_order_acquire)) {
596 return NULL;
597 }
598 if (out_len) *out_len = blob->len;
599 *out_pin = blob;
600 return blob->data;
601}
602
616const void *cwist_bdr_get_pinned(cwist_bdr_t *bdr, const char *method, const char *path,
617 size_t *out_len, bdr_blob_t **out_pin) {
618 if (!bdr || !method || !path || !out_pin) return NULL;
619 if (strcmp(method, "GET") != 0) return NULL;
620 if (atomic_load_explicit(&bdr->is_disk_mode, memory_order_acquire)) return NULL;
621
622 return bdr_serve_hit(bdr, bdr_hash(method, path), NULL, out_len, out_pin);
623}
624
642const void *cwist_bdr_get_pinned_cursor(cwist_bdr_t *bdr, const char *method, const char *path,
643 size_t path_len, size_t *out_len, bdr_blob_t **out_pin,
645 if (!bdr || !method || !path || !out_pin || !cursor) return NULL;
646 if (strcmp(method, "GET") != 0) return NULL;
647 if (atomic_load_explicit(&bdr->is_disk_mode, memory_order_acquire)) return NULL;
648
651 if (cursor->entry && cursor->path_len == path_len &&
653 memcmp(cursor->path, path, path_len) == 0) {
654 hint = cursor->entry;
655 req_h = cursor->req_hash;
656 } else {
657 req_h = bdr_hash_n(method, path, path_len);
658 }
659
660 const void *data = bdr_serve_hit(bdr, req_h, hint, out_len, out_pin);
661 if (data && path_len < CWIST_BDR_CURSOR_PATH_MAX) {
664 served->request_hash != req_h) {
665 /* The hint was stale (or absent): recover the served entry from
666 * the bucket chain so the cursor points at the live node. */
667 served = bdr_find(bdr, req_h);
668 }
669 if (served) {
670 cursor->entry = served;
671 cursor->req_hash = req_h;
672 cursor->path_len = path_len;
673 memcpy(cursor->path, path, path_len);
674 } else {
675 cursor->entry = NULL;
676 }
677 } else if (!data) {
678 cursor->entry = NULL;
679 }
680 return data;
681}
682
691 (void)pin; /* The pin is the epoch itself. */
693}
694
708const void *cwist_bdr_get(cwist_bdr_t *bdr, const char *method, const char *path, size_t *out_len) {
710 const void *data = cwist_bdr_get_pinned(bdr, method, path, out_len, &pin);
711 if (pin) cwist_bdr_unpin(pin);
712 return data;
713}
714
727void *cwist_bdr_copy_get(cwist_bdr_t *bdr, const char *method, const char *path, size_t *out_len) {
729 size_t len = 0;
730 const void *data = cwist_bdr_get_pinned(bdr, method, path, &len, &pin);
731 void *copy = NULL;
732 if (data && len > 0) {
733 copy = cwist_alloc(len);
734 if (copy) {
735 memcpy(copy, data, len);
736 if (out_len) *out_len = len;
737 }
738 }
739 if (pin) cwist_bdr_unpin(pin);
740 return copy;
741}
742
743/* --- Learn path (lock-free) ------------------------------------------------ */
744
756#ifdef CWIST_DB
757static void cwist_bdr_put_disk(cwist_bdr_t *bdr, uint64_t req_h, const void *data, size_t len) {
758 pthread_mutex_lock(&bdr->lock);
760 sqlite3_prepare_v2(bdr->disk_db, "INSERT OR REPLACE INTO bdr (hash, blob) VALUES (?, ?);", -1,
761 &stmt, NULL);
763 sqlite3_bind_blob(stmt, 2, data, (int)len, SQLITE_STATIC);
767}
768#endif
769
784void cwist_bdr_put(cwist_bdr_t *bdr, const char *method, const char *path, const void *data,
785 size_t len) {
786 if (!bdr || !method || !path || !data || len == 0) return;
787 if (strcmp(method, "GET") != 0) return;
788
789 uint64_t req_h = bdr_hash(method, path);
790 uint64_t res_h = bdr_hash_data(data, len);
791
792#ifdef CWIST_DB
793 if (atomic_load_explicit(&bdr->is_disk_mode, memory_order_acquire)) {
794 cwist_bdr_put_disk(bdr, req_h, data, len);
795 return;
796 }
797#endif
798
799 bdr_entry_t *entry = bdr_find_or_insert(bdr, req_h);
800 if (!entry) return;
801
803 /* Content changed under a stable entry: demote to candidate. */
805 bdr_entry_publish(bdr, entry, NULL);
810 }
811 } else {
815 /* Candidate reproduced the same bytes: stabilize. A concurrent
816 * stabilizer publishes an identical blob; the loser's duplicate
817 * comes back from the exchange and is retired unused. */
818 bdr_blob_t *blob = bdr_blob_learn(data, len);
819 if (blob) {
820 bdr_entry_publish(bdr, entry, blob);
825 }
826 } else {
827 /* Different bytes: the entry now tracks the new candidate hash. */
829 }
830 }
831
832 bdr_janitor_tick(bdr);
833}
834
848void cwist_bdr_put_fixed(cwist_bdr_t *bdr, const char *method, const char *path, const void *data,
849 size_t len) {
850 if (!bdr || !method || !path || !data || len == 0) return;
851 if (strcmp(method, "GET") != 0) return;
852
853 uint64_t req_h = bdr_hash(method, path);
854
855#ifdef CWIST_DB
856 if (atomic_load_explicit(&bdr->is_disk_mode, memory_order_acquire)) {
857 cwist_bdr_put_disk(bdr, req_h, data, len);
858 return;
859 }
860#endif
861
862 bdr_entry_t *entry = bdr_find_or_insert(bdr, req_h);
863 if (!entry) return;
864
865 bdr_blob_t *blob = bdr_blob_learn(data, len);
866 if (!blob) return;
867 atomic_store_explicit(&entry->response_hash, bdr_hash_data(data, len), memory_order_release);
868 bdr_entry_publish(bdr, entry, blob);
872
873 bdr_janitor_tick(bdr);
874}
875
894void cwist_bdr_put_revalidatable(cwist_bdr_t *bdr, const char *method, const char *path,
895 const void *data, size_t len, cwist_bdr_revalidate_fn fn,
896 void *arg) {
897 if (!bdr || !method || !path || !data || len == 0 || !fn) return;
898 if (strcmp(method, "GET") != 0) return;
899
900 uint64_t req_h = bdr_hash(method, path);
901
902#ifdef CWIST_DB
903 if (atomic_load_explicit(&bdr->is_disk_mode, memory_order_acquire)) {
904 cwist_bdr_put_disk(bdr, req_h, data, len);
905 return;
906 }
907#endif
908
909 bdr_entry_t *entry = bdr_find_or_insert(bdr, req_h);
910 if (!entry) return;
911
912 /* Publish the hook before the blob so a reader never observes a
913 * revalidatable blob without its hook. */
914 entry->revalidate_arg = arg;
917
918 bdr_blob_t *blob = bdr_blob_learn(data, len);
919 if (!blob) return;
920 atomic_store_explicit(&entry->response_hash, bdr_hash_data(data, len), memory_order_release);
921 bdr_entry_publish(bdr, entry, blob);
925
926 bdr_janitor_tick(bdr);
927}
928
940void cwist_bdr_set_limits(cwist_bdr_t *bdr, size_t max_bytes, time_t max_entry_age_sec,
941 uint64_t revalidate_hits) {
942 if (!bdr) return;
943 pthread_mutex_lock(&bdr->lock);
944 if (max_bytes > 0) bdr->max_bytes = max_bytes;
945 if (max_entry_age_sec > 0) bdr->max_entry_age_sec = max_entry_age_sec;
946 if (revalidate_hits > 0) bdr->revalidate_hits = revalidate_hits;
948}
Memory allocation wrappers backed by libttak.
void cwist_free(void *ptr)
Free memory obtained via cwist_alloc/cwist_strdup/etc.
Definition alloc.c:52
void * cwist_alloc_array(size_t count, size_t elem_size)
Allocate zeroed memory for count elements.
Definition alloc.c:21
void * cwist_alloc(size_t size)
Allocate zeroed memory tracked by libttak.
Definition alloc.c:17
void cwist_bdr_set_limits(cwist_bdr_t *bdr, size_t max_bytes, time_t max_entry_age_sec, uint64_t revalidate_hits)
Update cache limits; zero arguments leave the corresponding limit unchanged.
#define BDR_DEFAULT_MAX_BYTES
void cwist_bdr_put_fixed(cwist_bdr_t *bdr, const char *method, const char *path, const void *data, size_t len)
Publish a reply as immediately stable, bypassing the learning phase.
void cwist_bdr_unpin(bdr_blob_t *pin)
Release a pin obtained from a cwist_bdr_get_pinned*() call.
void cwist_bdr_put_revalidatable(cwist_bdr_t *bdr, const char *method, const char *path, const void *data, size_t len, cwist_bdr_revalidate_fn fn, void *arg)
Publish a stable reply together with a hit-time revalidation hook.
#define BDR_DEFAULT_ENTRY_TTL
#define BDR_JANITOR_PERIOD
void cwist_bdr_put(cwist_bdr_t *bdr, const char *method, const char *path, const void *data, size_t len)
Write a reply into the SQLite fallback database (disk mode).
void * cwist_bdr_copy_get(cwist_bdr_t *bdr, const char *method, const char *path, size_t *out_len)
Look up a cached GET reply and return an owned copy of its bytes.
#define BDR_GC_SWEEP
#define BDR_BUCKETS
void cwist_bdr_destroy(cwist_bdr_t *bdr)
Destroy a cache instance and release all its resources.
const void * cwist_bdr_get_pinned(cwist_bdr_t *bdr, const char *method, const char *path, size_t *out_len, bdr_blob_t **out_pin)
Look up a cached GET reply, returning epoch-pinned bytes.
#define BDR_DEFAULT_REVALIDATE_HITS
const void * cwist_bdr_get(cwist_bdr_t *bdr, const char *method, const char *path, size_t *out_len)
Look up a cached GET reply and return a borrowed pointer to its bytes.
const void * cwist_bdr_get_pinned_cursor(cwist_bdr_t *bdr, const char *method, const char *path, size_t path_len, size_t *out_len, bdr_blob_t **out_pin, cwist_bdr_cursor_t *cursor)
Cursor-accelerated variant of cwist_bdr_get_pinned() for non-NUL-terminated paths.
cwist_bdr_t * cwist_bdr_create(void)
Create a reply cache instance with default limits.
Auto-Caching Layer for repetitive requests.
#define CWIST_BDR_CURSOR_PATH_MAX
Per-connection cursor that turns repeated hits of the same route into a content-compare instead of a ...
bool(* cwist_bdr_revalidate_fn)(void *arg, void **out_data, size_t *out_len, void(**out_free)(void *))
Revalidation callback attached to a cache entry.
Convenience size-conversion and utility macros shared across CWIST.
#define CWIST_MIB(x)
Convert binary mebibytes to bytes.
Definition macros.h:16
uint64_t siphash24(const void *src, size_t len, const uint8_t key[16])
Compute SipHash-2-4 over a byte range using a 16-byte secret key.
Definition siphash.c:52
size_t head
Definition otel.c:57
size_t cap
Definition otel.c:56
Refcount-free cached response blob, reclaimed via libttak EBR.
const unsigned char * data
Serialized HTTP response (headers + body).
void(* free_fn)(void *)
Releaser for mem.
void * mem
Backing allocation (this object for learned blobs).
size_t len
Length of data in bytes.
Big Dumb Reply Entry. Holds an atomically swappable cached response blob.
_Atomic uint64_t hits
Hit count (approximate, relaxed)
uint64_t request_hash
Key: SipHash(Method + Path)
_Atomic bool is_stable
True once the response proved stable.
void * revalidate_arg
Opaque argument for the hook.
_Atomic uint64_t response_hash
Hash of the response content (stability check)
_Atomic bool retired
Unlinked by the janitor; walkers skip it.
cwist_bdr_revalidate_fn revalidate
Optional hit-time refresh hook.
_Atomic int64_t created_at
Creation timestamp (time_t)
struct bdr_entry_t * retire_next
Janitor retired-entry list link.
Big Dumb Reply Context. Lock-free read/learn paths; the mutex only serializes janitor work (sweep/tri...
System Resource Monitoring Utilities.
bool cwist_is_ram_critical(uint64_t threshold_bytes)
Check if system RAM is critically low.
Definition sys_info.c:100