Repository: zng-ui/zng (language: rust)

Issue:
Implement warning for blocking event handlers
Measure handlers time and print warning for calls that don't return immediately, they should be async.


Discussion / hints:

Fix patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 9030cf4f2..14e20ba81 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,6 @@
 # Unreleased
 
+* Add warning for slow event handlers in debug builds.
 * Add in memory "clipboard" for headless tests that use `CLIPBOARD`.
 * Fix `tooltip` showing instead of `disabled_tooltip` in contexts that disable the widget after a slight delay.
 * Fix tooltip opened by `ACCESS.show_tooltip` closing immediately on mouse leave.
diff --git a/crates/zng-wgt/src/node.rs b/crates/zng-wgt/src/node.rs
index d8eb9f028..6903dae62 100644
--- a/crates/zng-wgt/src/node.rs
+++ b/crates/zng-wgt/src/node.rs
@@ -573,7 +573,22 @@ where
 
             if let Some(args) = event.on(update) {
                 if !args.propagation().is_stopped() && filter(args) {
+                    #[cfg(debug_assertions)]
+                    let t = std::time::Instant::now();
+
                     handler.event(args);
+
+                    #[cfg(debug_assertions)]
+                    {
+                        let t = t.elapsed();
+                        if t > std::time::Duration::from_millis(300) {
+                            tracing::warn!(
+                                "event handler for `{}` in {:?} blocked for {t:?}, consider using `async_hn!`",
+                                event.as_any().name(),
+                                WIDGET.id()
+                            );
+                        }
+                    }
                 }
             }
         }
@@ -639,7 +654,22 @@ where
         UiNodeOp::Event { update } => {
             if let Some(args) = event.on(update) {
                 if !args.propagation().is_stopped() && filter(args) {
+                    #[cfg(debug_assertions)]
+                    let t = std::time::Instant::now();
+
                     handler.event(args);
+
+                    #[cfg(debug_assertions)]
+                    {
+                        let t = t.elapsed();
+                        if t > std::time::Duration::from_millis(300) {
+                            tracing::warn!(
+                                "preview event handler for `{}` in {:?} blocked for {t:?}, consider using `async_hn!`",
+                                event.as_any().name(),
+                                WIDGET.id()
+                            );
+                        }
+                    }
                 }
             }
         }


Test patch: