fix(vcores): hos_vcore_back ends an escape too, so no inner block takes a core early #2387
No reviewers
Labels
No labels
accessibility
autorelease: pending
autorelease: tagged
bug
dependencies
documentation
duplicate
enhancement
github_actions
good first issue
help wanted
invalid
question
testing wanted
wontfix
No milestone
No assignees
1 participant
Due date
No due date set.
Dependencies
No dependencies set
Reference
MagicBeanDev/Hookshot!2387
Loading…
Reference in a new issue
No description provided.
Delete branch "fix/vcore-escape-unpaired-back"
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?
What this is for: this makes the virtual-core escape fix of #2302 (issue #1929) safe against one kind of future change. @hckrman101 asked for it as an optional point in his review (comment). Nothing that runs today changes, because no call in the code reaches the case: it is there so that a later
hos_vcore_back()without itshos_vcore_away()cannot leave an escape flag up. What is measured: the test with g++ and clang++, on this branch and against main'shost/hos_thread.c, and the whole oftests/run_all.pyon Arch Linux. Not measured: no game run, because no call reaches the changed case. Windows was not tried.Problem
vc_acquire), andvc_escapedis set. Since #2302 the thread's nexthos_vcore_away()answers 1 and clears the flag, so thehos_vcore_back()paired with it takes a core again.hos_vcore_back()itself leaves the flag alone. Today everybackfollows anawaythat answered 1, except the two at a thread's start (hos_boot_main_thread,thread_main). No escape can be up there yet: the thread is new and zeroed, and thatbackis its first wait for a core. So the case below cannot happen now.backwithout itsawayon a thread that has escaped. The thread would take a core and keep the flag. Its next outer block would give the core up as usual. A block nested inside that one would then answer 1 and take a core in the middle of the outer block, which is the opposite of what the nesting rule is for.What changed
host/hos_thread.chos_vcore_back: clearsvc_escapedbeforevc_acquire, which sets it again if the thread escapes again. So after anyback, the flag is up only when the thread is again running without a core. The comment on the struct field says this now.backand notvc_take(the other place the review named): a thread waiting in the queue gets its core fromvc_grant.vc_grantruns on the thread that gives the core up, so a clear invc_takelimited tot == curwould miss that path.tests/test_vcore_escape.pyhas two new cases.backwithout anaway, then nests two blocks. The innerawaymust answer 0, and no core may be taken inside the outer block.backescapes again while both cores are held. The thread must still take a core after its next block.How it was tested
Arch Linux, kernel 7.2.15, g++ 16.2.1, clang++ 23.1.3, 2026-11-11. Branch head b52842db on main
b744b1ec.ok.host/hos_thread.c(b744b1ec), with both compilers:vc_acquireinstead, the flag of an escape that has just happened is wiped, and 8 checks fail with both compilers.tests/test_invalidate_vcore.py, which cutshos_vcore_awayandhos_vcore_backout of the source, passes with both compilers.tests/run_all.py: 373 passed, 4 failed, 10 skipped. Two of the failures fail on this machine whatever the change:test_updater_asset_digest.py(only the .NET 10 SDK is installed here, and the fixture needs net8.0) andtest_wayland_libdecor_hint.py(clang 23's-Wunused-but-set-globalunder-Werror). The other two,test_vcore_fs_open.pyandtest_vcore_video_acquire.py, fail on main itself until #2380 lands: since #2042 they no longer compile.Not tested
back, but no call today has a flag up there, so I expect no difference in the game. That is an inference from the code, not a measurement.Risks
back. The flag is touched only by its own thread, as before.Closes #2386.
Assisted-by: Claude Opus 5.5
Co-Authored-By: Claude Opus 5.5 [email protected]
Rebased onto main
b29967bfso the workflows run here: they had waited for an approval on the old head, and main's game-free tests are green again since #2380. The patch is unchanged (git range-diff: the commit is equal), and its test passes on the new base.Thanks for following up on the #2302 nit. Clearing
vc_escapedinhos_vcore_backbeforevc_acquireis the right spot:vc_acquiresets the flag again if the thread escapes again, so after anybackthe flag is up only while the thread really is running without a core. Your reason for not putting it invc_takeholds too, sincevc_grantruns on the releasing thread.What I checked:
vc_escapedis still written only by its own thread (vc_acquireont,hos_vcore_away, nowhos_vcore_back).vc_grant/vc_releasenever touch it, so the extra store at eachbackadds no cross-thread access.vc_held == 0, so the outermostawaystill answers 1 and clears it, inner ones answer 0, and only the outerbacktakes a core. The paired callers (hos_thread_wait,hos_cv_sleep, the otherif (away) hos_vcore_back()sites, the #2301/#2322 users) behave exactly as before. The two unpairedbacks at thread start see a zeroed flag, so nothing changes there today, as the description says.vc_yieldgoes throughvc_release/vc_acquiredirectly rather thanback, so a re-escape there still leaves the flag up until the next block, as intended.back) guards against moving the clear aftervc_acquire.CI:
test_vcore_escape.pyandtest_invalidate_vcore.pypass on Ubuntu and Windows. The two red jobs aren't from this PR: Windows part 1/2 fails intest_native_astc_texture_count.py(scanfdeprecated under-Werrorwith clang on Windows), and appimage timed out fetching system packages.Looks good to me.
Thanks for reviewing the flag ownership and re-escape cases. The unrelated Windows ASTC fixture failure is tracked in #2397; no further scheduler change is needed for that CI failure.
github-sync referenced this pull request2026-11-12 06:22:54 +00:00