Repository navigation
fix(libSceJobManager): spin idle workers without taking the manager lock - #12085
Conversation
A spinning or persistent worker unlocked and relocked the manager mutex around every pause. Two such workers contend on that mutex all the time, and on Windows the winpthreads mutex then puts them to sleep in the kernel: in guest_job_manager_worker_count the two persistent workers used 125 ms and 0 ms of CPU in 2 s and the check failed. Workers now pause on a work epoch that WakeWorkers, persistence changes and shutdown advance, and retake the lock when it changes or after a bounded spin, so spinning reacts to new work without contending on the mutex. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
Linux run at this PR's exact head Arch Linux, kernel 7.2.9, Ryzen 9 7950X, g++ (GCC) 16.2.1 20260810, glibc 2.44, x86-64. Release, Passed in each of 3 runs, exit code 0 each time, with no failed, skipped or not-run tests:
Only the CPU-only job manager tests were run ( These are the focused tests only; no title or console was used. AI-assisted: yes (Claude Code). |
|
Some comparisons could not be completed because a pull request conflicts with the base branch. Review these shared files manually before concluding that there is no overlap.
|
MarcZX10
left a comment
There was a problem hiding this comment.
I reviewed the current head f25e58c8cc07da5c12e96e559d8d6d9d0c0e018e.
The change keeps manager state under manager.lock and uses workEpoch only to bound how long a worker spins before rechecking that state. The Release guest_job_manager_tests target built successfully from this exact head in my isolated Linux checkout. I could not get a local runtime pass: all 29 registered guest_job_manager_* tests abort on this host because its Xeon E5-1620 v2 lacks the AVX2/BMI/LZCNT features required by the project. I therefore do not count that run as a test failure in the change. The PR reports 29 Windows test cases passing three times, and the author later supplied focused Linux failure/registration test runs.
There is an integration blocker with open PR #10249. Its current Export.cpp implementation replaces WorkerEntry's spin loop with condition-variable waiting, while this PR changes that same loop to poll workEpoch; a pairwise merge preview conflicts in Export.cpp. These are competing worker-loop behaviors, not just unrelated edits to the same file. Please coordinate with #10249's author on which behavior should land first or whether one supersedes the other, then declare the agreed order with Depends on if applicable. The overlap check also cannot compare #10249 against main while #10249 itself conflicts with the base, so this needs an explicit resolution before the stack can be integrated confidently.
I found no separate defect in the epoch signaling on this head. I’m requesting changes for the unresolved integration order, not for a demonstrated runtime regression.
|
Windows run of this head
AI-assisted: yes (Claude Code) |
|
On the #10249 blocker: I checked its current head, A pairwise This PR doesn't modify those lines. The AI-assisted: yes (Claude Code). |
|
Fresh Windows evidence on current With this PR's diff applied on that AI-assisted: yes (Claude Code). |
|
Revalidated on current
AI-assisted: yes (Claude Code). |
|
Refreshed on current With this head ( AI-assisted: yes (Claude Code). |
MarcZX10
left a comment
There was a problem hiding this comment.
Re-reviewed exact head f25e58c8cc07da5c12e96e559d8d6d9d0c0e018e. The Windows evidence closes the requested worker-count check: guest_job_manager_worker_count passes 3/3 on a clean merge with main edf6804cf5; the author also reports all 29 guest_job_manager_* tests passing in five repeated runs on the earlier merged tree. Current-main merge preview is clean and the effective change remains the worker spin/epoch signaling edit. I checked the overlap note: #10249’s conflict with main is in its stale SubmitCompute context, not this PR’s worker-loop hunk; that PR still needs its own main rebase. No dependency on #10249 is required for this independent worker change. Hosted Windows and conventions pass; product Linux is skipped. I did not run local tests.
|
Tested this on a second host, where
This PR looks like the fix for #13026, so its AI-assisted: yes (Claude Code) |
What
A JobManager worker that spins, whether inside its spin window or because it is persistent, unlocked and relocked the manager mutex around every
_mm_pause(). With more than one spinning worker, they contend on that mutex continuously. On Windows the winpthreads mutex then parks them in the kernel, so "spinning" workers mostly sleep.guest_job_manager_worker_countshows this on currentmain. With 4 workers and 2 set persistent, a local trace confirms that both workers enter the spin path. Yet over the test's 2-second window they used 125 ms and 0 ms of CPU, so the checkactive == persistentfails. The test fails in every run here.Workers now pause on a
workEpochatomic instead of the mutex.WakeWorkers(every publication of work),setPersistentThreadCountand shutdown advance it. A spinner retakes the lock as soon as the epoch changes, or after 1024 pauses, so the spin-window deadline and the other conditions are still re-checked regularly. Sleeping workers and the wake-token logic are unchanged.Tested
Windows 11 Enterprise 10.0.22621, Ryzen 9 5950X (16 cores), WinLibs GCC 15.2.0 UCRT r7, Ninja, Release,
BUILD_TESTING=ON, onmaind13b3d7c55. Vulkan is hidden (VK_DRIVER_FILESpointing at a missing manifest), soguest_job_manager_profiler_gpuskips.guest_job_manager_worker_countfails atRequire(active == persistent)in each of 4 runs onmain14332cd8d0. It was found in a full Windows CTest run of that head.guest_job_manager_*tests pass in each of 3 runs on the headf25e58c8cc, from all five test executables.profiler_gpuskips, as above.guest_job_manager_worker_spinmeasures one worker's idle CPU with spin 50. It is about the same: 91.8 to 92.1 M cycles onmainagainst 98.3 to 100.5 M with this change, 3 runs each. One spinner has no contention, so the old loop already spun there.python tools/check_conventions.py --base origin/mainandgit diff --checkpass.Not tested: Linux and macOS, whose futex-based mutexes may have hidden the effect; game runs; a frame-time comparison. This PR claims only the worker behaviour above, not a performance change.
Checklist
main; no other open PR implements the same functionsNotImplemented_nid_no_patch); silent stubs are listed under Silent stubs in their library's technical debt file.mdfiles or images added to the repository (attach them to this PR)AI-assisted (Claude Code).
🤖 Generated with Claude Code