Tracking: Thread scheduler scheduling
wheremyfoodat opened this issue · 3 comments
It is important to remember the thread scheduler only pretends to be cooperative and loves to preempt.
Particularly any sort of ARM11 timer interrupt can preempt the running process, which is important to emulate since a lot of games seem to get stuck due to multithreading issues (eg #88).
Unimplemented examples of this pre-emption can be seen in
- ArbitrateAddress (timing out will preempt, which we don't emulate)
- WaitSync1/WaitSyncN (timing out again)
- SleepThread (Waking up will preempt)
- OS timers firing
- TBD
We do have some cases implemented (eg events firing), but it's probably not good enough and still causes bugs/games hanging.
Below I attach some relevant resources from our Discord server.
- 3DBrew multithreading page: https://www.3dbrew.org/wiki/Multi-threading
- Further findings by @Subv https://gist.github.com/Subv/02f29bd9f1e5deb7aceea1e8f019c8f4
Additionally
- File and archive operations will make the running thread go to nap and cause a reschedule (need to wait for syscore and then for Process9)
- SendSyncRequest behaves similarly.
Both are quite important and very likely break games. Especially SendSyncRequest.
Thanks to these 2 yielding the running thread, it means that a program can completely ignore threads other than the main thread, and the kernel will still occasionally put the thread to sleep and give other threads time to shine.
Also as a side note the address arbiter code only works if the game only ever creates a single arbiter (because it straight up ignores which arbiter a thread is sleeping on)
In my defense I wrote the relevant code around the same day I started the emulator and had no idea what I was doing
Some games like Sonic Boom seem to ask for threads to run on the syscore. These threads typically have low priority so they may never actually get to run in the emulator, while real hardware would run them in parallel to the appcore using the limited syscore time applications can take up.
Old Citra fixed this by boosting the priority of starved threads, while newer Citra uses separate per core thread-managers.