Game: while a save loads, worker threads sometimes wait the full 2 s for a virtual core and then run without one (main too) #262
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#262
Loading…
Reference in a new issue
No description provided.
Delete branch "%!s()"
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?
Longshot version
main
d4a69456and main0c74253d4, and the branch of pull request 66 at 3fc3c891Channel
Built from source
What kind of problem
Something else
What happened
While the same Kokiri Forest save loads, some of the game's worker threads wait about two seconds for a virtual core they are allowed to run on, and then run without one. The two seconds are the existing escape deadline of
vc_acquireinhost/hos_thread.c, which is there to break a deadlock; the log line it writes isran without a core after 2000 ms.I noticed it while testing #66 (the Walk key) and first took it for something that change did. It is not: it happens on main as well, including this morning's main. Counted over ten start-ups on 13 November 2026:
d4a694560c74253d4The starts alternated between old main and the pull request's build, followed by two of the newer main. Each run was stopped as soon as the scene was reached, with a cap of 120 seconds; all ten reached the scene and ended normally, with no fatal, device-loss or kernel-fault line. Shaders had been prepared beforehand, so shader preparation is not in these runs. No controller was connected and no key was pressed.
d4a69456d4a69456d4a69456d4a694560c74253d40c74253d4What this does not establish:
No fix is proposed here and nothing was switched off. The two older builds contain a test-only key probe from the Walk work that was compiled in and not used in these runs; the build of main
0c74253d4is plain.Wanted: whether others see the line while loading. Searching your own
longshot_log.txtforran without a coreis enough; the build, the place and your CPU would help.Where in the game
Loading a save in Kokiri Forest (child), before play begins
How often
Sometimes
Graphics card and driver version
RTX 4090 24 GB, NVIDIA 615.78.08
CPU, RAM and operating system
Core i9-13900K, 96 GB RAM, Arch Linux (kernel 7.2.15)
Changes from the defaults
1920x1080, Custom profile, the game's own upscaler at 100%, frame generation off, no frame cap; run without a window
longshot_log.txt
Screenshots or video
None: nothing visible is wrong.
Before you send it
Not ticked: seen on main
0c74253d4built from source on 13 November 2026, not on a downloaded nightly.More of the same from a second set of runs on the same PC today, 13 November 2026 (RTX 4090, NVIDIA 615.78.08, Core i9-13900K, Arch Linux), taken for the cost measurement of #61: four start-ups into the same Kokiri Forest save, alternating main and that pull request's build, 1920x1080, run without a window. All four wrote the line, every time while the control bridge still reported the game as loading, and never during the 25 seconds that were measured afterwards. All four runs reached play and ended normally, with no fatal or kernel-fault line and no gap over one second between presented frames.
d725b363d725b363With the ten start-ups above that makes 8 of 14 with at least one line, on three different commits of main and two branches. In these four the core's holder is named as
BaseProcCreateAndDeleteThread(6),ModuleSystemWorker1(2),Presentation Thread(1),BaseProcExecutor1(1),aal::LowPrioWorkerThread(1),Resource Memory(1).I would not read anything into 5 lines on main against 1 on the other build: two starts each, and the first set had the counts the other way round between builds. Still no cause and no effect on play shown.
I took this one and have a cause and a fix ready.
Cause. The waiting threads are pinned to one core (PreActorModelCompose, BakeProbe, bake::ModuleMgr, tera::FileResourceMgr, aglTextureDataMgr::Compaction, PlayReporter) and have a lower priority than everything that holds it. A waiter asks a holder to give the core up only when the holder is worse than itself, and every release goes to the best waiter, so while the load runs the core changes hands between better threads and the pinned thread is never first. I counted it with a diagnostic build of main that logs every take of the waiter's core during a long wait. In 12 starts (the same Kokiri Forest save, windowless, RTX 4090): 15 escapes in 5 starts, the core taken 42,593 to 53,473 times during each 2 s wait (about 21,000 to 27,000 times a second, mostly by priority 16 and 23 threads), the holder at the escape having held it for 24 to 168 microseconds, and no core kept by one thread for the wait. A further 11 waits of 1.2 to 2.0 s ended by themselves the same way. On the lines posted here, the holder is better than the waiter in 22 of 22; in the log on #91 (an RX 9070 XT, 16 GB of RAM, Linux) it is 44 of 44, and its line
vcores: 1352 takes, 866 waits (mean 64434 us, longest 2001.0 ms), 184 kicks, 172 yields, 13 escapes in 5 sis the same fault (that PC was under memory pressure at 1.7 frames a second, so I read it as the same kind of line, not the same cause of the stall).Fix. A waiter that has waited 1 s (
HOS_VCORE_STARVE_US, 0 turns it off) goes first at the next release of a core it may use and asks the worst holder to give the core up, whatever its priority. The 2 s escape stays as the deadlock breaker. On main in ten plain starts, 3 had escapes (8 lines). With the fix against the same build withHOS_VCORE_STARVE_US=0, ten starts each, interleaved: 0 of 10 starts with an escape against 4 of 10 (7 lines), the longest core wait in every start 1.002 s at most against 2.0 s, and the time to play unchanged (median 43.1 and 43.2 s, ordinary runs). The count of starts alone is weak (p = 0.087); the cap on the wait and the take counts above are the evidence. Not tested: Windows, other GPUs and CPUs, play after loading.The change is a game-free test (fails on main, passes with the fix) and about 30 added lines in
host/hos_thread.c; it touches the same block as #327 and #249, which would need a rebase on whichever merges second. The pull request: #594 (work in progress until it is built for Windows and Linux on current main).