Skip to content

windows: Handle watch shutdown races (cherry-pick upstream #958) - #12

Open
cole-miller wants to merge 1 commit into
mainfrom
cherry-pick-upstream-958
Open

cole-miller wants to merge 1 commit into
mainfrom
cherry-pick-upstream-958

Conversation

@cole-miller

Copy link
Copy Markdown
Member

Cherry-pick of notify-rs#958 (upstream bc25704, Daan De Meyer, 2026-07-16), which fixes the intermittent windows::tests::delete_self_dir failure seen on #6 and #11.

The race: RemoveDirectory returns while the open ReadDirectoryChangesW handle keeps the directory delete-pending. The completion callback then arrives with ERROR_ACCESS_DENIED, but dir.try_exists() reports an access error rather than absence, so the handler falls through, tries to re-arm the read on the invalidated handle, and surfaces Io(code 5) instead of the Remove(Folder) event. Upstream adds an is_delete_pending(handle) check (GetFileInformationByHandleEx → FileStandardInfo.DeletePending) alongside try_exists, and also fixes a second shutdown race where a queued callback re-armed a handle that stop_watch had already closed.

Applied with git cherry-pick -x; no conflicts. Compiles for x86_64-pc-windows-msvc; the Windows test run here is the verification.

* windows: handle watch shutdown races

ReadDirectoryChangesW can complete an outstanding request just before
CancelIo runs. When stop_watch closed the directory handle and entered its
alertable wait, that already-queued callback still arrived with
ERROR_SUCCESS and tried to rearm the request using the closed handle. The
resulting ERROR_INVALID_HANDLE was emitted to users and caused overlapping
watch tests to fail intermittently.

Share a stopping flag between each watch state and its completion requests.
Set it before cancellation begins, then have callbacks release the completion
semaphore and return without processing or rearming once the watch is
stopping. This preserves the existing wait-for-completion lifetime guarantee
without submitting I/O against a closed handle.

Directory deletion has a second race: RemoveDirectory can return while the
open watch handle keeps the directory delete-pending. In that window,
try_exists may report an access error instead of absence, causing the callback
to rearm the invalidated handle and emit ERROR_ACCESS_DENIED. Query
FILE_STANDARD_INFO on the existing handle and treat DeletePending as directory
removal while preserving genuine permission failures.

The stopped-completion regression test verifies that a queued successful
completion is acknowledged without an error or another unwatch. The existing
delete_self_dir test covers removal notification for delete-pending directory
handles.

Signed-off-by: Daan De Meyer <daan@amutable.com>

* docs: add changelog entry

---------

Signed-off-by: Daan De Meyer <daan@amutable.com>
Co-authored-by: Yuki Okushi <huyuumi.dev@gmail.com>
(cherry picked from commit bc25704)
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