Skip to content

Start StageKit's games one at a time, and again when the binary is busy - #522

Open
GuvenHuso wants to merge 1 commit into
testingfrom
fix-stagekit-text-file-busy
Open

GuvenHuso wants to merge 1 commit into
testingfrom
fix-stagekit-text-file-busy

Conversation

@GuvenHuso

Copy link
Copy Markdown
Collaborator

StageKit's scenarios sometimes fail on CI with OSError: [Errno 26] Text file busy. In run 40916137686 (PR #521) this failed all five obstacle tests.

Why it happens

conftest.py's sessions() plays the scenarios side by side from a thread pool. runner.launch() puts a copy of the game in each scenario's folder. On CI, dist/ and /tmp are on different file systems, so os.link fails and shutil.copy writes the copy instead. If another thread forks while that copy is still open for writing, the child keeps the descriptor until it runs its own program. Starting the copied game during that time fails with ETXTBSY.

The change (tools/stagekit/tests/runner.py only)

  • launch() now copies the game and starts it under one lock (LAUNCHING). No other launch can fork while a copy is open, and Popen returns only once the child is running the game.
  • generate()'s tools fork from other threads outside the lock. To cover them, the start is tried up to five times, a tenth of a second apart, when it fails with ETXTBSY. Any other error is raised at once.

Checked

  • On Windows, where the scenarios skip without a Linux game: the 20 tests still collect and skip.
  • With a stand-in for Popen: it fails twice as busy, then starts; it gives up after five tries; other errors are raised at once; eight threads launching together each get a whole copy.
  • The scenarios themselves run in this PR's Linux CI job.

🤖 Generated with Claude Code

conftest.py's sessions() plays the scenarios side by side from a thread
pool, and launch() puts a copy of the game in each scenario's folder. On
CI dist/ and /tmp are on different file systems, so os.link fails and
shutil.copy writes the copy. If another thread forks while that copy is
open for writing, its child holds the descriptor until it runs its own
program, and starting the copied game in that time fails with
OSError: [Errno 26] Text file busy. That failed all five obstacle tests
in CI run 40916137686 (PR #521).

launch() now copies the game and starts it under one lock, so no other
launch forks while a copy is open (Popen returns only once the child has
run the game). The forks of generate()'s tools in other threads are not
under the lock, so the start is tried up to five times, a tenth of a
second apart, when it fails with ETXTBSY; any other error is raised at
once.

Checked on Windows, where the scenarios skip without a Linux game: the
tests still collect and skip, and a stand-in for Popen showed the retry
(two busy failures, then started), giving up after five, other errors
raised at once, and eight threads each getting a whole copy. Not yet run
on Linux; CI's Linux job runs the scenarios.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@VerySmuggy VerySmuggy added bug Something isn't working priority: medium Normal priority labels Oct 29, 2026

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 priority: medium Normal priority

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants