1#[cfg(debug_assertions)]
4mod supervisor;
5#[cfg(debug_assertions)]
6pub use subsecond::{aslr_reference, JumpTable};
7#[cfg(debug_assertions)]
8pub use supervisor::{AdmittedPatch, PatchSupervisor, SourceSnapshot};
9#[cfg(debug_assertions)]
10mod registration;
11#[cfg(debug_assertions)]
12pub use registration::FunctionRegistration;
13mod admission;
14pub use admission::{admit, Admission};
15#[inline(always)]
17pub fn hot_reloadable<R>(function: impl FnMut() -> R) -> R {
18 #[cfg(debug_assertions)]
19 {
20 let _exit = CallbackGuard::enter();
21 subsecond::call(function)
22 }
23 #[cfg(not(debug_assertions))]
24 {
25 let mut function = function;
26 function()
27 }
28}
29
30#[inline(always)]
37pub fn hot_reloadable_with<A, R>(function: fn(A) -> R, argument: A) -> R {
38 #[cfg(debug_assertions)]
39 {
40 let _exit = CallbackGuard::enter();
41 subsecond::HotFn::current(function).call((argument,))
42 }
43 #[cfg(not(debug_assertions))]
44 {
45 function(argument)
46 }
47}
48
49#[cfg(debug_assertions)]
50struct CallbackGuard;
51#[cfg(debug_assertions)]
52impl CallbackGuard {
53 fn enter() -> Self {
54 DEPTH.with(|depth| depth.set(depth.get() + 1));
55 Self
56 }
57}
58#[cfg(debug_assertions)]
59impl Drop for CallbackGuard {
60 fn drop(&mut self) {
61 DEPTH.with(|depth| depth.set(depth.get() - 1));
62 }
63}
64
65#[cfg(debug_assertions)]
66ferroui_system::ferro_thread_local! { static DEPTH: std::cell::Cell<usize> = const { std::cell::Cell::new(0) }; }
67#[cfg(debug_assertions)]
68fn in_callback() -> bool {
69 DEPTH.with(|depth| depth.get() != 0)
70}
71
72#[cfg(debug_assertions)]
73pub mod restart;
74
75#[cfg(debug_assertions)]
76pub mod inputs;
77
78#[cfg(all(test, debug_assertions))]
79mod callback_tests {
80 use super::*;
81
82 #[test]
83 fn typed_callback_preserves_arguments_and_nested_quiescence() {
84 fn command((mut text, delta): (String, usize)) -> String {
85 assert!(in_callback());
86 hot_reloadable(|| assert!(in_callback()));
87 assert!(in_callback());
88 text.push_str(&delta.to_string());
89 text
90 }
91 assert!(!in_callback());
92 assert_eq!(hot_reloadable_with(command, ("count=".into(), 3)), "count=3");
93 assert!(!in_callback());
94 fn handler(value: &mut i32) {
95 assert!(in_callback());
96 *value += 1;
97 }
98 let mut value = 7;
99 hot_reloadable_with(handler, &mut value);
100 assert_eq!(value, 8);
101 assert!(!in_callback());
102 }
103
104 #[test]
105 fn panicking_typed_callback_releases_quiescence_guard() {
106 fn handler(_: ()) {
107 assert!(in_callback());
108 panic!("application handler failed");
109 }
110 assert!(std::panic::catch_unwind(|| hot_reloadable_with(handler, ())).is_err());
111 assert!(!in_callback());
112 }
113}