Skip to content

Fix Python controller hang at exit with stdout/stderr redirection on Windows - #7049

Open
rin-katsuragi wants to merge 1 commit into
cyberbotics:masterfrom
rin-katsuragi:fix-issue-7040-python-stdout-redirect
Open

rin-katsuragi wants to merge 1 commit into
cyberbotics:masterfrom
rin-katsuragi:fix-issue-7040-python-stdout-redirect

Conversation

@rin-katsuragi

@rin-katsuragi rin-katsuragi commented Oct 10, 2026 •

Copy link
Copy Markdown

Description
On Windows, Python controllers run with --stdout-redirect or --stderr-redirect (or with WEBOTS_STDOUT_REDIRECT / WEBOTS_STDERR_REDIRECT set) hung indefinitely upon script completion.

This was caused by a circular deadlock in StdStreamRedirect:

  1. The redirect helper thread was non-daemon (daemon=False) and blocked on self._r.readline() on the anonymous pipe.
  2. At interpreter exit, Python joins all non-daemon threads before running atexit callbacks.
  3. Because the helper thread never exited (the pipe's write end self._w remained open), Python hung permanently before atexit could close it.

Fix:

  • Mark the helper thread as a daemon thread (daemon=True) so Python proceeds into the atexit stage.
  • Add an explicit, idempotent close() method registered directly via atexit.register(...). Closing the write end sends EOF down the pipe, allowing self._thread.join() to safely flush all remaining output to ucrtbase before process exit.
  • Restore sys.stdout / sys.stderr to the original streams before closing the pipe to prevent ValueError: I/O operation on closed file during late interpreter shutdown.

Related Issues
Fixes #7040

Tasks

  • Update the changelog
  • Update the documentation (not applicable, internal redirect fix)

Additional context
Verified on Windows 11 with Python 3.13 / 3.14: scripts now flush completely and terminate with exit code 0 immediately. Conforms to PEP8 and repository source check standards.

…berbotics#7040)

On Windows, Python controllers started with --stdout-redirect or --stderr-redirect hung indefinitely upon script completion. The helper thread in StdStreamRedirect was non-daemon and blocked in readline() on the pipe, while Python waits for non-daemon threads before running atexit handlers where the write end was supposed to close.

Resolve the deadlock by making the helper thread a daemon thread, adding an explicit close() method registered with atexit, and restoring original stream handles upon cleanup.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

Shutdown can still deadlock when pending output fills the native redirect pipe.

1 open finding
What changed in this PR

Addresses #7040 by updating Windows Python controller output-redirection shutdown.

Changes:

  • Makes redirect helpers daemon threads and registers explicit cleanup at exit.
  • Restores original streams before closing and joining helpers.
  • Documents the fix in the changelog.
File Description
lib/​controller/​python/​controller/​robot.py Updates redirect-thread lifecycle and stream cleanup.
docs/​reference/​changelog-r2025.md Records the Windows controller shutdown fix.

🧠 Review effort: Balanced


💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

elif self._stream == 2:
sys.stderr = self._orig
self._w.close()
self._thread.join()

This branch has not been deployed

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

Labels

bug Something isn't working test suite Start the test suite

Development

Successfully merging this pull request may close these issues.

Python controllers started with --stdout-redirect or --stderr-redirect never exit on Windows

3 participants