{"record":{"id":"f427ff8c11785151","repo":"iced-rs/iced","slug":"set-hot-functions","errorCode":null,"errorMessage":"Set hot functions","messagePattern":"Set hot functions","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"debug/src/lib.rs","lineNumber":432,"sourceCode":"    use std::sync::atomic::{self, AtomicBool};\n    use std::sync::{Arc, Mutex, OnceLock};\n\n    static IS_STALE: AtomicBool = AtomicBool::new(false);\n\n    static HOT_FUNCTIONS_PENDING: Mutex<BTreeSet<u64>> = Mutex::new(BTreeSet::new());\n\n    static HOT_FUNCTIONS: OnceLock<BTreeSet<u64>> = OnceLock::new();\n\n    pub fn init() {\n        cargo_hot::connect();\n\n        cargo_hot::subsecond::register_handler(Arc::new(|| {\n            if HOT_FUNCTIONS.get().is_none() {\n                HOT_FUNCTIONS\n                    .set(std::mem::take(\n                        &mut HOT_FUNCTIONS_PENDING.lock().expect(\"Lock hot functions\"),\n                    ))\n                    .expect(\"Set hot functions\");\n            }\n\n            IS_STALE.store(false, atomic::Ordering::Relaxed);\n        }));\n    }\n\n    pub fn call<O>(f: impl FnOnce() -> O) -> O {\n        let mut f = Some(f);\n\n        // The `move` here is important. Hotpatching will not work\n        // otherwise.\n        let mut f = cargo_hot::subsecond::HotFn::current(move || {\n            f.take().expect(\"Hot function is stale\")()\n        });\n\n        let address = f.ptr_address().0;\n\n        if let Some(hot_functions) = HOT_FUNCTIONS.get() {","sourceCodeStart":414,"sourceCodeEnd":450,"githubUrl":"https://github.com/iced-rs/iced/blob/2cffa99b395d84fe469b44dccb56bbacd2f1a157/debug/src/lib.rs#L414-L450","documentation":"`HOT_FUNCTIONS: OnceLock<BTreeSet<u64>>` is filled exactly once when the first hotpatch arrives. `.set(...).expect(\"Set hot functions\")` fires when `set` runs after the OnceLock is already populated. The `is_none()` pre-check does not make this atomic: two handler invocations (or one handler registered twice) can both observe None and both try to set; the loser panics.","triggerScenarios":"Calling the init that invokes `cargo_hot::subsecond::register_handler` more than once, so multiple handlers run on patch arrival; or concurrent handler invocations racing the check-then-set sequence on the first patch.","commonSituations":"Debug helpers that get initialized from several entry points (main, tests, workspace tools); refactors that made init idempotent everywhere except this registration; upgrading iced debug versions that changed handler wiring.","solutions":["Guard initialization with std::sync::Once (or call it from a single, documented entry point) so register_handler runs once","Use `HOT_FUNCTIONS.get_or_init(|| mem::take(...))` instead of check-then-set to make it race-free","If duplicates are benign, ignore the failure: `let _ = HOT_FUNCTIONS.set(...)`","Audit for duplicate handler registration: add a debug log/assert in init counting calls"],"exampleFix":"// before\nif HOT_FUNCTIONS.get().is_none() {\n    HOT_FUNCTIONS\n        .set(std::mem::take(&mut *HOT_FUNCTIONS_PENDING.lock().expect(\"Lock hot functions\")))\n        .expect(\"Set hot functions\");\n}\n// after\nlet pending = std::mem::take(\n    &mut *HOT_FUNCTIONS_PENDING.lock().unwrap_or_else(std::sync::PoisonError::into_inner),\n);\nlet _ = HOT_FUNCTIONS.set(pending); // idempotent: losing a benign race is fine","handlingStrategy":"validation","validationCode":"static HOTPATCH_INIT: std::sync::Once = std::sync::Once::new();\n\npub fn init() {\n    HOTPATCH_INIT.call_once(real_init); // second caller is a no-op, handler registered once\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Initialize hot-reload support from exactly one documented entry point","If multiple init paths are unavoidable, wrap registration in std::sync::Once or an AtomicBool","Prefer get_or_init over check-then-set when filling shared OnceLocks — the naive pattern races by construction","Ignore duplicate `set` results (let _ = ...) when losing the race is benign"],"tags":["rust","iced","debug","hot-reload","oncelock","race-condition","panic"],"backgroundTag":"once-initialization-conflict","analyzedSha":"2cffa99b395d84fe469b44dccb56bbacd2f1a157","analyzedAt":"2026-08-16T19:41:49.083Z","schemaVersion":2},"datasetVersion":"2026-08-16T23:17:17.608Z"}