From ea3e87906864038580c31957075cba2cfd3ac1aa Mon Sep 17 00:00:00 2001 From: reverse Date: Wed, 19 Aug 2026 01:56:22 +0000 Subject: [PATCH] =?UTF-8?q?connclass:=20cache=20resolved=20connect=20class?= =?UTF-8?q?es=20in=20a=20config=5Fgen-tagged=20thread=5Flocal=20=E2=80=94?= =?UTF-8?q?=20all()/named()=20(and=20thus=20pick/assign=20and=20every=20pe?= =?UTF-8?q?r-ping/per-message=20getter)=20re-parsed=20the=20connectclass?= =?UTF-8?q?=20config=20and=20re-resolved=20parent=20inheritance=20on=20eac?= =?UTF-8?q?h=20call;=20now=20rebuilt=20only=20on=20rehash?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/modules/connclass.rs | 34 ++++++++++++++++++++++++++++------ 1 file changed, 28 insertions(+), 6 deletions(-) diff --git a/src/modules/connclass.rs b/src/modules/connclass.rs index d9a9971..83d1384 100644 --- a/src/modules/connclass.rs +++ b/src/modules/connclass.rs @@ -163,18 +163,40 @@ fn build(s: &Server, name: &str) -> Option { Some(c) } +thread_local! { + /// (config_gen, resolved classes) — rebuilt only when the config changes. The + /// core is single-threaded, so this thread_local cache lets the `&Server` + /// entry points (pick/assign/named and the per-ping/per-message getters) avoid + /// re-parsing + re-resolving parent inheritance on every call. + static CLASSES: std::cell::RefCell<(u64, Vec)> = + const { std::cell::RefCell::new((u64::MAX, Vec::new())) }; +} + +/// Run `f` over the resolved connect classes, (re)building them only on config change. +fn with_classes(s: &Server, f: impl FnOnce(&[ConnClass]) -> R) -> R { + CLASSES.with(|cell| { + if cell.borrow().0 != s.config_gen { + let fresh: Vec = s + .conf_all("connectclass") + .iter() + .filter_map(|l| l.split_whitespace().next()) + .filter_map(|name| build(s, name)) + .collect(); + *cell.borrow_mut() = (s.config_gen, fresh); + } + let g = cell.borrow(); + f(&g.1) + }) +} + /// Every configured class, resolved. pub fn all(s: &Server) -> Vec { - s.conf_all("connectclass") - .iter() - .filter_map(|l| l.split_whitespace().next()) - .filter_map(|name| build(s, name)) - .collect() + with_classes(s, <[ConnClass]>::to_vec) } /// A single resolved class by name. pub fn named(s: &Server, name: &str) -> Option { - build(s, name) + with_classes(s, |c| c.iter().find(|x| x.name == name).cloned()) } // --- mask matching -----------------------------------------------------------