Skip to content

fix(native): eliminate explorer hang deadlock and improve taskbar crash recovery - #98

Merged
CodeZeno merged 4 commits into
CodeZeno:mainfrom
fix2it:fix/explorer-hang-and-restart-recovery
Sep 13, 2026
Merged

CodeZeno merged 4 commits into
CodeZeno:mainfrom
fix2it:fix/explorer-hang-and-restart-recovery

Conversation

@fix2it

@fix2it fix2it commented Sep 12, 2026

Copy link
Copy Markdown
Contributor

Background & Context

While using the app on Windows 11, I ran into the issue discussed in #94 during an explorer.exe UI hang (Event ID 1002, Application Hang). The app vanished from the taskbar and froze completely. After digging into the root cause, I found that while the recent context-menu fixes in v2.10.24/25 addressed menu deadlocks, a couple of underlying Win32 LPC and watchdog checks were still causing the monitor thread to hang indefinitely or fail to re-dock when Explorer hung or restarted. Here is the detailed breakdown and the fix.

Problem

When explorer.exe experiences a UI hang or freeze (Event ID 1002, Application Hang), Claude-Code-Usage-Monitor freezes indefinitely and vanishes from the Windows 11 taskbar without recovering:

  1. Deadlock on Explorer hang: SHAppBarMessage(ABM_GETTASKBARPOS, ...) in src/native_interop.rs relies on synchronous LPC with Explorer's UI thread. When Explorer is unresponsive, this call blocks indefinitely, freezing the monitor thread.
  2. Watchdog failure on taskbar crash: When Explorer crashes or restarts, Windows does not automatically destroy cross-process child windows. IsWindow(child_hwnd) continues to return true, causing spawn_taskbar_watchdog to believe the host taskbar is still alive and leave the widget orphaned.
  3. Redocking latency: Redocking was delayed until the periodic watchdog timer ticked instead of immediately responding to shell rebirth.

Solution

  • Non-blocking taskbar metrics: Replaced SHAppBarMessage with get_window_rect_safe(taskbar_hwnd). Non-blocking GetWindowRect takes ~0.003 ms and never deadlocks during Explorer hangs (verified empirically via NtSuspendProcess tests where SHAppBarMessage hung for >1500 ms).
  • Robust watchdog parent verification: In spawn_taskbar_watchdog, we now check GetParent(hwnd). If a surface has a parent assigned and !IsWindow(parent), the watchdog detects the dead host and triggers redocking. Unparented/floating surfaces safely evaluate to false to avoid false-positive restart loops. Uses a zero-heap lazy iterator (once(...).chain(...).any(...)).
  • Instant recovery via TaskbarCreated: Explicitly triggers render_layered() when the TaskbarCreated broadcast message is received.

Testing

  • Explorer Freeze: Simulated via NtSuspendProcess on explorer.exe: monitor remained 100% responsive (Responding = True, ~28 MB RAM) and resumed immediately.
  • Graceful Shell Restart: Tested via WM_CLOSE to Shell_TrayWnd and restarting explorer.exe: widget cleanly and automatically redocked to the new taskbar (Visible = True).
  • Abrupt Shell Crash: Tested via taskkill /f /im explorer.exe: watchdog cleanly detected dead parent and recovered with single-instance mutex handoff.
  • Single-Instance Guard: Verified that launching a second instance exits with code 0 without disturbing the running monitor.
  • Unit Tests & CI: All 200+ unit tests pass cleanly (test result: ok).

Note: This fix was researched, profiled, and refined in collaboration with an AI coding agent (Google Antigravity), and rigorously validated across live Windows 11 shell hang & restart lifecycles.

- Replace synchronous SHAppBarMessage in get_taskbar_rect with non-blocking get_window_rect_safe to prevent thread deadlock when explorer.exe hangs (Application Hang 1002)
- In spawn_taskbar_watchdog, verify parent window validity via GetParent() when shell_hosted to detect orphaned child windows across explorer restarts
- Re-dock taskbar surfaces and re-render on TaskbarCreated shell broadcast
CodeZeno added a commit that referenced this pull request Sep 13, 2026
Integrate PR #98: avoid the shell taskbar-position round-trip, check surviving window parents, and repaint after TaskbarCreated.

Remove the CI workflow added by the PR. Preserve all four original author commits.

Verified release build in target/pr-98-validation, cargo clippy, cargo test (242 passed, 1 ignored), and cargo fmt --check.
@CodeZeno
CodeZeno merged commit 80d9998 into CodeZeno:main Sep 13, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants