fix(build): one build at a time in a work folder, so a second build doesn't compile again what the first is compiling #630
No reviewers
Labels
No labels
area/accessibility
area/audio
area/input
area/install
area/menu
area/renderer
area/saves
area/upscaling
beanstalk: approved @ 1697603
beanstalk: approved @ 2c5a62e
beanstalk: approved @ 4fd82b7
beanstalk: approved @ 64d6d09
beanstalk: approved @ 6f91fae
beanstalk: approved @ 70a78f9
beanstalk: approved @ 8d361dc
beanstalk: approved @ 9d8356e
beanstalk: approved @ abf2227
beanstalk: approved @ d82f202
beanstalk: approved @ e5f1e40
beanstalk: approved @ ee8ab5f
ci: approved
good first issue
gpu/amd
gpu/igpu
gpu/intel
gpu/nvidia
help wanted
kind
bug
kind
chore
kind
docs
kind
feature
kind
performance
kind
question
needs measurements
platform/linux
platform/macos
platform/steam-deck
platform/windows
preview
priority
critical
priority
high
priority
low
regression
status
blocked
status
confirmed
status
duplicate
status
invalid
status
needs-info
status
wontfix
testing wanted
ci: approved
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set
Reference
MagicBeanDev/Longshot!630
Loading…
Reference in a new issue
No description provided.
Delete branch "HumanBean/Longshot:fix/work-lock"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Type: fix · Risk: low · Tested on: Linux, i7-9700K lab · Needs: Windows: a second build started while one runs
Why
Two builds could run in one work folder at the same time. The second finds the first's lift done, compiles again every object the first hasn't cached yet, and then the two race over the game folder's files. That happened on a player's PC: a second build started about 7.5 minutes into a first one and compiled 197 of 480 files over again (451 s). On the lab, the second build then crashed renaming
game/longshot.new, which the first had already moved. A normal update compiles none of the lifted code (below), so this was the only cause found for that rebuild.How to reproduce
Seen on: Linux (a player's PC through the launcher; reproduced on the lab)
Settings and switches: defaults
Steps: start
python -I nx2/make_game.py ... --work <w>, and while it compiles start a second one with the same--work <w>.Expected: the second stops at once, saying a build is using the folder.
Actual: the second build compiles what the first hasn't cached yet. On the lab it then stopped with
FileNotFoundError: game/longshot.new -> game/longshot.What changed
make_game.pynow holds a lock on the work folder for the whole build, and a second build into it stops at once with "Another build is using the work folder … Wait for it to end (or stop it), then build again." The lock is an OS lock, not a marker file, so a stopped or killed build never keeps the next one out.lock_work()innx2/make_game.pyholds<work>/make_game.lock(fcntl.flockon Linux,msvcrt.lockingon Windows). It is called inmain()right afterworkis set, beforestop_stray_compilers,drop_stale_objectsand the lift.make_game --checktakes no lock. A secondmain()in the same process keeps the lock it holds (the tests do this).tests/test_work_lock.py: a second build into a held folder stops, a dead holder frees the lock, andmain()takes the lock before anything in the folder.Measurements
Verdict: The bug is gone on the lab: with the fix, the second of two simultaneous updates was refused in 1 s. Without it, the second compiled 2 of the first's 3 files again and crashed.
Hardware: Linux, i7-9700K,
LONGSHOT_JOBS=6, under the lab's build lockScene: none (the build)
Method: launcher-style updates. Two checkouts at different paths share one
--work, and each job's key parts (source, flag text, deps, each header) were dumped and compared per file.How to reproduce: the steps above
2b1d38c6/225c37f8a947cae32b1d38c6225c37f8(host/control.conly), lift keptcode_*, 29 snx2/lift.pyedit (full re-lift, 104 s)code_*, 29 sgame/longshot.newcode_*file.build_times.json): the two builds ended 19 s apart but took 950.9 s and 521.2 s. The second one's lift took 0.1 s, so it started while the first was compiling.Flags and settings
None
Testing
Tested: on the lab (Linux), the runs above.
tests/test_work_lock.pypasses. The 52tests/test_*.pythat mentionmake_gamefail identically with and without the patch: 9 also fail on main there, because the lab lacks--cc/--cxxarguments, dotnet and the like. Nothing was run on the author's PC.Not tested: Windows (
msvcrt.locking); the fulltests/run_all.py.Must be tested:
make_game.py, start a second with the same work folder, and expect the "Another build is using the work folder" message. Then, once the first has ended, a build in the folder must run.Risks
A tool that starts two
make_game.pybuilds into one work folder on purpose now gets the second refused. Nothing in the tree does that. The lock file stays in the work folder (empty). Reverting the commit restores the old behaviour.What started the second build on the player's PC isn't known. The launcher doesn't stop a build when its window closes, and a new build step always starts another. That is a launcher follow-up.
Checklist
Everyone
type(scope): description) of the status line's type.main.python -I -B tests/run_all.pypasses (any failure that also fails onmainis named). (not run: left to CI)third_party/untouched (a new folder with aPROVENANCE.mdinstead).When it applies
Assisted-by:/Co-Authored-By:trailers.mainand passing here.🤖 Generated with Claude Code
https://claude.ai/code/session_017vw8e3MfaADZobPGhWJk77
WIP: fix(build): one build at a time in a work folder, so a second build doesn't compile again what the first is compilingto fix(build): one build at a time in a work folder, so a second build doesn't compile again what the first is compiling