Skip to content

Stop xclip from holding a launcher's captured output streams - #9419

Merged
kuzeyron merged 2 commits into
kivy:masterfrom
charan-rathore:fix/xclip-stdio-leak-9418
Oct 18, 2026
Merged

kuzeyron merged 2 commits into
kivy:masterfrom
charan-rathore:fix/xclip-stdio-leak-9418

Conversation

@charan-rathore

@charan-rathore charan-rathore commented Oct 9, 2026 •

Copy link
Copy Markdown
Contributor

On X11, quitting an app that had used the xclip clipboard left the process's captured output streams open: xclip forks to keep serving the selection and inherits the launcher's pipes, so anything reading them (a test runner, a supervisor, a shell pipeline) never saw EOF and the process looked hung after exit.

Fix

  • kivy/core/clipboard/clipboard_xclip.py: redirect the two streams xclip does not use to /dev/null; only the pipe for the requested selection direction stays connected.

Testing

  • Two tests in kivy/tests/test_clipboard_xclip.py start xclip through the clipboard and assert the parent's output pipe reaches EOF, for both the clipboard and the primary selection.
  • Ran here under Xvfb with real xclip: on master both tests fail (the pipe stays open); with the fix both pass.
  • The one failing CI job (unit_test on macos-15-intel) died while downloading dependencies (connection error before any test ran); unrelated to this change.

ClipboardXclip._clip only redirected the stream it actively uses, so the
xclip selection owner inherited the launcher's remaining stdout/stderr.
When a launcher captures Kivy's output, that surviving process keeps the
pipe open after the Kivy child exits, so the launcher never sees EOF.

Redirect all three streams to DEVNULL and pipe only the one in use.
Add a regression test that round-trips both selections through real
xclip/X11 and requires captured-output EOF once the child exits.

Fixes kivy#9418

Signed-off-by: Charan Rathore <[email protected]>
@ElliotGarbus

Copy link
Copy Markdown
Contributor

Thanks for the fix. I reproduced the problem and confirmed the change works.

I ran your test logic on Ubuntu (WSL) under Xvfb with real xclip. On master the output pipe is never closed, for both the clipboard and primary selections. With this PR it closes in both cases.

BLOCKER: the new tests don't run in CI.
The new tests are skipped in CI (test_clipboard_xclip.py ss in the Ubuntu logs), because xclip isn't installed there. Adding xclip to the apt-get install line in .ci/ubuntu_ci.sh should make them run.

Small suggestion:

  • If communicate(timeout=1) times out, the test fails with a raw TimeoutExpired. A message like "xclip is still holding the output pipe" would be clearer.

The new tests were skipped in CI because xclip was not installed there.
Also fail with 'xclip is still holding the output pipe' instead of a
raw TimeoutExpired when communicate() times out.

Signed-off-by: Charan Rathore <[email protected]>
@charan-rathore

Copy link
Copy Markdown
Contributor Author

Thanks for verifying on real xclip. Both points are in 17de9ec: xclip is added to the Ubuntu CI apt line so the tests actually run there, and a communicate() timeout now fails with "xclip is still holding the output pipe" instead of a raw TimeoutExpired.

@ElliotGarbus
ElliotGarbus requested a review from kuzeyron October 18, 2026 12:00
@ElliotGarbus

Copy link
Copy Markdown
Contributor

@kuzeyron I have tested this fix and it is ready for approval

@kuzeyron
kuzeyron merged commit 261b89e into kivy:master Oct 18, 2026
33 checks passed
@kuzeyron kuzeyron modified the milestones: 2.3.2, 3.0.0 Oct 27, 2026
@AndreMiras

Copy link
Copy Markdown
Member

The backport to devel-2.3.x failed:

The process '/usr/bin/git' failed with exit code 1

To backport manually, run these commands in your terminal:

# Fetch latest updates from GitHub
git fetch
# Create a new working tree
git worktree add .worktrees/backport-devel-2.3.x devel-2.3.x
# Navigate to the new working tree
cd .worktrees/backport-devel-2.3.x
# Create a new branch
git switch --create backport-9419-to-devel-2.3.x
# Cherry-pick the merged commit of this pull request and resolve the conflicts
git cherry-pick -x --mainline 1 261b89e28032cc9aaaae87220bb587c345cb3ee4
# Push it to GitHub
git push --set-upstream origin backport-9419-to-devel-2.3.x
# Go back to the original working tree
cd ../..
# Delete the working tree
git worktree remove .worktrees/backport-devel-2.3.x

Then, create a pull request where the base branch is devel-2.3.x and the compare/head branch is backport-9419-to-devel-2.3.x.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants