feat(diag): UI-thread hang watchdog with minidump capture (#120)

The Windows freeze in #120 never reproduces locally and a hung app
prints nothing, so terminal runs and logs came back empty. A watchdog
thread now watches a heartbeat the UI thread bumps on every processed
message (plus a 5 s liveness timer); 25 s of silence writes a minidump
with every thread's stack to %LOCALAPPDATA%\OpenCADStudio plus a note
telling the reporter to attach it to the issue. One dump per run,
armed only after the first beat, suspend-aware, OCS_NO_WATCHDOG=1
disables. Windows-only; windows-sys gains the Debug/Threading binding
features (no new crates).

Co-Authored-By: Claude Fable 5 <[email protected]>
This commit is contained in:
Hakan SevenandClaude Fable 5 committed 2026-07-22 22:13:00 +03:00
1 parent 805da6428a
commit b3decf45ec
7 files changed
+172 -1

No files matched your search

+10
View File
@@ -118,6 +118,12 @@ impl OpenCADStudio {
}
pub fn update(&mut self, msg: Message) -> Task<Message> {
// Every processed message proves the UI thread is alive (#120).
#[cfg(target_os = "windows")]
crate::io::hang_watchdog::beat();
if matches!(msg, Message::WatchdogBeat) {
return Task::none();
}
// A modal dialog must capture the keyboard the same way it already
// captures the mouse. Otherwise keystrokes from the global key
// subscription leak past the modal into the command line and fire as
@@ -2396,6 +2402,10 @@ impl OpenCADStudio {
self.mtext_caret_move(d);
Task::none()
}
// Handled (and returned) in `update` before dispatch; kept for
// match exhaustiveness.
Message::WatchdogBeat => Task::none(),
Message::MTextCaretBlink => {
if let Some(ed) = self.mtext_editor.as_mut() {
ed.caret_blink_on = !ed.caret_blink_on;