Try to detect stale type changes when hotpatching

This commit is contained in:
Héctor Ramón Jiménez 2025-06-10 02:32:22 +02:00
parent c1c5fbda61
commit 699b85762b
No known key found for this signature in database
GPG key ID: 7CC46565708259A7
2 changed files with 72 additions and 14 deletions

View file

@ -121,6 +121,10 @@ pub fn on_hotpatch(f: impl Fn() + Send + Sync + 'static) {
internal::on_hotpatch(f) internal::on_hotpatch(f)
} }
pub fn is_stale() -> bool {
internal::is_stale()
}
#[cfg(all(feature = "enable", not(target_arch = "wasm32")))] #[cfg(all(feature = "enable", not(target_arch = "wasm32")))]
mod internal { mod internal {
use crate::core::theme; use crate::core::theme;
@ -136,8 +140,16 @@ mod internal {
use beacon::span; use beacon::span;
use beacon::span::present; use beacon::span::present;
use std::collections::BTreeSet;
use std::sync::atomic::{self, AtomicBool, AtomicUsize}; use std::sync::atomic::{self, AtomicBool, AtomicUsize};
use std::sync::{Arc, LazyLock, RwLock}; use std::sync::{Arc, LazyLock, Mutex, OnceLock, RwLock};
static IS_STALE: AtomicBool = AtomicBool::new(false);
static HOT_FUNCTIONS_PENDING: Mutex<BTreeSet<u64>> =
Mutex::new(BTreeSet::new());
static HOT_FUNCTIONS: OnceLock<BTreeSet<u64>> = OnceLock::new();
pub fn init(metadata: Metadata) { pub fn init(metadata: Metadata) {
let name = metadata.name.split("::").next().unwrap_or(metadata.name); let name = metadata.name.split("::").next().unwrap_or(metadata.name);
@ -150,6 +162,20 @@ mod internal {
}; };
cargo_hot::connect(); cargo_hot::connect();
cargo_hot::subsecond::register_handler(Arc::new(|| {
if HOT_FUNCTIONS.get().is_none() {
HOT_FUNCTIONS
.set(std::mem::take(
&mut HOT_FUNCTIONS_PENDING
.lock()
.expect("Lock hot functions"),
))
.expect("Set hot functions");
}
IS_STALE.store(false, atomic::Ordering::Relaxed);
}));
} }
pub fn quit() -> bool { pub fn quit() -> bool {
@ -286,15 +312,34 @@ mod internal {
// The `move` here is important. Hotpatching will not work // The `move` here is important. Hotpatching will not work
// otherwise. // otherwise.
cargo_hot::subsecond::call(move || { let mut f = cargo_hot::subsecond::HotFn::current(move || {
f.take().expect("Hot function is stale")() f.take().expect("Hot function is stale")()
}) });
let address = f.ptr_address().0;
if let Some(hot_functions) = HOT_FUNCTIONS.get() {
if hot_functions.contains(&address) {
IS_STALE.store(true, atomic::Ordering::Relaxed);
}
} else {
let _ = HOT_FUNCTIONS_PENDING
.lock()
.expect("Lock hot functions")
.insert(address);
}
f.call(())
} }
pub fn on_hotpatch(f: impl Fn() + Send + Sync + 'static) { pub fn on_hotpatch(f: impl Fn() + Send + Sync + 'static) {
cargo_hot::subsecond::register_handler(Arc::new(f)); cargo_hot::subsecond::register_handler(Arc::new(f));
} }
pub fn is_stale() -> bool {
IS_STALE.load(atomic::Ordering::Relaxed)
}
fn span(span: span::Stage) -> Span { fn span(span: span::Stage) -> Span {
log(client::Event::SpanStarted(span.clone())); log(client::Event::SpanStarted(span.clone()));
@ -428,4 +473,8 @@ mod internal {
} }
pub fn on_hotpatch(_f: impl Fn() + Send + Sync + 'static) {} pub fn on_hotpatch(_f: impl Fn() + Send + Sync + 'static) {}
pub fn is_stale() -> bool {
false
}
} }

View file

@ -343,21 +343,30 @@ where
themer(derive_theme(), Element::from(mode).map(Event::Message)) themer(derive_theme(), Element::from(mode).map(Event::Message))
}); });
let notification = self.show_notification.then(|| { let notification = self
themer( .show_notification
derive_theme(), .then(|| text("Press F12 to open debug metrics"))
bottom_right(opaque( .or_else(|| {
container(text("Press F12 to open debug metrics")) debug::is_stale().then(|| {
.padding(10) text(
.style(container::dark), "Types have changed. Restart to re-enable hotpatching.",
)), )
) })
}); });
stack![view] stack![view]
.height(Fill) .height(Fill)
.push_maybe(mode.map(opaque)) .push_maybe(mode.map(opaque))
.push_maybe(notification) .push_maybe(notification.map(|notification| {
themer(
derive_theme(),
bottom_right(opaque(
container(notification)
.padding(10)
.style(container::dark),
)),
)
}))
.into() .into()
} }