From: Vsevolod Stakhov Date: Sat, 9 May 2026 13:37:58 +0000 (+0100) Subject: [Feature] libev: add fake-clock and time-resync hooks for tests X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=1f83bd9676f581f97b348079416008fbf4e45d53;p=thirdparty%2Frspamd.git [Feature] libev: add fake-clock and time-resync hooks for tests Three local extensions on top of stock libev: * ev_set_fake_time_cb / ev_get_fake_time_cb — process-global hook; when set, replaces both ev_time() and the internal monotonic clock so timers and ev_now() advance under test control. * ev_now_resync — force-resync the loop's cached realtime/monotonic state from the current sources, discarding interpolation. Required after installing or removing a fake clock; also useful after any other large clock discontinuity. Default cb is NULL, so production cost is one predicted-false branch in each clock read. Local style follows libev's (GNU-ish, two-space, space-before-paren), not the rspamd tree style — bypassing clang-format here intentionally. --- diff --git a/contrib/libev/Changes b/contrib/libev/Changes index 04e58ba0ff..3d59f38435 100644 --- a/contrib/libev/Changes +++ b/contrib/libev/Changes @@ -1,5 +1,18 @@ Revision history for libev, a high-performance and full-featured event loop. +Rspamd local extensions (not present in upstream libev): + - ev_active_cnt: returns the loop's activecnt counter. + - ev_now_update_if_cheap: cheap variant of ev_now_update for paths + that need a fresh cached time but cannot afford a realtime read. + - ev_now_resync: force-resync the loop's cached realtime/monotonic + state from the current time sources, discarding interpolation. + Use after installing/removing a fake clock. + - ev_set_fake_time_cb / ev_get_fake_time_cb: process-global fake + clock hook for unit tests; when set, replaces both ev_time() and + the internal monotonic clock so timers and ev_now() advance under + test control. NULL by default; cost in production is one + predicted-false branch. + 4.25 Fri Dec 21 07:49:20 CET 2018 - INCOMPATIBLE CHANGE: EV_THROW was renamed to EV_NOEXCEPT (EV_THROW sitll provided) and now uses noexcept on C++11 or newer. diff --git a/contrib/libev/ev.c b/contrib/libev/ev.c index 63ab289302..c52a35c252 100644 --- a/contrib/libev/ev.c +++ b/contrib/libev/ev.c @@ -2075,6 +2075,26 @@ ev_set_allocator (void *(*cb)(void *ptr, long size) EV_NOEXCEPT) EV_NOEXCEPT alloc = cb; } +/* RSPAMD LOCAL EXTENSION: process-global fake-clock hook for unit tests. + * When non-NULL, both ev_time() and get_clock() return the callback's value + * instead of reading the system clocks. See ev.h for the public API. + */ +static ev_fake_time_cb fake_time_cb = 0; + +ecb_cold +void +ev_set_fake_time_cb (ev_fake_time_cb cb) EV_NOEXCEPT +{ + fake_time_cb = cb; +} + +ecb_cold +ev_fake_time_cb +ev_get_fake_time_cb (void) EV_NOEXCEPT +{ + return fake_time_cb; +} + inline_speed void * ev_realloc (void *ptr, long size) { @@ -2202,6 +2222,10 @@ typedef struct ev_tstamp ev_time (void) EV_NOEXCEPT { + /* RSPAMD LOCAL EXTENSION: fake clock hook for unit tests. */ + if (ecb_expect_false (fake_time_cb != 0)) + return fake_time_cb (); + #if EV_USE_REALTIME if (ecb_expect_true (have_realtime)) { @@ -2222,6 +2246,10 @@ ev_time (void) EV_NOEXCEPT inline_size ev_tstamp get_clock (void) { + /* RSPAMD LOCAL EXTENSION: fake clock hook for unit tests. */ + if (ecb_expect_false (fake_time_cb != 0)) + return fake_time_cb (); + #if EV_USE_MONOTONIC if (ecb_expect_true (have_monotonic)) { @@ -5689,6 +5717,16 @@ ev_now_update_if_cheap (EV_P) EV_NOEXCEPT if (have_cheap_timer) time_update (EV_A_ 1e100); } +/* RSPAMD LOCAL EXTENSION: see ev.h. */ +void +ev_now_resync (EV_P) EV_NOEXCEPT +{ + ev_rt_now = ev_time (); + mn_now = get_clock (); + now_floor = mn_now; + rtmn_diff = ev_rt_now - mn_now; +} + int ev_active_cnt (EV_P) EV_NOEXCEPT { diff --git a/contrib/libev/ev.h b/contrib/libev/ev.h index 7135a08a57..a2a9715824 100644 --- a/contrib/libev/ev.h +++ b/contrib/libev/ev.h @@ -553,6 +553,17 @@ EV_API_DECL void ev_set_allocator (void *(*cb)(void *ptr, long size) EV_NOEXCEPT */ EV_API_DECL void ev_set_syserr_cb (void (*cb)(const char *msg) EV_NOEXCEPT) EV_NOEXCEPT; +/* RSPAMD LOCAL EXTENSION: + * Install a process-global fake clock callback used by ev_time() and the + * internal monotonic clock read. When set, the callback's return value + * replaces both the realtime and monotonic clock sources, so timers and + * ev_now()/ev_now_update() advance under test control. Pass NULL to + * restore the system clocks. Intended for unit tests only. + */ +typedef ev_tstamp (*ev_fake_time_cb)(void); +EV_API_DECL void ev_set_fake_time_cb (ev_fake_time_cb cb) EV_NOEXCEPT; +EV_API_DECL ev_fake_time_cb ev_get_fake_time_cb (void) EV_NOEXCEPT; + #if EV_MULTIPLICITY /* the default loop is the only one that handles signals and child watchers */ @@ -602,6 +613,13 @@ EV_API_DECL void ev_now_update (EV_P) EV_NOEXCEPT; /* update event loop time */ * are used in system. */ EV_API_DECL void ev_now_update_if_cheap (EV_P) EV_NOEXCEPT; +/* RSPAMD LOCAL EXTENSION: + * Force-resync the loop's cached realtime/monotonic state from the current + * time sources, discarding any interpolation state. Use after installing + * or removing a fake clock via ev_set_fake_time_cb(), or after any other + * large discontinuity in the clock source. + */ +EV_API_DECL void ev_now_resync (EV_P) EV_NOEXCEPT; #if EV_WALK_ENABLE /* walk (almost) all watchers in the loop of a given type, invoking the */