fix: move IN-loop clipboard ops off watchdog thread pool (Windows)#12
Merged
Merged
Conversation
On Windows, watchdog's Observer uses WindowsApiObserver which dispatches events through a thread pool. The old code ran file I/O and pyperclip clipboard access synchronously inside the watchdog event handler, blocking pool workers under load and causing pool-exhaustion errors and UI slowness that didn't reproduce on Linux. - Add _in_queue (SimpleQueue) and a dedicated clipsync-in thread that drains the queue and calls _on_file_changed. The watchdog handler now just puts a path on the queue and returns immediately, keeping the thread pool free. - Cache resolved clipboard file paths in _ClipboardFileHandler so _matches() doesn't call Path.resolve() three times per event. - Add fast name-based pre-filter before the (more expensive) resolve() call, since Syncthing generates many temp-file events for other files. https://claude.ai/code/session_0128MyVVEu1Dt3jTeaZ1edyL
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Root cause
On Windows,
ObserverusesWindowsApiObserver, which in watchdog 4.x dispatches filesystem events through an internal thread pool. The previous code called_on_file_changed()synchronously inside the watchdog event handler, meaning every incoming file-change event blocked a pool worker thread while doing:pyperclip.paste()— Windows clipboard access, a GUI resource that serializes between processes and can stallpyperclip.copy()— sameUnder normal Syncthing activity (many temp-file events), all pool workers could be blocked simultaneously →
BrokenExecutor/ pool-exhaustion error + the app becoming sluggish or unresponsive. Linux usesInotifyObserverwhich doesn't have a thread pool limit in the same way, so the bug didn't surface there.There was also a secondary performance issue:
_matches()calledPath.resolve()three times on every filesystem event (including all the Syncthing temp files), which is expensive on Windows.Changes (
clipsync/clipboard.py)_in_queue+clipsync-inthread: watchdog handler now just doesqueue.put(path)and returns immediately, keeping the pool free. A dedicated thread we own drains the queue and calls_on_file_changed()._ClipboardFileHandler.__init__: resolved once at startup, reused on every event._matches(): checksPath(path).nameagainst a set{"clipboard.txt", "clipboard.png"}before the more expensivePath.resolve()call, so Syncthing temp-file events are rejected in O(1)._in_loopshutdown:stop()posts a sentinel ("") to unblock the queue get, joins the thread with a 3 s timeout.Test plan
clipsync-inthread appears in thread listing at startupstop())https://claude.ai/code/session_0128MyVVEu1Dt3jTeaZ1edyL
Generated by Claude Code