gpui-pre-windows: guard the VSync `RedrawWindow` loop with `IsWindow` (spurious ERROR_INVALID_WINDOW_HANDLE on window close)
Maintainers usually reply within 1 day
Nobody has claimed this yet.
Assessment
- Difficulty
- 2/5
- Estimated time
- 1-3 hours
- Newbie friendliness
- 77/100
Research direction
Read src/platform.rs and inspect begin_vsync_thread, especially the loop over all_windows and its RedrawWindow call. Check how WindowsWindow::drop and WM_GPUI_CLOSE_ONE_WINDOW affect the lifetime of those handles. Add the described guard for invalid HWNDs, then confirm closing a secondary window no longer logs the error.
Written by the indexing model from the issue text.
Description
What happens
Closing a secondary GPUI window on Windows logs a spurious COM error once per frame, for a tick or two after the window is gone:
ERROR 无效的窗口句柄。 (0x80070578) // ERROR_INVALID_WINDOW_HANDLE
It comes from the VSync thread calling RedrawWindow on an already destroyed HWND. Every close of a settings/dialog window in our app prints it, so it looks like the app is broken even though nothing is.
Why
gpui-pre-windows' begin_vsync_thread (src/platform.rs, zed's crates/gpui_windows/src/platform.rs) redraws every handle tracked in all_windows:
for hwnd in all_windows.read().iter() {
unsafe {
if let Some(signal_at) = signal_at {
if IsWindowVisible(hwnd.as_raw()).as_bool() && !IsIconic(hwnd.as_raw()).as_bool() {
...
}
}
RedrawWindow(Some(hwnd.as_raw()), None, None, RDW_INVALIDATE)
.ok()
.log_err();
}
}
The handle is destroyed by WindowsWindow::drop → DestroyWindow, but its entry in the raw-handle list is pruned only when the WM_GPUI_CLOSE_ONE_WINDOW message that DestroyWindow posts gets processed. Between those two points the VSync thread still sees the destroyed handle, and RedrawWindow fails with ERROR_INVALID_WINDOW_HANDLE (which is then log_err'd at ERROR level).
IsWindowVisible does not help: it returns false for an invalid handle, but the RedrawWindow call is unconditional, so it is reached either way.
Fix
Skip stale handles in the VSync loop:
for hwnd in all_windows.read().iter() {
unsafe {
// The HWND outlives its GPUI entry for a tick or two: the raw handle is
// pruned only after WM_DESTROY is processed. RedrawWindow on a destroyed
// handle logs a spurious ERROR_INVALID_WINDOW_HANDLE; skip it.
if !IsWindow(Some(hwnd.as_raw())).as_bool() {
continue;
}
...
A more thorough fix would be to remove the handle from all_windows in WindowsWindow::drop (or in the DestroyWindow path) so the VSync thread never sees a stale handle at all; the IsWindow check above is the minimal, allocation-free version we have been carrying.
Notes
- Verified against
gpui-pre-windows0.3.10 and 0.3.11 (zedf816e1f). The same loop in zedmain(crates/gpui_windows/src/platform.rs) is still unguarded, so this may be worth a PR upstream as well. IsWindowis already in scope there (UI::WindowsAndMessaging::*),windows0.62 signature:IsWindow(Some(hwnd.as_raw())).as_bool().- We vendor
gpui-pre-windowswith this one-line guard (diff and repro notes in https://github.com/qiuzhanghua/qcode/blob/main/vendor/gpui-pre-windows/README.qcode.md); once this is fixed upstream we can drop the fork.
- Dominant language
- Rust
- Stars
- 18.9k
- Forks
- 1.2k
- Avg merge
- 23h 2m
- Merged PRs (30d)
- 259
Getting set up
- No Dockerfile or Docker Compose file
- Has a pull request template
- Read the contributing guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
More from longbridge/gpui-kit
-
DataTable: Tab and Shift+Tab can't leave a focused tablePossibly taken @huacnlee claimed this today. Open
Difficulty 2/5 1-3 hours Newbie friendliness 91/100
longbridge/gpui-kit#3788 ·
Maintainers usually reply within 1 day
-
Difficulty 2/5 1-3 hours Newbie friendliness 67/100
longbridge/gpui-kit#3735 ·
Maintainers usually reply within 1 day
-
Difficulty 2/5 1-3 hours Newbie friendliness 82/100
longbridge/gpui-kit#3711 ·
Maintainers usually reply within 1 day
-
bundle: ignores the manifest entryPossibly taken @huacnlee claimed this 11 days ago. Open
Difficulty 2/5 1-3 hours Newbie friendliness 85/100
longbridge/gpui-kit#3675 ·
Maintainers usually reply within 1 day
-
Difficulty 2/5 1-3 hours Newbie friendliness 73/100
longbridge/gpui-kit#3630 ·
Maintainers usually reply within 1 day
All issues in longbridge/gpui-kit
Similar issues
-
Difficulty 2/5 1-3 hours Newbie friendliness 86/100
Maintainers usually reply within 1 day
-
`nsh-core` fails to compile when taffy's default `calc` feature is enabled elsewhere in the graphOpen
Difficulty 2/5 1-3 hours Newbie friendliness 76/100
longbridge/nsh#966 ·
Maintainers usually reply within 1 day
-
Windows docs: pin a tested ConPTY version for Kitty graphics; 1.25.260930003 corrupts large uploadsOpen
Difficulty 1/5 1-3 hours Newbie friendliness 88/100
raphamorim/rio#2021 ·
Maintainers usually reply within 1 day
-
Difficulty 2/5 1-3 hours Newbie friendliness 84/100
hydro-project/infinity#187 ·
Maintainers usually reply within 1 day
-
Difficulty 2/5 1-3 hours Newbie friendliness 77/100
near/near-sdk-rs#1676 ·
Maintainers usually reply within 2 days