Compare commits

...

255 Commits

Author SHA1 Message Date
german
e895ab7d6f Implements touch, pan, pinch and rotation gestures 2021-02-27 19:54:42 -06:00
bunnei
0896089092 Merge pull request #5339 from german77/interactive
Settings: Make settings controller image change with controller input
2021-02-07 20:53:46 -08:00
Chloe
d0a760a34a Merge pull request #5872 from lioncash/svc-error
svc: Provide more detailed error logs for svc functions
2021-02-08 12:27:36 +11:00
Rodrigo Locatti
4c82c08897 Merge pull request #5888 from Morph1984/ogl-4.6
renderer_opengl: Update OpenGL backend version requirement to 4.6
2021-02-07 21:44:49 -03:00
LC
148fb12bf3 Merge pull request #5889 from ogniK5377/morton-remove
video_core: Delete morton
2021-02-07 18:48:28 -05:00
Chloe Marcec
c5f109bc50 video_core: Delete morton
moron.h & morton.cpp are not used anywhere and are just empty files
2021-02-08 10:20:21 +11:00
Morph
6e5cc977ad renderer_opengl: Update OpenGL backend version requirement to 4.6 2021-02-07 16:32:35 -05:00
bunnei
230e71b255 Merge pull request #5887 from ogniK5377/lm-fix
lm: Fix ReadLeb128
2021-02-07 10:25:56 -08:00
Morph
458be11f93 Merge pull request #5878 from aleasto/master
pl_u: Fix read out of bounds
2021-02-07 22:20:47 +08:00
Chloe Marcec
9d5a56a40b lm: Fix ReadLeb128
Fixes assertion on Bloodstained Ritual of the Night.

We would over read sometimes, this is fixed by checking if the top bit is set in the first iteration. We also lock the loop off to be only the max size of the type we can fit. Finally we changed an incorrect print of "DEBUG" to "TRACE" to reflect the proper log severity
2021-02-07 23:52:56 +11:00
german
8893b766c3 Add GC controller animation 2021-02-07 00:15:24 -06:00
bunnei
4a01812ebe Merge pull request #5885 from MerryMage/ring_buffer-granularity
ring_buffer: Remove granularity template argument
2021-02-06 13:18:41 -08:00
bunnei
9078f4a9c7 Merge pull request #5871 from lioncash/address-arb
k_address_arbiter: Minor cleanup
2021-02-06 13:05:19 -08:00
MerryMage
8d00265998 ring_buffer: Remove granularity template argument
Non-obvious bug in RingBuffer::Push(std::vector<T>&) when granularity != 1

Just remove it altogether because we do not have a use for granularity != 1
2021-02-06 19:16:00 +00:00
Alessandro Astone
32d9a83f8e pl_u: Fix read out of bounds 2021-02-06 18:44:01 +01:00
german
160341fcf8 Refresh debug controller settings 2021-02-06 09:43:42 -06:00
german
d6a0975e5d Refresh controller only when necessary 2021-02-06 09:43:42 -06:00
german
c9597af39d Add SL SR vectors, change dual joycon view, add missing raw data from keyboard/mouse 2021-02-06 09:43:42 -06:00
german
a7f9983563 Add controller window and single joycon top view 2021-02-06 09:43:42 -06:00
german
ea1f656d7e Replace text with vectors 2021-02-06 09:43:42 -06:00
german
481cd86722 Make settings controller image change with controller input 2021-02-06 09:43:41 -06:00
bunnei
61bf850f3d Merge pull request #5326 from german77/hidUpdate1
HID: Update the HID service to match more closely to switchbrew part 1
2021-02-06 02:40:11 -08:00
Lioncash
40ab2b9348 k_address_arbiter: Unfold R_UNLESS macros
Allows for more descriptive error messages and also doesn't hide
control-path exit returns from the reader.
2021-02-06 04:08:27 -05:00
Lioncash
b8fc74d74d k_address_arbiter: Remove unnecessary usages of std::addressof
This is a useful function in a generic context or with types that
overload unary operator&. However, primitives and pointers will never do
this, so we can opt for a more straightforward syntax.
2021-02-06 04:06:33 -05:00
Lioncash
7b9c58880f k_address_arbiter: Remove dead code
This code is never used, so we can remove it. It's in version control,
so it can always be brought back when needed.
2021-02-06 04:06:33 -05:00
Lioncash
75a60a6e22 svc: Provide more detailed error logs for svc functions
Allows SVC calls to have much more informative information during error
cases. This also doesn't hide control flow returns from the reader.
2021-02-06 02:03:40 -05:00
bunnei
1498a7c9a8 Merge pull request #5862 from bunnei/kevent
Kernel Rework: Refactor KEvent/KReadableEvent/KWritableEvent
2021-02-05 23:00:43 -08:00
bunnei
3a804752cb Merge pull request #5875 from lioncash/identifier
k_priority_queue: Minor cleanup
2021-02-05 17:02:13 -08:00
bunnei
ea4f62615e hle: kernel: Drop R_UNLESS_NOLOG in favor of expanded if-statement. 2021-02-05 14:03:36 -08:00
bunnei
546af64340 hle: kernel: KAddressArbiter: Remove noisy error log. 2021-02-05 14:03:36 -08:00
bunnei
eba3c59a61 hle: kernel: svc: Cleanup KEvent/KReadableEvent/KWritableEvent SVCs. 2021-02-05 14:03:36 -08:00
bunnei
18175c71ed common: scope_exit: Add a cancellable ScopeExit macro. 2021-02-05 14:03:36 -08:00
bunnei
ff3c7c068b hle: kernel: Reimplement KReadableEvent and KWritableEvent. 2021-02-05 14:03:32 -08:00
bunnei
6bf80dfee0 hle: kernel: Implement KEvent. 2021-02-05 14:00:36 -08:00
bunnei
e9446d232f hle: kernel: KAddressArbiter: Use R_UNLESS_NOLOG where applicable. 2021-02-05 14:00:36 -08:00
bunnei
4577dcd5f9 common: common_funcs: Add R_UNLESS_NOLOG for scenarios that should not log. 2021-02-05 14:00:36 -08:00
bunnei
3f942c01f0 hle: kernel: Rename WritableEvent to KWritableEvent. 2021-02-05 14:00:36 -08:00
bunnei
e86a7e3691 hle: kernel: Rename ReadableEvent to KReadableEvent. 2021-02-05 14:00:36 -08:00
bunnei
b0727c90c5 Merge pull request #5867 from Morph1984/am-GetHealthWarningDisappearedSystemEvent
IApplicationFunctions: Implement GetHealthWarningDisappearedSystemEvent
2021-02-05 13:49:49 -08:00
bunnei
741dc13c5a Merge pull request #5865 from lat9nq/conditionally-quiet
video_core: host_shaders: Don't pass --quiet to glslangValidator if unavailable
2021-02-04 17:07:17 -08:00
bunnei
00cb631b2f Merge pull request #5876 from lioncash/truncation
k_affinity_mask: Avoid implicit truncation to bool
2021-02-04 14:44:11 -08:00
Lioncash
756365386a k_affinity_mask: Avoid implicit truncation to bool
This can cause compiler warnings. Instead, we can explicitly add a
boolean expression around it to naturally turn the result into a bool.
2021-02-04 15:35:46 -05:00
Lioncash
b944edc85d k_priority_queue: Unfold several declval usages
Given these are only used as function existence checks, we can simplify
some usages of declval, given they aren't particularly useful here.

Reduces a few template instantiations, which at most reduces compile
times a tiny bit.
2021-02-04 15:18:58 -05:00
Lioncash
31e6e58101 k_priority_queue: Simplify affinity mask type alias
We can make use of the _t variants of the templates to cut down on a
little bit of verbosity.
2021-02-04 14:57:41 -05:00
Lioncash
53aec1fe2d k_priority_queue: Resolved reserved identifier
An identifier containing a starting underscore followed by a capital
letter is reserved by the standard. It's trivial to avoid this by moving
the underscore to the end of the identifier.

While the likelihood of clashing here being minimal, we can turn a
"should not break" scenario into a definitive "will not break" one, so
why not?.
2021-02-04 14:55:08 -05:00
bunnei
eb3afd30b1 Merge pull request #5874 from Morph1984/create-keys-dir
key_manager: Create the keys directory if it does not exist
2021-02-04 11:47:14 -08:00
Morph
806e2d7900 key_manager: Create the keys directory if it does not exist 2021-02-04 06:05:50 -05:00
bunnei
b331bb5210 Merge pull request #5870 from german77/hanheldfix2
config: Always update handheld config
2021-02-03 19:02:59 -08:00
german
8019b2b9b5 Add footer types and address comments 2021-02-03 20:17:08 -06:00
german
9a9e81f2e9 Fix npad struct to match switchbrew 2021-02-03 20:17:08 -06:00
german
f30ef98761 Adds missing controller types and properties 2021-02-03 20:17:08 -06:00
bunnei
cde532cc52 Merge pull request #5863 from ogniK5377/disable-reverb
audren: Disable reverb for the time being
2021-02-03 18:05:54 -08:00
german
c1b81f776c Always update handheld config 2021-02-03 17:48:04 -06:00
bunnei
b0c9752663 Merge pull request #5848 from ogniK5377/k-resourcelimit
kernel: Rewrite resource limit to be more accurate
2021-02-03 14:53:25 -08:00
Chloe Marcec
2c6e940493 Simplify limitableresource names 2021-02-03 12:55:16 +11:00
bunnei
48d040fded Merge pull request #5842 from german77/userfix
acc: Fix error when second user is selected
2021-02-02 15:43:51 -08:00
bunnei
e5a76d728f Merge pull request #5841 from german77/username
Avoid overwriting username
2021-02-02 15:04:09 -08:00
LC
82d232db46 Merge pull request #5866 from Morph1984/log-all-paths
settings: Log the cache, config and mod load directories
2021-02-02 11:07:42 -05:00
Morph
13b08376b7 IApplicationFunctions: Implement GetHealthWarningDisappearedSystemEvent 2021-02-02 10:47:38 -05:00
Morph
9137f3ec68 settings: Log the cache, config, and mod load directories 2021-02-02 04:01:05 -05:00
lat9nq
fc43eac82a video_core: host_shaders: Don't pass --quiet to glslangValidator if unavailable
Prevents CMake from calling `glslangValidator` with `--quiet` when it is
not available, i.e. on older downstream versions from Ubuntu.
2021-02-01 23:39:54 -05:00
bunnei
d4ebc9a120 Merge pull request #5861 from german77/HandheldFix
hid: Only update motion for npad and prevent over scheduling events
2021-02-01 18:46:26 -08:00
Chloe Marcec
64c3582705 Compile error 2021-02-02 13:23:34 +11:00
Chloe Marcec
9e4b2d60bc Address issues 2021-02-02 13:23:00 +11:00
LC
5e49b81d4d Merge pull request #5864 from MerryMage/is-thumb
arm_dynarmic_32: Print out CPSR.T on exception
2021-02-01 13:36:55 -05:00
MerryMage
821fc4a7b6 arm_dynarmic_32: Print out CPSR.T on exception 2021-02-01 18:35:33 +00:00
bunnei
f317b0d354 Merge pull request #5859 from Morph1984/nifm
nifm: Stub GetCurrentNetworkProfile and GetCurrentIpConfigInfo
2021-01-31 21:31:14 -08:00
Ameer J
f614d7d887 Merge pull request #5856 from Morph1984/nifm-fix-getappletinfo-stub
nifm: Fix GetAppletInfo stub
2021-01-31 23:33:11 -05:00
Chloe Marcec
9fc7f60b94 audren: Disable reverb for the time being
As this is causing issues in a few games, it's best to have it disabled until it's completely implemented
2021-02-01 14:41:58 +11:00
bunnei
67d08f14af Merge pull request #5858 from Morph1984/IsGamePlayRecordingSupported-stub
am/IApplicationFunctions: Stub IsGamePlayRecordingSupported
2021-01-31 18:04:04 -08:00
german
2489547dc5 Only update motion for npad and prevent over scheduling events 2021-01-31 18:52:38 -06:00
bunnei
1498ece290 Merge pull request #5860 from Morph1984/prepo-transmission-stub
prepo: Stub RequestImmediateTransmission and GetTransmissionStatus
2021-01-31 12:26:00 -08:00
Morph
f7dc77e03a Merge pull request #5846 from ameerj/analog-join
analog_from_button: Fix update_thread.join exception
2021-01-31 23:06:06 +08:00
Morph
8c7d89e6c7 nifm: Stub GetCurrentIpConfigInfo
- Used by Lets Sing 12
2021-01-31 07:36:37 -05:00
Morph
7d9465d47a prepo: Stub GetTransmissionStatus 2021-01-31 07:07:11 -05:00
Morph
2394807b42 prepo: Stub RequestImmediateTransmission
- Used by Animal Crossing: New Horizons
2021-01-31 07:04:35 -05:00
Morph
4bf1cf1f81 nifm: Stub GetCurrentNetworkProfile
- Used by Minecraft Bedrock Edition
- Used by Bloons TD 5
2021-01-31 06:56:29 -05:00
Morph
70a2065828 nifm: Add several structs 2021-01-31 06:56:29 -05:00
bunnei
ec51f8de82 Merge pull request #5857 from Morph1984/bsd-fix-eventfd-stub
bsd: Fix EventFd stub
2021-01-31 01:41:06 -08:00
Morph
9f6b35e61f am/IApplicationFunctions: Stub IsGamePlayRecordingSupported
Used by RetroArch
2021-01-31 03:11:03 -05:00
Morph
ded094a340 bsd: Fix EventFd stub 2021-01-31 02:57:56 -05:00
bunnei
1cc009a996 Merge pull request #5855 from Morph1984/bsd-fix-getsockopt-stub
bsd: Fix GetSockOpt stub
2021-01-30 23:21:21 -08:00
Morph
9914db8daa nifm: Fix GetAppletInfo stub 2021-01-31 02:19:36 -05:00
Morph
94f660d1cb Merge pull request #5851 from ameerj/pop-inv-stub
am: Stub TryPopFromFriendInvitationStorageChannel
2021-01-31 14:18:40 +08:00
Morph
6cc769065d bsd: Fix GetSockOpt stub 2021-01-31 01:08:56 -05:00
bunnei
360ea64738 Merge pull request #5852 from ameerj/event-fd-stub
bsd: Stub EventFd
2021-01-30 21:38:08 -08:00
ameerj
ad146eca60 bsd: Stub EventFd
Used by Family Feud
2021-01-30 21:47:32 -05:00
ameerj
99dcf7da21 am: Stub TryPopFromFriendInvitationStorageChannel
Used by Family Feud
2021-01-30 21:43:55 -05:00
Chloe Marcec
ee333e063d fix compile error 2021-01-30 21:51:22 +11:00
Chloe Marcec
56742c6222 cleanup commenting 2021-01-30 21:20:35 +11:00
Chloe Marcec
7791cfd960 Drop m_ from lock 2021-01-30 21:19:49 +11:00
Chloe Marcec
3bf62c7a8a Move to GetGlobalTimeNs, fix GetTotalPhysicalMemoryAvailable 2021-01-30 21:03:10 +11:00
Chloe Marcec
3be1a565f8 kernel: Rewrite resource limit to be more accurate
Matches closer to hardware
2021-01-30 20:40:49 +11:00
bunnei
a4526c4e1a Merge pull request #5779 from bunnei/kthread-rewrite
Rewrite KThread to be more accurate
2021-01-29 23:06:40 -08:00
bunnei
5861bacafd Merge pull request #5795 from ReinUsesLisp/bytes-to-map-end
video_core/memory_manager: Add BytesToMapEnd
2021-01-29 22:56:29 -08:00
Morph
f67cfebada Merge pull request #5838 from german77/prepostub
prepo: Stub GetSystemSessionId
2021-01-30 14:19:59 +08:00
bunnei
aaf834ffa6 Merge pull request #5847 from bunnei/update-dynarmic
externals: Dynarmic: Update to latest to include A32 ISB hook.
2021-01-29 21:52:25 -08:00
german
9150b8972e Stub GetSystemSessionId 2021-01-29 18:41:28 -06:00
bunnei
650734cc3e Merge pull request #5805 from german77/HandheldFix
Fix connect and disconnect controller events
2021-01-29 16:39:01 -08:00
bunnei
15d3376399 externals: Dynarmic: Update to latest to include A32 ISB hook.
- Fixes perf. issues with Megadimension Neptunia VII.
2021-01-29 16:36:45 -08:00
ameerj
ef8b3623f5 analog_from_button: Fix update_thread.join exception
This commit aims to address an exception that occurs when trying to join the Analog object's update_thread.

By using an atomic bool for the status of the update thread, we ensure its value is consistent across the threads accessing it.
2021-01-29 18:41:22 -05:00
bunnei
b786568c5a Merge pull request #5809 from ogniK5377/FlushAudioOutBuffers
audout: FlushAudioOutBuffers
2021-01-28 21:54:46 -08:00
bunnei
543e212554 hle: kernel: KLightLock: Fix several bugs. 2021-01-28 21:53:21 -08:00
bunnei
8d1afcb90f common: common_funcs: Change R_UNLESS to LOG_ERROR. 2021-01-28 21:51:16 -08:00
bunnei
c8fe8247ee arm: dynarmic: Reintroduce JIT checks on SaveContext/LoadContext. 2021-01-28 21:50:39 -08:00
bunnei
e24c6dab93 hle: kernel: KThread: Release thread resource on thread exit. 2021-01-28 21:49:47 -08:00
bunnei
10738839ad yuzu: debugger: Ignore HLE threads. 2021-01-28 21:42:27 -08:00
bunnei
3856564727 hle: kernel: process: Add state lock. 2021-01-28 21:42:26 -08:00
bunnei
ff46ef7ea3 hle: kernel: threading: Fix bug with host thread naming. 2021-01-28 21:42:26 -08:00
bunnei
6ee8340a6b hle: kernel: k_scheduler_lock: Cleanup. 2021-01-28 21:42:26 -08:00
bunnei
055194d2ab core: arm: Remove unnecessary JIT checks. 2021-01-28 21:42:26 -08:00
bunnei
091e9e8c41 common: common_funcs: Log error on R_UNLESS. 2021-01-28 21:42:26 -08:00
bunnei
6e953f7f02 hle: kernel: Allocate a dummy KThread for each host thread, and use it for scheduling. 2021-01-28 21:42:26 -08:00
bunnei
37f74d8741 hle: kernel: k_scheduler: Use atomics for current_thread, etc. 2021-01-28 21:42:26 -08:00
bunnei
f6b10fad63 hle: kernel: k_scheduler: Fix for single core mode. 2021-01-28 21:42:26 -08:00
bunnei
0a1449e04b kernel: Fix build errors. 2021-01-28 21:42:26 -08:00
bunnei
89a5ae92bd core: cpu_manager: Remove unused variable. 2021-01-28 21:42:26 -08:00
bunnei
ca78f77827 hle: kernel: KScheduler: Introduce thread context_guard. 2021-01-28 21:42:26 -08:00
bunnei
cdd14b03e5 hle: kernel: Recode implementation of KThread to be more accurate. 2021-01-28 21:42:26 -08:00
bunnei
1470338458 kernel: svc_types: Add ThreadActivity. 2021-01-28 21:42:26 -08:00
bunnei
1772ebeb1e kernel: KSchedulerPriorityQueue: Lowest priority should be LowestThreadPriority. 2021-01-28 21:42:26 -08:00
bunnei
1f99f5473c kernel: k_light_lock: Simplify EmuThreadHandle implementation. 2021-01-28 21:42:26 -08:00
bunnei
c0f5830323 hle: kernel: TimeManager: Simplify to not rely on previous EmuThreadHandle implementation. 2021-01-28 21:42:26 -08:00
bunnei
bb966d3e33 common: common_funcs: Add useful kernel macro R_SUCCEED_IF. 2021-01-28 21:42:26 -08:00
bunnei
ff186b2498 core: hle: kernel: object: Implement Finalize() virtual method. 2021-01-28 21:42:26 -08:00
bunnei
33b4930280 core: hle: kernel: svc_results: Populate with several missing error codes. 2021-01-28 21:42:26 -08:00
bunnei
5a4fc4a529 core: hle: kernel: Implement KLightLock. 2021-01-28 21:42:26 -08:00
bunnei
97129bc742 core: hle: kernel: Implement KThreadQueue. 2021-01-28 21:42:25 -08:00
bunnei
2fb77adb9f common: common_funcs: Add a few more useful macros for kernel code. 2021-01-28 21:42:25 -08:00
bunnei
4dbf3f4880 hle: kernel: KThread: Clean up thread priorities. 2021-01-28 21:42:25 -08:00
bunnei
1e55498110 hle: kernel: KThread: Reorganize thread priority defaults. 2021-01-28 21:42:25 -08:00
bunnei
0530292b97 hle: kernel: KThread: Fix ThreadType definition. 2021-01-28 21:42:25 -08:00
bunnei
4782985013 hle: kernel: Move single core "phantom mode" out of KThread.
- This is a workaround that does not belong in a kernel primitive.
2021-01-28 21:42:25 -08:00
bunnei
eea346ba8e hle: kernel: KThread: Remove thread types that do not exist. 2021-01-28 21:42:25 -08:00
bunnei
9a4e148f9e arm: arm_dynarmic: Skip calls when JIT is invalid.
- This can happen if called from an idle or suspension thread.
2021-01-28 21:42:25 -08:00
bunnei
c0d3aef28c core: hle: kernel: Rename Thread to KThread. 2021-01-28 21:42:25 -08:00
german
8ba0cac71c Fix user changing to 0 if valid 2021-01-28 21:02:25 -06:00
bunnei
df41e78205 Merge pull request #5837 from german77/socketstub
sockets: Stub GetSockOpt
2021-01-28 16:28:11 -08:00
german
7b8fa78c65 Avoid overwritting username 2021-01-28 16:58:53 -06:00
LC
c21ce728c2 Merge pull request #5831 from MerryMage/isb
dynarmic: Do not flush cache when executing an ISB instruction
2021-01-28 10:54:48 -05:00
LC
16818e952c Merge pull request #5836 from ReinUsesLisp/unaligned-constr-sched
vk_scheduler: Fix unaligned placement new expressions
2021-01-28 10:53:15 -05:00
LC
9f6290d207 Merge pull request #5840 from Morph1984/prepo-fix
prepo: Fix BufferDescriptorX invalid buffer index errors and add New variants of SaveReport
2021-01-28 10:52:44 -05:00
LC
0e125dfd43 Merge pull request #5835 from Morph1984/cleanup-sixaxis-fusion
hid: Cleanup SixAxisFusionParameters
2021-01-28 10:51:04 -05:00
Morph
4921ba05db hid: Add static_assert for Parameter size 2021-01-28 09:41:43 -05:00
Morph
ae6b3bdfbf prepo: Fix BufferDescriptorX invalid buffer errors and add "New" variants of SaveReport
The second input buffer could be optional when prepo/srepo is called, test for the availability of the second buffer prior to reading from it.
2021-01-28 01:32:24 -05:00
Morph
008afa5d59 hle_ipc: Add Can(Read, Write)Buffer
Allows us to test whether a buffer can be read from or written to memory
2021-01-28 01:32:24 -05:00
german
659b5f8088 Stub GetSockOpt 2021-01-27 23:18:20 -06:00
ReinUsesLisp
9e88ad8da9 vk_scheduler: Fix unaligned placement new expressions
We were accidentaly creating an object in an unaligned memory address.
Fix this by manually aligning the offset.
2021-01-27 22:28:22 -03:00
bunnei
45b13c3037 Merge pull request #5786 from ReinUsesLisp/glsl-cbuf
gl_shader_decompiler: Fix constant buffer size calculation
2021-01-27 15:27:53 -08:00
Rodrigo Locatti
ef6cc3aa1d vulkan_device: Blacklist Intel from float16 math (#5798)
Astral Chain crashes Intel's SPIR-V compiler when using fp16.
Disable this while the vendor works on a fix.
2021-01-27 13:31:32 -08:00
bunnei
28b822fe38 Merge pull request #5778 from ReinUsesLisp/shader-dir
renderer_opengl: Avoid precompiled cache and force NV GL cache directory
2021-01-27 11:34:21 -08:00
Morph
d4d39aa4c7 npad: Remove unused device handle parameter 2021-01-27 13:05:31 -05:00
bunnei
fb0fe3b8c3 Merge pull request #5812 from german77/StubSixaxisFusion
HID: Stub Set/Get/Reset SixaxisSensorFusionParameters
2021-01-26 15:58:04 -08:00
MerryMage
2350b76a91 externals: Update dynarmic to 0f27368f 2021-01-26 23:46:49 +00:00
bunnei
09b6f03592 Merge pull request #5810 from ogniK5377/stereo-vision
hle: Implement remaining services for Stereo Vision
2021-01-26 15:01:01 -08:00
bunnei
72c1cb85f1 Merge pull request #5824 from ogniK5377/IPsmSession
psm: IPsmSession
2021-01-26 11:11:29 -08:00
bunnei
64a5548454 Merge pull request #5774 from ogniK5377/mii-raw-random
mii: Fix BuildRandomStoreData & Cleanup raw_data
2021-01-25 16:41:20 -08:00
bunnei
81a037df9d Merge pull request #5771 from ogniK5377/lm-rework
lm: Recode LM service
2021-01-25 10:18:09 -08:00
Chloe Marcec
2c57f0fbd5 Omit system reference 2021-01-25 23:13:37 +11:00
Chloe Marcec
04e9486651 psm: IPsmSession
Used by homebrew menu
2021-01-25 21:37:51 +11:00
bunnei
2a2ee62cfd Merge pull request #5799 from ogniK5377/event-register-unregister
nvdrv: Unregister already registered events
2021-01-24 23:19:10 -08:00
bunnei
62766b1326 Merge pull request #5785 from ReinUsesLisp/buffer-dma
video_core/memory_manager: Flush destination buffer on CopyBlock
2021-01-24 22:57:00 -08:00
Rodrigo Locatti
5dc021d15b Merge pull request #5823 from ReinUsesLisp/revert-flags
Revert "Start of Integer flags implementation"
2021-01-25 03:35:48 -03:00
ReinUsesLisp
34c3ec2f8c Revert "Start of Integer flags implementation"
This reverts #4713. The implementation in that PR is not accurate.
It does not reflect the behavior seen in hardware.
2021-01-25 02:48:03 -03:00
bunnei
45e117b043 Merge pull request #5819 from ReinUsesLisp/cull-mode-cast
vk_graphics_pipeline: Fix narrowing conversion on MSVC
2021-01-24 16:58:38 -08:00
ReinUsesLisp
9dc4a80b17 vk_graphics_pipeline: Fix narrowing conversion on MSVC 2021-01-24 21:41:29 -03:00
LC
df0d8c45d2 Merge pull request #5807 from ReinUsesLisp/vc-warnings
video_core: Silence the remaining gcc warnings and enforce them
2021-01-24 17:36:43 -05:00
Rodrigo Locatti
b769b1be26 Merge pull request #5363 from ReinUsesLisp/vk-image-usage
vk_texture_cache: Support image store on sRGB images with VkImageViewUsageCreateInfo
2021-01-24 18:44:51 -03:00
bunnei
44c5ea3639 Merge pull request #5151 from comex/xx-vfs
vfs_real: When moving files or directories, don't assume file opening will succeed
2021-01-24 13:42:51 -08:00
ReinUsesLisp
6b00443bc1 vk_texture_cache: Support image store on sRGB images with VkImageViewUsageCreateInfo
Vulkan 1.0 didn't support creating sRGB image views on an ABGR8 VkImage
with storage usage bits. VK_KHR_maintenance2 addressed this allowing to
reduce the usage bits on a VkImageView.

To allow image store on non-sRGB image views when the VkImage is created
with sRGB, always create VkImages without sRGB and add the sRGB format
on the view.
2021-01-24 18:16:43 -03:00
LC
8959f3521f Merge pull request #5814 from ReinUsesLisp/remove-rdna-dynstate
vulkan_device: Lift VK_EXT_extended_dynamic_state blacklist on RDNA
2021-01-24 15:54:30 -05:00
ReinUsesLisp
6a0143400f vulkan_device: Lift VK_EXT_extended_dynamic_state blacklist on RDNA
It seems to be safe to use this on new drivers.
2021-01-24 20:21:11 -03:00
ReinUsesLisp
748551dafb cmake: Enforce -Warray-bounds and -Wmissing-field-initializers globally 2021-01-24 17:31:29 -03:00
bunnei
19c14589d3 Merge pull request #5796 from ReinUsesLisp/vertex-a-bypass-vk
vk_pipeline_cache: Properly bypass VertexA shaders
2021-01-24 11:22:58 -08:00
german
a8245cf2f1 Stub Set/Get/Reset SixaxisSensorFusionParameters 2021-01-24 11:28:52 -06:00
Chloe Marcec
2afc1060ef Print Process ID and Thread ID as hex 2021-01-25 02:47:40 +11:00
Chloe Marcec
5882cc0502 hle: Implement remaining services for Stereo Vision
Used by Zelda Breath of the Wild, Super Mario Odyssey and Nintendo Labo
2021-01-25 00:34:01 +11:00
LC
04dcada85f Merge pull request #5808 from ReinUsesLisp/glslang-quiet
host_shaders/cmake: Pass --quiet to glslang to keep it quiet
2021-01-24 05:01:10 -05:00
Chloe Marcec
b2b95e96c1 audout: FlushAudioOutBuffers
Fixes Devil May Cry
2021-01-24 19:13:34 +11:00
ReinUsesLisp
f81c783b5b host_shaders/cmake: Pass --quiet to glslang to keep it quiet
Silences noisy builds on toolchains.
2021-01-24 04:55:23 -03:00
ReinUsesLisp
cc4335a9c6 video_core/cmake: Enforce -Warray-bounds and -Wmissing-field-initializers 2021-01-24 04:42:41 -03:00
bunnei
f7ac4e1eb4 Merge pull request #5806 from bunnei/am-stub
hle: service: am: Stub ILibraryAppletAccessor::PresetLibraryAppletGpuTimeSliceZero.
2021-01-23 23:37:05 -08:00
ReinUsesLisp
1b76e7e890 video_core: Silence -Wmissing-field-initializers warnings 2021-01-24 04:32:19 -03:00
ReinUsesLisp
80a673a27f maxwell_3d: Silence array bounds warnings 2021-01-24 04:31:41 -03:00
ReinUsesLisp
ad48259d7e maxwell_to_vk: Silence -Wextra warnings about using different enum types 2021-01-24 04:03:36 -03:00
german
3b4da2d7fa Fix connect and disconnect controller events 2021-01-23 22:59:44 -06:00
comex
e9bb95ae16 vfs_real: When moving files or directories, don't assume file opening will succeed
Found this via a warning, but it's a substantive fix.

Since this is only for a cache, it should be safe to silently drop the
entry if opening fails.  I think.
2021-01-23 16:19:29 -05:00
Levi Behunin
9477d23d70 shader_ir: Fix comment typo 2021-01-23 13:16:37 -05:00
LC
bfd2bcb068 Merge pull request #5800 from Morph1984/max-vibration-duration
sdl_impl: Set the maximum vibration duration to 1 second
2021-01-23 10:46:18 -05:00
Morph
5942d206c2 sdl_impl: Set the maximum vibration duration to 1 second 2021-01-23 08:06:07 -05:00
Chloe Marcec
822edff5bd Simplify condition 2021-01-23 22:12:05 +11:00
Chloe Marcec
3b0458a7a5 nvdrv: Unregister already registered events 2021-01-23 22:02:14 +11:00
LC
65f821850e Merge pull request #5797 from ReinUsesLisp/nsight-aftermath-build
nsight_aftermath_tracker: Fix build issues when enabled
2021-01-23 05:40:50 -05:00
Chloe Marcec
df42100320 Clamp string reads to buffer size 2021-01-23 18:24:57 +11:00
ReinUsesLisp
966896daad video_core/cmake: Properly generate fatal errors on Aftermath
Fix "message(ERROR ..." to "message(FATAL_ERROR ..." to properly stop
cmake when Nsight Aftermath can't be configured.
2021-01-23 04:15:30 -03:00
ReinUsesLisp
625a011888 nsight_aftermath_tracker: Fix build issues when enabled
Fixes a bunch of build errors when Nsight Aftermath is properly enabled.
2021-01-23 04:13:39 -03:00
bunnei
12355cbf02 Merge pull request #5776 from ogniK5377/lbl
lbl: Implement most of lbl
2021-01-22 23:13:23 -08:00
ReinUsesLisp
37ef2ee595 vk_pipeline_cache: Properly bypass VertexA shaders
The VertexA stage is not yet implemented, but Vulkan is adding its
descriptors, causing a discrepancy in the pushed descriptors and the
template. This generally ends up in a driver side crash.

Bypass the VertexA stage for now.
2021-01-23 03:59:59 -03:00
bunnei
302a5f00e8 Merge pull request #4713 from behunin/int-flags
Start of Integer flags implementation
2021-01-22 21:57:14 -08:00
bunnei
981d8e82d2 Merge pull request #5765 from ogniK5377/StoreSaveDataThumbnail-stub
acc: Stub StoreSaveDataThumbnail
2021-01-22 21:51:54 -08:00
ReinUsesLisp
bda177ef40 video_core/memory_manager: Add BytesToMapEnd
Track map address sizes in a flat ordered map and add a method to query
the number of bytes until the end of a map in a given address.
2021-01-22 18:31:12 -03:00
bunnei
a175ba1089 Merge pull request #5784 from v1993/patch-1
Bump conan SDL2 version to 2.0.14
2021-01-21 21:39:44 -08:00
bunnei
1e9b1d439f common: Add missing include to bit_util.h 2021-01-21 15:30:28 -08:00
ReinUsesLisp
436457b6e7 gl_shader_decompiler: Fix constant buffer size calculation
The divide logic was wrong and can cause an uniform buffer size
overflow.
2021-01-21 19:47:41 -03:00
bunnei
2c4c7aea8a Merge pull request #5781 from lioncash/bits
bit_util: Unify implementations of MostSignificantBit32/MostSignificantBit64
2021-01-21 14:45:40 -08:00
ReinUsesLisp
b7febb5625 video_core/memory_manager: Remove unused CopyBlockUnsafe
This function was not being used.
2021-01-21 19:16:06 -03:00
ReinUsesLisp
0e9a6759f9 video_core/memory_manager: Flush destination buffer on CopyBlock
When we copy into a buffer, it might contain data modified from the GPU
on the same pages. Because of this, we have to flush the contents before
writing new data.

An alternative approach would be to write the data in place, but games
can also write data in other ways, invalidating our contents.

Fixes geometry in Zombie Panic in Wonderland DX.
2021-01-21 19:16:06 -03:00
ReinUsesLisp
dd790abab0 video_core/memory_manager: Add GPU address based flush method
Allow flushing rasterizer contents based on a GPU address.
2021-01-21 19:16:05 -03:00
Valeri
46dda01151 Bump conan SDL2 version to 2.0.14
Update conan package version used for building.

A couple of new joystick-related functions might pose interest to yuzu's input system. Some sort of LED management have been added, but it doesn't seem to support leds used for player number indication JoyCons/ProCons use.
2021-01-21 21:47:35 +03:00
Lioncash
6ff2db181f bit_util: Unify implementations of MostSignificantBit32/MostSignificantBit64
We can use the standardized CLZ facilities to perform this. This also
allows us to make utilizing functions constexpr and eliminate the
inclusion of an intrinsics header.
2021-01-21 04:07:58 -05:00
bunnei
a1335d3d51 Merge pull request #5270 from german77/multiTouch
HID: Add multitouch support
2021-01-20 22:39:01 -08:00
bunnei
ffbde909c8 Merge pull request #5361 from ReinUsesLisp/vk-shader-comment
vk_shader_decompiler: Show comments as OpUndef with a type
2021-01-20 21:33:42 -08:00
bunnei
f83ef80ebd hle: service: am: Stub ILibraryAppletAccessor::PresetLibraryAppletGpuTimeSliceZero.
- Used by Monster Hunter Rise demo.
2021-01-20 20:35:12 -08:00
ReinUsesLisp
51512d01d8 renderer_opengl: Avoid precompiled cache and force NV GL cache directory
Setting __GL_SHADER_DISK_CACHE_PATH we can force the cache directory to
be in yuzu's user directory to stop commonly distributed malware from
deleting our driver shader cache. And by setting
__GL_SHADER_DISK_CACHE_SKIP_CLEANUP we can have an unbounded shader
cache size.

This has only been implemented on Windows, mostly because previous tests
didn't seem to work on Linux.

Disable the precompiled cache on Nvidia's driver. There's no need to
hide information the driver already has in its own cache.
2021-01-21 00:41:03 -03:00
bunnei
d98b0f8f48 Merge pull request #5743 from german77/HandheldFix
Fix player 1 turning on handheld and not updating handheld settings
2021-01-20 17:05:44 -08:00
Chloe Marcec
c795207fb2 lbl: Implement most of lbl
Pretty basic service, only thing left to do is handle setting applying once set:sys is implemented
2021-01-21 00:46:03 +11:00
Chloe Marcec
5b8bc56e65 mii: Fix BuildRandomStoreData & Cleanup raw_data
Cleaned up mii raw data to reflect the underlying values instead of just a chunk of bytes.
Fixed BuildRandomStoreData not actually generating random miis properly. "values" should be a u32, not a u8.
2021-01-20 21:53:57 +11:00
Chloe Marcec
dc18a1261c Mark DestinationToString as static 2021-01-20 18:42:39 +11:00
Chloe Marcec
dca2e2c8f1 Mark LogPacketHeaderEntry hash as noexcept 2021-01-20 18:35:58 +11:00
Chloe Marcec
83f8c1a25e lm: Recode LM service
Rework the service to spit out to logs instead of a seperate file as well as fix any crashes caused by lm.
2021-01-20 18:25:15 +11:00
bunnei
4cd8b2f1f7 Merge pull request #5755 from FearlessTobi/port-5344
Port citra-emu/citra#5344: "game_list: Fix folder reordering"
2021-01-19 10:53:18 -08:00
Chloe Marcec
2d33b2c55a acc: Stub StoreSaveDataThumbnail
Fixes ACA NEOGEO METAL SLUG hanging on boot.
2021-01-19 20:56:18 +11:00
Rodrigo Locatti
2ef4591e58 Merge pull request #5746 from lioncash/sign-compare
texture_cache/util: Resolve -Wsign-compare warning
2021-01-18 03:49:58 -03:00
LC
f1b58f0cd9 Merge pull request #5754 from lat9nq/fix-disable-boxcat
configure_service: Only compile FormatEventStatusString when YUZU_ENABLE_BOXCAT is enabled
2021-01-17 23:52:47 -05:00
LC
dd0679d710 Merge pull request #5757 from Morph1984/npad-handheld
npad: Add check for HANDHELD_INDEX in UpdateControllerAt()
2021-01-17 23:51:30 -05:00
Morph
4a67a5b917 npad: Add check for HANDHELD_INDEX in UpdateControllerAt() 2021-01-17 22:36:17 -05:00
german
e7c1d7bf77 Always update configuration for handheld 2021-01-17 18:40:27 -06:00
FearlessTobi
bf9f737c60 game_list: Fix folder reordering
The bug(s) happened because we swapped the contents on values.game_dirs, but the pointer each item had to their respective game_dir wasn't updated. This made it so that the item had the wrong game_dir associated with it after a "move up" or "move down" operation. It can be observed by choosing "open directory location" after such operation.

Changed from raw pointer to an index because it's equivalent but a bit clearer, but the change is not essential.

Co-Authored-By: Vitor K <29167336+vitor-k@users.noreply.github.com>
2021-01-18 01:22:54 +01:00
lat9nq
fb796843df configure_service: Only compile FormatEventStatusString when YUZU_ENABLE_BOXCAT is enabled
The function is unused if YUZU_ENABLE_BOXCAT is disabled, causing a
-Wunused-funciton error when compiled.

Wrapping it with `#ifdef YUZU_ENABLE_BOXCAT` to prevent compiling the
function when the variable is disabled. Opting to not use [[maybe
unused]] in case the function is totally unused in the future.
2021-01-17 17:54:29 -05:00
german
0bd8cecc94 Fix player 1 default connected value 2021-01-17 12:55:28 -06:00
bunnei
e8401964b4 Merge pull request #5360 from ReinUsesLisp/enforce-memclass-access
core: Silence Wclass-memaccess warnings and enforce it
2021-01-17 00:55:10 -08:00
Rodrigo Locatti
132f2006af Merge pull request #5745 from lioncash/documentation
video_core: Resolve -Wdocumentation warnings
2021-01-17 05:37:17 -03:00
bunnei
e1ecf64701 Merge pull request #5744 from lioncash/header-guard
vulkan_debug_callback: Add missing header guard
2021-01-17 00:16:12 -08:00
Lioncash
5f4e7c77bd texture_cache/util: Resolve -Wsign-compare warning
Resolves a -Wsign-compare warning on Clang.
2021-01-17 02:47:48 -05:00
Lioncash
40acc2c079 video_core: Resolve -Wdocumentation warnings
Silences some -Wdocumentation warnings on Clang.
2021-01-17 02:44:21 -05:00
Lioncash
c61b973968 vulkan_debug_callback: Add missing header guard
Prevents inclusion issues from occurring.
2021-01-17 02:39:24 -05:00
Rodrigo Locatti
0e0fc07135 Merge pull request #5740 from lioncash/const-fn
input_interpreter: Mark two member functions as const
2021-01-16 20:02:02 -03:00
Rodrigo Locatti
fd873fd369 Merge pull request #5262 from ReinUsesLisp/buffer-base
buffer_cache/buffer_base: Add a range tracking buffer container and tests
2021-01-16 19:48:26 -03:00
Lioncash
ca9afa3293 input_interpreter: Mark two member functions as const
These aren't stateful functions, so we can make use of const.

While we're at, we can resolve some -Wdocumentation warnings.
2021-01-16 16:08:35 -05:00
bunnei
ff2b7cc0d3 Merge pull request #5366 from Morph1984/button-press
input_interpreter: Add method to check for a button press state
2021-01-16 13:00:54 -08:00
ReinUsesLisp
c3c7603076 vk_shader_decompiler: Show comments as OpUndef with a type
Silence the new validation layer error about SPIR-V not allowing OpUndef
on a OpTypeVoid, even when the SPIR-V spec doesn't say anything against
it.

They will be inserted as an undefined int to avoid SPIRV-Cross and
validation errors, but only when a debugging tool is attached.
2021-01-15 21:12:57 -03:00
ReinUsesLisp
5f517e3e16 core/cmake: Enforce Wclass-memaccess
Treat -Wclass-memaccess as an error.
2021-01-15 16:31:19 -03:00
ReinUsesLisp
f8650a9580 core: Silence Wclass-memaccess warnings
This requires making several types trivial and properly initialize
them whenever they are called.
2021-01-15 16:31:19 -03:00
german
b483f2d010 Always initialize keyboard input 2021-01-15 09:05:17 -06:00
german
8495e1bd83 Add mutitouch support for touch screens 2021-01-15 09:05:17 -06:00
german
d8df9a16bd Allow to return up to 16 touch inputs per engine 2021-01-15 09:05:17 -06:00
german
390ee10eef Allow all touch inputs at the same time and remove config options that are not longer necesary 2021-01-15 09:05:17 -06:00
german
d583e01f54 Add multitouch support 2021-01-15 09:03:39 -06:00
ReinUsesLisp
7bd603061c tests: Add unit tests for the GPU range tracking buffer container
Due to how error prone the container design is, this commit adds unit
tests for it.

Some tests taken from here are based on bugs from using this buffer
container in games, so if we ever break it in the future in a way that
might harm games, the tests should fail.
2021-01-13 04:31:40 -03:00
ReinUsesLisp
a4bfae1b55 buffer_cache/buffer_base: Add a range tracking buffer container
It keeps track of the modified CPU and GPU ranges on a CPU page
granularity, notifying the given rasterizer about state changes
in the tracking behavior of the buffer.

Use a small vector optimization to store buffers smaller than 256 KiB
locally instead of using free store memory allocations.
2021-01-13 04:14:58 -03:00
Levi
7a3c884e39 Merge remote-tracking branch 'upstream/master' into int-flags 2021-01-10 22:09:56 -07:00
Levi Behunin
bc69cc1511 More forgetting... duh 2020-09-24 22:12:13 -06:00
Levi Behunin
24c1bb3842 Forgot to apply suggestion here as well 2020-09-24 21:58:51 -06:00
Levi Behunin
a19dc3bf00 Address Comments 2020-09-24 21:52:23 -06:00
Levi Behunin
d53b79ff5c Start of Integer flags implementation 2020-09-24 16:40:06 -06:00
265 changed files with 12988 additions and 6572 deletions

View File

@@ -261,7 +261,7 @@ if(ENABLE_SDL2)
find_package(SDL2)
if (NOT SDL2_FOUND)
# otherwise add this to the list of libraries to install
list(APPEND CONAN_REQUIRED_LIBS "sdl2/2.0.12@bincrafters/stable")
list(APPEND CONAN_REQUIRED_LIBS "sdl2/2.0.14@bincrafters/stable")
endif()
endif()

View File

@@ -1,26 +1,5 @@
<RCC>
<qresource prefix="controller">
<file alias="dual_joycon">dual_joycon.png</file>
<file alias="dual_joycon_dark">dual_joycon_dark.png</file>
<file alias="dual_joycon_midnight">dual_joycon_midnight.png</file>
<file alias="handheld">handheld.png</file>
<file alias="handheld_dark">handheld_dark.png</file>
<file alias="handheld_midnight">handheld_midnight.png</file>
<file alias="pro_controller">pro_controller.png</file>
<file alias="pro_controller_dark">pro_controller_dark.png</file>
<file alias="pro_controller_midnight">pro_controller_midnight.png</file>
<file alias="single_joycon_left">single_joycon_left.png</file>
<file alias="single_joycon_left_dark">single_joycon_left_dark.png</file>
<file alias="single_joycon_left_midnight">single_joycon_left_midnight.png</file>
<file alias="single_joycon_right">single_joycon_right.png</file>
<file alias="single_joycon_right_dark">single_joycon_right_dark.png</file>
<file alias="single_joycon_right_midnight">single_joycon_right_midnight.png</file>
<file alias="single_joycon_left_vertical">single_joycon_left_vertical.png</file>
<file alias="single_joycon_left_vertical_dark">single_joycon_left_vertical_dark.png</file>
<file alias="single_joycon_left_vertical_midnight">single_joycon_left_vertical_midnight.png</file>
<file alias="single_joycon_right_vertical">single_joycon_right_vertical.png</file>
<file alias="single_joycon_right_vertical_dark">single_joycon_right_vertical_dark.png</file>
<file alias="single_joycon_right_vertical_midnight">single_joycon_right_vertical_midnight.png</file>
<file alias="applet_dual_joycon">applet_dual_joycon.png</file>
<file alias="applet_dual_joycon_dark">applet_dual_joycon_dark.png</file>
<file alias="applet_dual_joycon_midnight">applet_dual_joycon_midnight.png</file>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 36 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 35 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 34 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 36 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 34 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 35 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 25 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 25 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 23 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 27 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 27 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 26 KiB

View File

@@ -64,8 +64,10 @@ if (MSVC)
else()
add_compile_options(
-Wall
-Werror=array-bounds
-Werror=implicit-fallthrough
-Werror=missing-declarations
-Werror=missing-field-initializers
-Werror=reorder
-Werror=switch
-Werror=uninitialized

View File

@@ -383,11 +383,14 @@ void CommandGenerator::GenerateI3dl2ReverbEffectCommand(s32 mix_buffer_offset, E
const auto channel_count = params.channel_count;
for (s32 i = 0; i < channel_count; i++) {
// TODO(ogniK): Actually implement reverb
/*
if (params.input[i] != params.output[i]) {
const auto* input = GetMixBuffer(mix_buffer_offset + params.input[i]);
auto* output = GetMixBuffer(mix_buffer_offset + params.output[i]);
ApplyMix<1>(output, input, 32768, worker_params.sample_count);
}
}*/
auto* output = GetMixBuffer(mix_buffer_offset + params.output[i]);
std::memset(output, 0, worker_params.sample_count * sizeof(s32));
}
}

View File

@@ -51,6 +51,14 @@ void Stream::Stop() {
UNIMPLEMENTED();
}
bool Stream::Flush() {
const bool had_buffers = !queued_buffers.empty();
while (!queued_buffers.empty()) {
queued_buffers.pop();
}
return had_buffers;
}
void Stream::SetVolume(float volume) {
game_volume = volume;
}

View File

@@ -56,6 +56,9 @@ public:
/// Queues a buffer into the audio stream, returns true on success
bool QueueBuffer(BufferPtr&& buffer);
/// Flush audio buffers
bool Flush();
/// Returns true if the audio stream contains a buffer with the specified tag
[[nodiscard]] bool ContainsBuffer(Buffer::Tag tag) const;

View File

@@ -86,28 +86,28 @@ struct BehaviorFlags {
static_assert(sizeof(BehaviorFlags) == 0x4, "BehaviorFlags is an invalid size");
struct ADPCMContext {
u16 header{};
s16 yn1{};
s16 yn2{};
u16 header;
s16 yn1;
s16 yn2;
};
static_assert(sizeof(ADPCMContext) == 0x6, "ADPCMContext is an invalid size");
struct VoiceState {
s64 played_sample_count{};
s32 offset{};
s32 wave_buffer_index{};
std::array<bool, AudioCommon::MAX_WAVE_BUFFERS> is_wave_buffer_valid{};
s32 wave_buffer_consumed{};
std::array<s32, AudioCommon::MAX_SAMPLE_HISTORY> sample_history{};
s32 fraction{};
VAddr context_address{};
Codec::ADPCM_Coeff coeff{};
ADPCMContext context{};
std::array<s64, 2> biquad_filter_state{};
std::array<s32, AudioCommon::MAX_MIX_BUFFERS> previous_samples{};
u32 external_context_size{};
bool is_external_context_used{};
bool voice_dropped{};
s64 played_sample_count;
s32 offset;
s32 wave_buffer_index;
std::array<bool, AudioCommon::MAX_WAVE_BUFFERS> is_wave_buffer_valid;
s32 wave_buffer_consumed;
std::array<s32, AudioCommon::MAX_SAMPLE_HISTORY> sample_history;
s32 fraction;
VAddr context_address;
Codec::ADPCM_Coeff coeff;
ADPCMContext context;
std::array<s64, 2> biquad_filter_state;
std::array<s32, AudioCommon::MAX_MIX_BUFFERS> previous_samples;
u32 external_context_size;
bool is_external_context_used;
bool voice_dropped;
};
class VoiceChannelResource {

View File

@@ -138,6 +138,8 @@ add_library(common STATIC
microprofile.h
microprofileui.h
misc.cpp
nvidia_flags.cpp
nvidia_flags.h
page_table.cpp
page_table.h
param_package.cpp

View File

@@ -4,13 +4,10 @@
#pragma once
#include <bit>
#include <climits>
#include <cstddef>
#ifdef _MSC_VER
#include <intrin.h>
#endif
#include "common/common_types.h"
namespace Common {
@@ -21,48 +18,30 @@ template <typename T>
return sizeof(T) * CHAR_BIT;
}
#ifdef _MSC_VER
[[nodiscard]] inline u32 MostSignificantBit32(const u32 value) {
unsigned long result;
_BitScanReverse(&result, value);
return static_cast<u32>(result);
[[nodiscard]] constexpr u32 MostSignificantBit32(const u32 value) {
return 31U - static_cast<u32>(std::countl_zero(value));
}
[[nodiscard]] inline u32 MostSignificantBit64(const u64 value) {
unsigned long result;
_BitScanReverse64(&result, value);
return static_cast<u32>(result);
[[nodiscard]] constexpr u32 MostSignificantBit64(const u64 value) {
return 63U - static_cast<u32>(std::countl_zero(value));
}
#else
[[nodiscard]] inline u32 MostSignificantBit32(const u32 value) {
return 31U - static_cast<u32>(__builtin_clz(value));
}
[[nodiscard]] inline u32 MostSignificantBit64(const u64 value) {
return 63U - static_cast<u32>(__builtin_clzll(value));
}
#endif
[[nodiscard]] inline u32 Log2Floor32(const u32 value) {
[[nodiscard]] constexpr u32 Log2Floor32(const u32 value) {
return MostSignificantBit32(value);
}
[[nodiscard]] inline u32 Log2Ceil32(const u32 value) {
const u32 log2_f = Log2Floor32(value);
return log2_f + ((value ^ (1U << log2_f)) != 0U);
}
[[nodiscard]] inline u32 Log2Floor64(const u64 value) {
[[nodiscard]] constexpr u32 Log2Floor64(const u64 value) {
return MostSignificantBit64(value);
}
[[nodiscard]] inline u32 Log2Ceil64(const u64 value) {
const u64 log2_f = static_cast<u64>(Log2Floor64(value));
return static_cast<u32>(log2_f + ((value ^ (1ULL << log2_f)) != 0ULL));
[[nodiscard]] constexpr u32 Log2Ceil32(const u32 value) {
const u32 log2_f = Log2Floor32(value);
return log2_f + static_cast<u32>((value ^ (1U << log2_f)) != 0U);
}
[[nodiscard]] constexpr u32 Log2Ceil64(const u64 value) {
const u64 log2_f = Log2Floor64(value);
return static_cast<u32>(log2_f + static_cast<u64>((value ^ (1ULL << log2_f)) != 0ULL));
}
} // namespace Common

View File

@@ -97,10 +97,27 @@ __declspec(dllimport) void __stdcall DebugBreak(void);
#define R_UNLESS(expr, res) \
{ \
if (!(expr)) { \
if (res.IsError()) { \
LOG_ERROR(Kernel, "Failed with result: {}", res.raw); \
} \
return res; \
} \
}
#define R_SUCCEEDED(res) (res.IsSuccess())
/// Evaluates an expression that returns a result, and returns the result if it would fail.
#define R_TRY(res_expr) \
{ \
const auto _tmp_r_try_rc = (res_expr); \
if (_tmp_r_try_rc.IsError()) { \
return _tmp_r_try_rc; \
} \
}
/// Evaluates a boolean expression, and succeeds if that expression is true.
#define R_SUCCEED_IF(expr) R_UNLESS(!(expr), RESULT_SUCCESS)
namespace Common {
[[nodiscard]] constexpr u32 MakeMagic(char a, char b, char c, char d) {

View File

@@ -0,0 +1,27 @@
// Copyright 2021 yuzu Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include <filesystem>
#include <stdlib.h>
#include <fmt/format.h>
#include "common/file_util.h"
#include "common/nvidia_flags.h"
namespace Common {
void ConfigureNvidiaEnvironmentFlags() {
#ifdef _WIN32
const std::string shader_path = Common::FS::SanitizePath(
fmt::format("{}/nvidia/", Common::FS::GetUserPath(Common::FS::UserPath::ShaderDir)));
const std::string windows_path =
Common::FS::SanitizePath(shader_path, Common::FS::DirectorySeparator::BackwardSlash);
void(Common::FS::CreateFullPath(shader_path + '/'));
void(_putenv(fmt::format("__GL_SHADER_DISK_CACHE_PATH={}", windows_path).c_str()));
void(_putenv("__GL_SHADER_DISK_CACHE_SKIP_CLEANUP=1"));
#endif
}
} // namespace Common

10
src/common/nvidia_flags.h Normal file
View File

@@ -0,0 +1,10 @@
// Copyright 2021 yuzu Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
namespace Common {
/// Configure platform specific flags for Nvidia's driver
void ConfigureNvidiaEnvironmentFlags();
} // namespace Common

View File

@@ -19,15 +19,14 @@ namespace Common {
/// SPSC ring buffer
/// @tparam T Element type
/// @tparam capacity Number of slots in ring buffer
/// @tparam granularity Slot size in terms of number of elements
template <typename T, std::size_t capacity, std::size_t granularity = 1>
template <typename T, std::size_t capacity>
class RingBuffer {
/// A "slot" is made of `granularity` elements of `T`.
static constexpr std::size_t slot_size = granularity * sizeof(T);
/// A "slot" is made of a single `T`.
static constexpr std::size_t slot_size = sizeof(T);
// T must be safely memcpy-able and have a trivial default constructor.
static_assert(std::is_trivial_v<T>);
// Ensure capacity is sensible.
static_assert(capacity < std::numeric_limits<std::size_t>::max() / 2 / granularity);
static_assert(capacity < std::numeric_limits<std::size_t>::max() / 2);
static_assert((capacity & (capacity - 1)) == 0, "capacity must be a power of two");
// Ensure lock-free.
static_assert(std::atomic_size_t::is_always_lock_free);
@@ -47,7 +46,7 @@ public:
const std::size_t second_copy = push_count - first_copy;
const char* in = static_cast<const char*>(new_slots);
std::memcpy(m_data.data() + pos * granularity, in, first_copy * slot_size);
std::memcpy(m_data.data() + pos, in, first_copy * slot_size);
in += first_copy * slot_size;
std::memcpy(m_data.data(), in, second_copy * slot_size);
@@ -74,7 +73,7 @@ public:
const std::size_t second_copy = pop_count - first_copy;
char* out = static_cast<char*>(output);
std::memcpy(out, m_data.data() + pos * granularity, first_copy * slot_size);
std::memcpy(out, m_data.data() + pos, first_copy * slot_size);
out += first_copy * slot_size;
std::memcpy(out, m_data.data(), second_copy * slot_size);
@@ -84,9 +83,9 @@ public:
}
std::vector<T> Pop(std::size_t max_slots = ~std::size_t(0)) {
std::vector<T> out(std::min(max_slots, capacity) * granularity);
const std::size_t count = Pop(out.data(), out.size() / granularity);
out.resize(count * granularity);
std::vector<T> out(std::min(max_slots, capacity));
const std::size_t count = Pop(out.data(), out.size());
out.resize(count);
return out;
}
@@ -113,7 +112,7 @@ private:
alignas(128) std::atomic_size_t m_write_index{0};
#endif
std::array<T, granularity * capacity> m_data;
std::array<T, capacity> m_data;
};
} // namespace Common

View File

@@ -49,3 +49,9 @@ ScopeExitHelper<Func> ScopeExit(Func&& func) {
* \endcode
*/
#define SCOPE_EXIT(body) auto CONCAT2(scope_exit_helper_, __LINE__) = detail::ScopeExit([&]() body)
/**
* This macro is similar to SCOPE_EXIT, except the object is caller managed. This is intended to be
* used when the caller might want to cancel the ScopeExit.
*/
#define SCOPE_GUARD(body) detail::ScopeExit([&]() body)

View File

@@ -14,8 +14,8 @@ constexpr u128 INVALID_UUID{{0, 0}};
struct UUID {
// UUIDs which are 0 are considered invalid!
u128 uuid = INVALID_UUID;
constexpr UUID() = default;
u128 uuid;
UUID() = default;
constexpr explicit UUID(const u128& id) : uuid{id} {}
constexpr explicit UUID(const u64 lo, const u64 hi) : uuid{{lo, hi}} {}

View File

@@ -160,7 +160,16 @@ add_library(core STATIC
hle/kernel/k_affinity_mask.h
hle/kernel/k_condition_variable.cpp
hle/kernel/k_condition_variable.h
hle/kernel/k_event.cpp
hle/kernel/k_event.h
hle/kernel/k_light_condition_variable.h
hle/kernel/k_light_lock.cpp
hle/kernel/k_light_lock.h
hle/kernel/k_priority_queue.h
hle/kernel/k_readable_event.cpp
hle/kernel/k_readable_event.h
hle/kernel/k_resource_limit.cpp
hle/kernel/k_resource_limit.h
hle/kernel/k_scheduler.cpp
hle/kernel/k_scheduler.h
hle/kernel/k_scheduler_lock.h
@@ -168,6 +177,11 @@ add_library(core STATIC
hle/kernel/k_scoped_scheduler_lock_and_sleep.h
hle/kernel/k_synchronization_object.cpp
hle/kernel/k_synchronization_object.h
hle/kernel/k_thread.cpp
hle/kernel/k_thread.h
hle/kernel/k_thread_queue.h
hle/kernel/k_writable_event.cpp
hle/kernel/k_writable_event.h
hle/kernel/kernel.cpp
hle/kernel/kernel.h
hle/kernel/memory/address_space_info.cpp
@@ -196,10 +210,6 @@ add_library(core STATIC
hle/kernel/process.h
hle/kernel/process_capability.cpp
hle/kernel/process_capability.h
hle/kernel/readable_event.cpp
hle/kernel/readable_event.h
hle/kernel/resource_limit.cpp
hle/kernel/resource_limit.h
hle/kernel/server_port.cpp
hle/kernel/server_port.h
hle/kernel/server_session.cpp
@@ -216,14 +226,10 @@ add_library(core STATIC
hle/kernel/svc_results.h
hle/kernel/svc_types.h
hle/kernel/svc_wrap.h
hle/kernel/thread.cpp
hle/kernel/thread.h
hle/kernel/time_manager.cpp
hle/kernel/time_manager.h
hle/kernel/transfer_memory.cpp
hle/kernel/transfer_memory.h
hle/kernel/writable_event.cpp
hle/kernel/writable_event.h
hle/lock.cpp
hle/lock.h
hle/result.h
@@ -400,8 +406,6 @@ add_library(core STATIC
hle/service/ldr/ldr.h
hle/service/lm/lm.cpp
hle/service/lm/lm.h
hle/service/lm/manager.cpp
hle/service/lm/manager.h
hle/service/mig/mig.cpp
hle/service/mig/mig.h
hle/service/mii/manager.cpp
@@ -645,6 +649,7 @@ else()
-Werror=implicit-fallthrough
-Werror=sign-compare
$<$<CXX_COMPILER_ID:GNU>:-Werror=class-memaccess>
$<$<CXX_COMPILER_ID:GNU>:-Werror=unused-but-set-parameter>
$<$<CXX_COMPILER_ID:GNU>:-Werror=unused-but-set-variable>

View File

@@ -71,8 +71,9 @@ public:
}
void ExceptionRaised(u32 pc, Dynarmic::A32::Exception exception) override {
LOG_CRITICAL(Core_ARM, "ExceptionRaised(exception = {}, pc = {:08X}, code = {:08X})",
exception, pc, MemoryReadCode(pc));
LOG_CRITICAL(Core_ARM,
"ExceptionRaised(exception = {}, pc = {:08X}, code = {:08X}, thumb = {})",
exception, pc, MemoryReadCode(pc), parent.IsInThumbMode());
UNIMPLEMENTED();
}
@@ -255,6 +256,9 @@ void ARM_Dynarmic_32::ChangeProcessorID(std::size_t new_core_id) {
}
void ARM_Dynarmic_32::SaveContext(ThreadContext32& ctx) {
if (!jit) {
return;
}
Dynarmic::A32::Context context;
jit->SaveContext(context);
ctx.cpu_registers = context.Regs();
@@ -264,6 +268,9 @@ void ARM_Dynarmic_32::SaveContext(ThreadContext32& ctx) {
}
void ARM_Dynarmic_32::LoadContext(const ThreadContext32& ctx) {
if (!jit) {
return;
}
Dynarmic::A32::Context context;
context.Regs() = ctx.cpu_registers;
context.ExtRegs() = ctx.extension_registers;

View File

@@ -50,6 +50,10 @@ public:
u64 GetTPIDR_EL0() const override;
void ChangeProcessorID(std::size_t new_core_id) override;
bool IsInThumbMode() const {
return (GetPSTATE() & 0x20) != 0;
}
void SaveContext(ThreadContext32& ctx) override;
void SaveContext(ThreadContext64& ctx) override {}
void LoadContext(const ThreadContext32& ctx) override;

View File

@@ -294,6 +294,9 @@ void ARM_Dynarmic_64::ChangeProcessorID(std::size_t new_core_id) {
}
void ARM_Dynarmic_64::SaveContext(ThreadContext64& ctx) {
if (!jit) {
return;
}
ctx.cpu_registers = jit->GetRegisters();
ctx.sp = jit->GetSP();
ctx.pc = jit->GetPC();
@@ -305,6 +308,9 @@ void ARM_Dynarmic_64::SaveContext(ThreadContext64& ctx) {
}
void ARM_Dynarmic_64::LoadContext(const ThreadContext64& ctx) {
if (!jit) {
return;
}
jit->SetRegisters(ctx.cpu_registers);
jit->SetSP(ctx.sp);
jit->SetPC(ctx.pc);

View File

@@ -28,15 +28,14 @@
#include "core/hardware_interrupt_manager.h"
#include "core/hle/kernel/client_port.h"
#include "core/hle/kernel/k_scheduler.h"
#include "core/hle/kernel/k_thread.h"
#include "core/hle/kernel/kernel.h"
#include "core/hle/kernel/physical_core.h"
#include "core/hle/kernel/process.h"
#include "core/hle/kernel/thread.h"
#include "core/hle/service/am/applets/applets.h"
#include "core/hle/service/apm/controller.h"
#include "core/hle/service/filesystem/filesystem.h"
#include "core/hle/service/glue/manager.h"
#include "core/hle/service/lm/manager.h"
#include "core/hle/service/service.h"
#include "core/hle/service/sm/sm.h"
#include "core/hle/service/time/time_manager.h"
@@ -293,8 +292,6 @@ struct System::Impl {
perf_stats->GetMeanFrametime());
}
lm_manager.Flush();
is_powered_on = false;
exit_lock = false;
@@ -398,7 +395,6 @@ struct System::Impl {
/// Service State
Service::Glue::ARPManager arp_manager;
Service::LM::Manager lm_manager{reporter};
Service::Time::TimeManager time_manager;
/// Service manager
@@ -720,14 +716,6 @@ const Service::APM::Controller& System::GetAPMController() const {
return impl->apm_controller;
}
Service::LM::Manager& System::GetLogManager() {
return impl->lm_manager;
}
const Service::LM::Manager& System::GetLogManager() const {
return impl->lm_manager;
}
Service::Time::TimeManager& System::GetTimeManager() {
return impl->time_manager;
}

View File

@@ -62,10 +62,6 @@ namespace Glue {
class ARPManager;
}
namespace LM {
class Manager;
} // namespace LM
namespace SM {
class ServiceManager;
} // namespace SM
@@ -351,9 +347,6 @@ public:
[[nodiscard]] Service::APM::Controller& GetAPMController();
[[nodiscard]] const Service::APM::Controller& GetAPMController() const;
[[nodiscard]] Service::LM::Manager& GetLogManager();
[[nodiscard]] const Service::LM::Manager& GetLogManager() const;
[[nodiscard]] Service::Time::TimeManager& GetTimeManager();
[[nodiscard]] const Service::Time::TimeManager& GetTimeManager() const;

View File

@@ -11,9 +11,9 @@
#include "core/core_timing.h"
#include "core/cpu_manager.h"
#include "core/hle/kernel/k_scheduler.h"
#include "core/hle/kernel/k_thread.h"
#include "core/hle/kernel/kernel.h"
#include "core/hle/kernel/physical_core.h"
#include "core/hle/kernel/thread.h"
#include "video_core/gpu.h"
namespace Core {
@@ -147,7 +147,7 @@ void CpuManager::MultiCoreRunSuspendThread() {
while (true) {
auto core = kernel.GetCurrentHostThreadID();
auto& scheduler = *kernel.CurrentScheduler();
Kernel::Thread* current_thread = scheduler.GetCurrentThread();
Kernel::KThread* current_thread = scheduler.GetCurrentThread();
Common::Fiber::YieldTo(current_thread->GetHostContext(), core_data[core].host_context);
ASSERT(scheduler.ContextSwitchPending());
ASSERT(core == kernel.GetCurrentHostThreadID());
@@ -208,7 +208,6 @@ void CpuManager::SingleCoreRunGuestThread() {
void CpuManager::SingleCoreRunGuestLoop() {
auto& kernel = system.Kernel();
auto* thread = kernel.CurrentScheduler()->GetCurrentThread();
while (true) {
auto* physical_core = &kernel.CurrentPhysicalCore();
system.EnterDynarmicProfile();
@@ -217,9 +216,9 @@ void CpuManager::SingleCoreRunGuestLoop() {
physical_core = &kernel.CurrentPhysicalCore();
}
system.ExitDynarmicProfile();
thread->SetPhantomMode(true);
kernel.SetIsPhantomModeForSingleCore(true);
system.CoreTiming().Advance();
thread->SetPhantomMode(false);
kernel.SetIsPhantomModeForSingleCore(false);
physical_core->ArmInterface().ClearExclusiveState();
PreemptSingleCore();
auto& scheduler = kernel.Scheduler(current_core);
@@ -245,7 +244,7 @@ void CpuManager::SingleCoreRunSuspendThread() {
while (true) {
auto core = kernel.GetCurrentHostThreadID();
auto& scheduler = *kernel.CurrentScheduler();
Kernel::Thread* current_thread = scheduler.GetCurrentThread();
Kernel::KThread* current_thread = scheduler.GetCurrentThread();
Common::Fiber::YieldTo(current_thread->GetHostContext(), core_data[0].host_context);
ASSERT(scheduler.ContextSwitchPending());
ASSERT(core == kernel.GetCurrentHostThreadID());
@@ -255,22 +254,23 @@ void CpuManager::SingleCoreRunSuspendThread() {
void CpuManager::PreemptSingleCore(bool from_running_enviroment) {
{
auto& scheduler = system.Kernel().Scheduler(current_core);
Kernel::Thread* current_thread = scheduler.GetCurrentThread();
auto& kernel = system.Kernel();
auto& scheduler = kernel.Scheduler(current_core);
Kernel::KThread* current_thread = scheduler.GetCurrentThread();
if (idle_count >= 4 || from_running_enviroment) {
if (!from_running_enviroment) {
system.CoreTiming().Idle();
idle_count = 0;
}
current_thread->SetPhantomMode(true);
kernel.SetIsPhantomModeForSingleCore(true);
system.CoreTiming().Advance();
current_thread->SetPhantomMode(false);
kernel.SetIsPhantomModeForSingleCore(false);
}
current_core.store((current_core + 1) % Core::Hardware::NUM_CPU_CORES);
system.CoreTiming().ResetTicks();
scheduler.Unload(scheduler.GetCurrentThread());
auto& next_scheduler = system.Kernel().Scheduler(current_core);
auto& next_scheduler = kernel.Scheduler(current_core);
Common::Fiber::YieldTo(current_thread->GetHostContext(), next_scheduler.ControlContext());
}
@@ -278,8 +278,7 @@ void CpuManager::PreemptSingleCore(bool from_running_enviroment) {
{
auto& scheduler = system.Kernel().Scheduler(current_core);
scheduler.Reload(scheduler.GetCurrentThread());
auto* currrent_thread2 = scheduler.GetCurrentThread();
if (!currrent_thread2->IsIdleThread()) {
if (!scheduler.IsIdle()) {
idle_count = 0;
}
}

View File

@@ -568,6 +568,11 @@ KeyManager::KeyManager() {
// Initialize keys
const std::string hactool_keys_dir = Common::FS::GetHactoolConfigurationPath();
const std::string yuzu_keys_dir = Common::FS::GetUserPath(Common::FS::UserPath::KeysDir);
if (!Common::FS::Exists(yuzu_keys_dir)) {
Common::FS::CreateDir(yuzu_keys_dir);
}
if (Settings::values.use_dev_keys) {
dev_mode = true;
AttemptLoadKeyFile(yuzu_keys_dir, hactool_keys_dir, "dev.keys", false);

View File

@@ -58,7 +58,7 @@ struct SaveDataAttribute {
SaveDataType type;
SaveDataRank rank;
u16 index;
INSERT_PADDING_BYTES(4);
INSERT_PADDING_BYTES_NOINIT(4);
u64 zero_1;
u64 zero_2;
u64 zero_3;
@@ -72,7 +72,7 @@ struct SaveDataExtraData {
u64 owner_id;
s64 timestamp;
SaveDataFlags flags;
INSERT_PADDING_BYTES(4);
INSERT_PADDING_BYTES_NOINIT(4);
s64 available_size;
s64 journal_size;
s64 commit_id;

View File

@@ -133,8 +133,11 @@ VirtualFile RealVfsFilesystem::MoveFile(std::string_view old_path_, std::string_
}
cache.erase(old_path);
file->Open(new_path, "r+b");
cache.insert_or_assign(new_path, std::move(file));
if (file->Open(new_path, "r+b")) {
cache.insert_or_assign(new_path, std::move(file));
} else {
LOG_ERROR(Service_FS, "Failed to open path {} in order to re-cache it", new_path);
}
} else {
UNREACHABLE();
return nullptr;
@@ -214,9 +217,12 @@ VirtualDir RealVfsFilesystem::MoveDirectory(std::string_view old_path_,
}
auto file = cached.lock();
file->Open(file_new_path, "r+b");
cache.erase(file_old_path);
cache.insert_or_assign(std::move(file_new_path), std::move(file));
if (file->Open(file_new_path, "r+b")) {
cache.insert_or_assign(std::move(file_new_path), std::move(file));
} else {
LOG_ERROR(Service_FS, "Failed to open path {} in order to re-cache it", file_new_path);
}
}
return OpenDirectory(new_path, Mode::ReadWrite);

View File

@@ -21,21 +21,18 @@ public:
std::mutex mutex;
bool touch_pressed = false; ///< True if touchpad area is currently pressed, otherwise false
float touch_x = 0.0f; ///< Touchpad X-position
float touch_y = 0.0f; ///< Touchpad Y-position
Input::TouchStatus status;
private:
class Device : public Input::TouchDevice {
public:
explicit Device(std::weak_ptr<TouchState>&& touch_state) : touch_state(touch_state) {}
std::tuple<float, float, bool> GetStatus() const override {
Input::TouchStatus GetStatus() const override {
if (auto state = touch_state.lock()) {
std::lock_guard guard{state->mutex};
return std::make_tuple(state->touch_x, state->touch_y, state->touch_pressed);
return state->status;
}
return std::make_tuple(0.0f, 0.0f, false);
return {};
}
private:
@@ -79,36 +76,44 @@ std::tuple<unsigned, unsigned> EmuWindow::ClipToTouchScreen(unsigned new_x, unsi
return std::make_tuple(new_x, new_y);
}
void EmuWindow::TouchPressed(unsigned framebuffer_x, unsigned framebuffer_y) {
if (!IsWithinTouchscreen(framebuffer_layout, framebuffer_x, framebuffer_y))
void EmuWindow::TouchPressed(unsigned framebuffer_x, unsigned framebuffer_y, std::size_t id) {
if (!IsWithinTouchscreen(framebuffer_layout, framebuffer_x, framebuffer_y)) {
return;
}
if (id >= touch_state->status.size()) {
return;
}
std::lock_guard guard{touch_state->mutex};
touch_state->touch_x =
const float x =
static_cast<float>(framebuffer_x - framebuffer_layout.screen.left) /
static_cast<float>(framebuffer_layout.screen.right - framebuffer_layout.screen.left);
touch_state->touch_y =
const float y =
static_cast<float>(framebuffer_y - framebuffer_layout.screen.top) /
static_cast<float>(framebuffer_layout.screen.bottom - framebuffer_layout.screen.top);
touch_state->touch_pressed = true;
touch_state->status[id] = std::make_tuple(x, y, true);
}
void EmuWindow::TouchReleased() {
void EmuWindow::TouchReleased(std::size_t id) {
if (id >= touch_state->status.size()) {
return;
}
std::lock_guard guard{touch_state->mutex};
touch_state->touch_pressed = false;
touch_state->touch_x = 0;
touch_state->touch_y = 0;
touch_state->status[id] = std::make_tuple(0.0f, 0.0f, false);
}
void EmuWindow::TouchMoved(unsigned framebuffer_x, unsigned framebuffer_y) {
if (!touch_state->touch_pressed)
void EmuWindow::TouchMoved(unsigned framebuffer_x, unsigned framebuffer_y, std::size_t id) {
if (id >= touch_state->status.size()) {
return;
}
if (!std::get<2>(touch_state->status[id]))
return;
if (!IsWithinTouchscreen(framebuffer_layout, framebuffer_x, framebuffer_y))
std::tie(framebuffer_x, framebuffer_y) = ClipToTouchScreen(framebuffer_x, framebuffer_y);
TouchPressed(framebuffer_x, framebuffer_y);
TouchPressed(framebuffer_x, framebuffer_y, id);
}
void EmuWindow::UpdateCurrentFramebufferLayout(unsigned width, unsigned height) {

View File

@@ -117,18 +117,23 @@ public:
* Signal that a touch pressed event has occurred (e.g. mouse click pressed)
* @param framebuffer_x Framebuffer x-coordinate that was pressed
* @param framebuffer_y Framebuffer y-coordinate that was pressed
* @param id Touch event ID
*/
void TouchPressed(unsigned framebuffer_x, unsigned framebuffer_y);
void TouchPressed(unsigned framebuffer_x, unsigned framebuffer_y, std::size_t id);
/// Signal that a touch released event has occurred (e.g. mouse click released)
void TouchReleased();
/**
* Signal that a touch released event has occurred (e.g. mouse click released)
* @param id Touch event ID
*/
void TouchReleased(std::size_t id);
/**
* Signal that a touch movement event has occurred (e.g. mouse was moved over the emu window)
* @param framebuffer_x Framebuffer x-coordinate
* @param framebuffer_y Framebuffer y-coordinate
* @param id Touch event ID
*/
void TouchMoved(unsigned framebuffer_x, unsigned framebuffer_y);
void TouchMoved(unsigned framebuffer_x, unsigned framebuffer_y, std::size_t id);
/**
* Returns currently active configuration.

View File

@@ -21,6 +21,11 @@ enum class AnalogDirection : u8 {
UP,
DOWN,
};
struct AnalogProperties {
float deadzone;
float range;
float threshold;
};
/// An abstract class template for an input device (a button, an analog input, etc.).
template <typename StatusType>
@@ -30,6 +35,12 @@ public:
virtual StatusType GetStatus() const {
return {};
}
virtual StatusType GetRawStatus() const {
return GetStatus();
}
virtual AnalogProperties GetAnalogProperties() const {
return {};
}
virtual bool GetAnalogDirectionStatus([[maybe_unused]] AnalogDirection direction) const {
return {};
}
@@ -163,10 +174,11 @@ using MotionStatus = std::tuple<Common::Vec3<float>, Common::Vec3<float>, Common
using MotionDevice = InputDevice<MotionStatus>;
/**
* A touch status is an object that returns a tuple of two floats and a bool. The floats are
* x and y coordinates in the range 0.0 - 1.0, and the bool indicates whether it is pressed.
* A touch status is an object that returns an array of 16 tuple elements of two floats and a bool.
* The floats are x and y coordinates in the range 0.0 - 1.0, and the bool indicates whether it is
* pressed.
*/
using TouchStatus = std::tuple<float, float, bool>;
using TouchStatus = std::array<std::tuple<float, float, bool>, 16>;
/**
* A touch device is an input device that returns a touch status object

View File

@@ -100,12 +100,12 @@ public:
/**
* Checks whether any of the buttons in the parameter list is pressed once.
*
* @tparam HIDButton The buttons to check.
* @tparam T The buttons to check.
*
* @returns True when at least one of the buttons is pressed once.
*/
template <HIDButton... T>
[[nodiscard]] bool IsAnyButtonPressedOnce() {
[[nodiscard]] bool IsAnyButtonPressedOnce() const {
return (IsButtonPressedOnce(T) || ...);
}
@@ -121,12 +121,12 @@ public:
/**
* Checks whether any of the buttons in the parameter list is held down.
*
* @tparam HIDButton The buttons to check.
* @tparam T The buttons to check.
*
* @returns True when at least one of the buttons is held down.
*/
template <HIDButton... T>
[[nodiscard]] bool IsAnyButtonHeld() {
[[nodiscard]] bool IsAnyButtonHeld() const {
return (IsButtonHeld(T) || ...);
}

View File

@@ -4,8 +4,10 @@
#pragma once
#include <array>
#include <tuple>
#include "common/bit_util.h"
#include "common/common_types.h"
namespace Core {
@@ -18,34 +20,12 @@ constexpr u64 BASE_CLOCK_RATE = 1019215872; // Switch cpu frequency is 1020MHz u
constexpr u64 CNTFREQ = 19200000; // Switch's hardware clock speed
constexpr u32 NUM_CPU_CORES = 4; // Number of CPU Cores
} // namespace Hardware
constexpr u32 INVALID_HOST_THREAD_ID = 0xFFFFFFFF;
struct EmuThreadHandle {
u32 host_handle;
u32 guest_handle;
u64 GetRaw() const {
return (static_cast<u64>(host_handle) << 32) | guest_handle;
}
bool operator==(const EmuThreadHandle& rhs) const {
return std::tie(host_handle, guest_handle) == std::tie(rhs.host_handle, rhs.guest_handle);
}
bool operator!=(const EmuThreadHandle& rhs) const {
return !operator==(rhs);
}
static constexpr EmuThreadHandle InvalidHandle() {
constexpr u32 invalid_handle = 0xFFFFFFFF;
return {invalid_handle, invalid_handle};
}
bool IsInvalid() const {
return (*this) == InvalidHandle();
}
// Virtual to Physical core map.
constexpr std::array<s32, Common::BitSize<u64>()> VirtualToPhysicalCoreMap{
0, 1, 2, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3,
};
} // namespace Hardware
} // namespace Core

View File

@@ -146,7 +146,7 @@ static_assert(sizeof(BufferDescriptorC) == 8, "BufferDescriptorC size is incorre
struct DataPayloadHeader {
u32_le magic;
INSERT_PADDING_WORDS(1);
INSERT_PADDING_WORDS_NOINIT(1);
};
static_assert(sizeof(DataPayloadHeader) == 8, "DataPayloadHeader size is incorrect");
@@ -174,7 +174,7 @@ struct DomainMessageHeader {
INSERT_PADDING_WORDS_NOINIT(2);
};
std::array<u32, 4> raw{};
std::array<u32, 4> raw;
};
};
static_assert(sizeof(DomainMessageHeader) == 16, "DomainMessageHeader size is incorrect");

View File

@@ -51,6 +51,8 @@ public:
*/
void ConnectionClosed();
void Finalize() override {}
private:
std::shared_ptr<ServerPort> server_port; ///< ServerPort associated with this client port.
u32 max_sessions = 0; ///< Maximum number of simultaneous sessions the port can have

View File

@@ -5,9 +5,9 @@
#include "core/hle/kernel/client_session.h"
#include "core/hle/kernel/errors.h"
#include "core/hle/kernel/hle_ipc.h"
#include "core/hle/kernel/k_thread.h"
#include "core/hle/kernel/server_session.h"
#include "core/hle/kernel/session.h"
#include "core/hle/kernel/thread.h"
#include "core/hle/result.h"
namespace Kernel {
@@ -38,7 +38,7 @@ ResultVal<std::shared_ptr<ClientSession>> ClientSession::Create(KernelCore& kern
return MakeResult(std::move(client_session));
}
ResultCode ClientSession::SendSyncRequest(std::shared_ptr<Thread> thread,
ResultCode ClientSession::SendSyncRequest(std::shared_ptr<KThread> thread,
Core::Memory::Memory& memory,
Core::Timing::CoreTiming& core_timing) {
// Keep ServerSession alive until we're done working with it.

View File

@@ -24,7 +24,7 @@ namespace Kernel {
class KernelCore;
class Session;
class Thread;
class KThread;
class ClientSession final : public KSynchronizationObject {
public:
@@ -46,11 +46,13 @@ public:
return HANDLE_TYPE;
}
ResultCode SendSyncRequest(std::shared_ptr<Thread> thread, Core::Memory::Memory& memory,
ResultCode SendSyncRequest(std::shared_ptr<KThread> thread, Core::Memory::Memory& memory,
Core::Timing::CoreTiming& core_timing);
bool IsSignaled() const override;
void Finalize() override {}
private:
static ResultVal<std::shared_ptr<ClientSession>> Create(KernelCore& kernel,
std::shared_ptr<Session> parent,

View File

@@ -17,12 +17,12 @@ GlobalSchedulerContext::GlobalSchedulerContext(KernelCore& kernel)
GlobalSchedulerContext::~GlobalSchedulerContext() = default;
void GlobalSchedulerContext::AddThread(std::shared_ptr<Thread> thread) {
void GlobalSchedulerContext::AddThread(std::shared_ptr<KThread> thread) {
std::scoped_lock lock{global_list_guard};
thread_list.push_back(std::move(thread));
}
void GlobalSchedulerContext::RemoveThread(std::shared_ptr<Thread> thread) {
void GlobalSchedulerContext::RemoveThread(std::shared_ptr<KThread> thread) {
std::scoped_lock lock{global_list_guard};
thread_list.erase(std::remove(thread_list.begin(), thread_list.end(), thread),
thread_list.end());

View File

@@ -12,7 +12,8 @@
#include "core/hardware_properties.h"
#include "core/hle/kernel/k_priority_queue.h"
#include "core/hle/kernel/k_scheduler_lock.h"
#include "core/hle/kernel/thread.h"
#include "core/hle/kernel/k_thread.h"
#include "core/hle/kernel/svc_types.h"
namespace Kernel {
@@ -20,8 +21,12 @@ class KernelCore;
class SchedulerLock;
using KSchedulerPriorityQueue =
KPriorityQueue<Thread, Core::Hardware::NUM_CPU_CORES, THREADPRIO_LOWEST, THREADPRIO_HIGHEST>;
constexpr s32 HighestCoreMigrationAllowedPriority = 2;
KPriorityQueue<KThread, Core::Hardware::NUM_CPU_CORES, Svc::LowestThreadPriority,
Svc::HighestThreadPriority>;
static constexpr s32 HighestCoreMigrationAllowedPriority = 2;
static_assert(Svc::LowestThreadPriority >= HighestCoreMigrationAllowedPriority);
static_assert(Svc::HighestThreadPriority <= HighestCoreMigrationAllowedPriority);
class GlobalSchedulerContext final {
friend class KScheduler;
@@ -33,13 +38,13 @@ public:
~GlobalSchedulerContext();
/// Adds a new thread to the scheduler
void AddThread(std::shared_ptr<Thread> thread);
void AddThread(std::shared_ptr<KThread> thread);
/// Removes a thread from the scheduler
void RemoveThread(std::shared_ptr<Thread> thread);
void RemoveThread(std::shared_ptr<KThread> thread);
/// Returns a list of all threads managed by the scheduler
[[nodiscard]] const std::vector<std::shared_ptr<Thread>>& GetThreadList() const {
[[nodiscard]] const std::vector<std::shared_ptr<KThread>>& GetThreadList() const {
return thread_list;
}
@@ -74,7 +79,7 @@ private:
LockType scheduler_lock;
/// Lists all thread ids that aren't deleted/etc.
std::vector<std::shared_ptr<Thread>> thread_list;
std::vector<std::shared_ptr<KThread>> thread_list;
Common::SpinLock global_list_guard{};
};

View File

@@ -9,9 +9,9 @@
#include "core/hle/kernel/errors.h"
#include "core/hle/kernel/handle_table.h"
#include "core/hle/kernel/k_scheduler.h"
#include "core/hle/kernel/k_thread.h"
#include "core/hle/kernel/kernel.h"
#include "core/hle/kernel/process.h"
#include "core/hle/kernel/thread.h"
namespace Kernel {
namespace {
@@ -89,6 +89,10 @@ ResultCode HandleTable::Close(Handle handle) {
const u16 slot = GetSlot(handle);
if (objects[slot].use_count() == 1) {
objects[slot]->Finalize();
}
objects[slot] = nullptr;
generations[slot] = next_free_slot;

View File

@@ -17,16 +17,16 @@
#include "core/hle/kernel/errors.h"
#include "core/hle/kernel/handle_table.h"
#include "core/hle/kernel/hle_ipc.h"
#include "core/hle/kernel/k_readable_event.h"
#include "core/hle/kernel/k_scheduler.h"
#include "core/hle/kernel/k_scoped_scheduler_lock_and_sleep.h"
#include "core/hle/kernel/k_thread.h"
#include "core/hle/kernel/k_writable_event.h"
#include "core/hle/kernel/kernel.h"
#include "core/hle/kernel/object.h"
#include "core/hle/kernel/process.h"
#include "core/hle/kernel/readable_event.h"
#include "core/hle/kernel/server_session.h"
#include "core/hle/kernel/thread.h"
#include "core/hle/kernel/time_manager.h"
#include "core/hle/kernel/writable_event.h"
#include "core/memory.h"
namespace Kernel {
@@ -48,7 +48,7 @@ void SessionRequestHandler::ClientDisconnected(
HLERequestContext::HLERequestContext(KernelCore& kernel, Core::Memory::Memory& memory,
std::shared_ptr<ServerSession> server_session,
std::shared_ptr<Thread> thread)
std::shared_ptr<KThread> thread)
: server_session(std::move(server_session)),
thread(std::move(thread)), kernel{kernel}, memory{memory} {
cmd_buf[0] = 0;
@@ -182,7 +182,7 @@ ResultCode HLERequestContext::PopulateFromIncomingCommandBuffer(const HandleTabl
return RESULT_SUCCESS;
}
ResultCode HLERequestContext::WriteToOutgoingCommandBuffer(Thread& thread) {
ResultCode HLERequestContext::WriteToOutgoingCommandBuffer(KThread& thread) {
auto& owner_process = *thread.GetOwnerProcess();
auto& handle_table = owner_process.GetHandleTable();
@@ -338,6 +338,28 @@ std::size_t HLERequestContext::GetWriteBufferSize(std::size_t buffer_index) cons
return 0;
}
bool HLERequestContext::CanReadBuffer(std::size_t buffer_index) const {
const bool is_buffer_a{BufferDescriptorA().size() > buffer_index &&
BufferDescriptorA()[buffer_index].Size()};
if (is_buffer_a) {
return BufferDescriptorA().size() > buffer_index;
} else {
return BufferDescriptorX().size() > buffer_index;
}
}
bool HLERequestContext::CanWriteBuffer(std::size_t buffer_index) const {
const bool is_buffer_b{BufferDescriptorB().size() > buffer_index &&
BufferDescriptorB()[buffer_index].Size()};
if (is_buffer_b) {
return BufferDescriptorB().size() > buffer_index;
} else {
return BufferDescriptorC().size() > buffer_index;
}
}
std::string HLERequestContext::Description() const {
if (!command_header) {
return "No command header available";

View File

@@ -40,9 +40,9 @@ class HLERequestContext;
class KernelCore;
class Process;
class ServerSession;
class Thread;
class ReadableEvent;
class WritableEvent;
class KThread;
class KReadableEvent;
class KWritableEvent;
enum class ThreadWakeupReason;
@@ -110,7 +110,7 @@ class HLERequestContext {
public:
explicit HLERequestContext(KernelCore& kernel, Core::Memory::Memory& memory,
std::shared_ptr<ServerSession> session,
std::shared_ptr<Thread> thread);
std::shared_ptr<KThread> thread);
~HLERequestContext();
/// Returns a pointer to the IPC command buffer for this request.
@@ -126,15 +126,12 @@ public:
return server_session;
}
using WakeupCallback = std::function<void(
std::shared_ptr<Thread> thread, HLERequestContext& context, ThreadWakeupReason reason)>;
/// Populates this context with data from the requesting process/thread.
ResultCode PopulateFromIncomingCommandBuffer(const HandleTable& handle_table,
u32_le* src_cmdbuf);
/// Writes data from this context back to the requesting process/thread.
ResultCode WriteToOutgoingCommandBuffer(Thread& thread);
ResultCode WriteToOutgoingCommandBuffer(KThread& thread);
u32_le GetCommand() const {
return command;
@@ -207,6 +204,12 @@ public:
/// Helper function to get the size of the output buffer
std::size_t GetWriteBufferSize(std::size_t buffer_index = 0) const;
/// Helper function to test whether the input buffer at buffer_index can be read
bool CanReadBuffer(std::size_t buffer_index = 0) const;
/// Helper function to test whether the output buffer at buffer_index can be written
bool CanWriteBuffer(std::size_t buffer_index = 0) const;
template <typename T>
std::shared_ptr<T> GetCopyObject(std::size_t index) {
return DynamicObjectCast<T>(copy_objects.at(index));
@@ -261,11 +264,11 @@ public:
std::string Description() const;
Thread& GetThread() {
KThread& GetThread() {
return *thread;
}
const Thread& GetThread() const {
const KThread& GetThread() const {
return *thread;
}
@@ -280,7 +283,7 @@ private:
std::array<u32, IPC::COMMAND_BUFFER_LENGTH> cmd_buf;
std::shared_ptr<Kernel::ServerSession> server_session;
std::shared_ptr<Thread> thread;
std::shared_ptr<KThread> thread;
// TODO(yuriks): Check common usage of this and optimize size accordingly
boost::container::small_vector<std::shared_ptr<Object>, 8> move_objects;
boost::container::small_vector<std::shared_ptr<Object>, 8> copy_objects;

View File

@@ -7,9 +7,9 @@
#include "core/hle/kernel/k_address_arbiter.h"
#include "core/hle/kernel/k_scheduler.h"
#include "core/hle/kernel/k_scoped_scheduler_lock_and_sleep.h"
#include "core/hle/kernel/k_thread.h"
#include "core/hle/kernel/kernel.h"
#include "core/hle/kernel/svc_results.h"
#include "core/hle/kernel/thread.h"
#include "core/hle/kernel/time_manager.h"
#include "core/memory.h"
@@ -96,7 +96,7 @@ ResultCode KAddressArbiter::Signal(VAddr addr, s32 count) {
auto it = thread_tree.nfind_light({addr, -1});
while ((it != thread_tree.end()) && (count <= 0 || num_waiters < count) &&
(it->GetAddressArbiterKey() == addr)) {
Thread* target_thread = std::addressof(*it);
KThread* target_thread = std::addressof(*it);
target_thread->SetSyncedObject(nullptr, RESULT_SUCCESS);
ASSERT(target_thread->IsWaitingForAddressArbiter());
@@ -118,14 +118,18 @@ ResultCode KAddressArbiter::SignalAndIncrementIfEqual(VAddr addr, s32 value, s32
// Check the userspace value.
s32 user_value{};
R_UNLESS(UpdateIfEqual(system, std::addressof(user_value), addr, value, value + 1),
Svc::ResultInvalidCurrentMemory);
R_UNLESS(user_value == value, Svc::ResultInvalidState);
if (!UpdateIfEqual(system, &user_value, addr, value, value + 1)) {
LOG_ERROR(Kernel, "Invalid current memory!");
return Svc::ResultInvalidCurrentMemory;
}
if (user_value != value) {
return Svc::ResultInvalidState;
}
auto it = thread_tree.nfind_light({addr, -1});
while ((it != thread_tree.end()) && (count <= 0 || num_waiters < count) &&
(it->GetAddressArbiterKey() == addr)) {
Thread* target_thread = std::addressof(*it);
KThread* target_thread = std::addressof(*it);
target_thread->SetSyncedObject(nullptr, RESULT_SUCCESS);
ASSERT(target_thread->IsWaitingForAddressArbiter());
@@ -143,61 +147,34 @@ ResultCode KAddressArbiter::SignalAndModifyByWaitingCountIfEqual(VAddr addr, s32
// Perform signaling.
s32 num_waiters{};
{
KScopedSchedulerLock sl(kernel);
[[maybe_unused]] const KScopedSchedulerLock sl(kernel);
auto it = thread_tree.nfind_light({addr, -1});
// Determine the updated value.
s32 new_value{};
if (/*GetTargetFirmware() >= TargetFirmware_7_0_0*/ true) {
if (count <= 0) {
if ((it != thread_tree.end()) && (it->GetAddressArbiterKey() == addr)) {
new_value = value - 2;
} else {
new_value = value + 1;
}
if (count <= 0) {
if (it != thread_tree.end() && it->GetAddressArbiterKey() == addr) {
new_value = value - 2;
} else {
if ((it != thread_tree.end()) && (it->GetAddressArbiterKey() == addr)) {
auto tmp_it = it;
s32 tmp_num_waiters{};
while ((++tmp_it != thread_tree.end()) &&
(tmp_it->GetAddressArbiterKey() == addr)) {
if ((tmp_num_waiters++) >= count) {
break;
}
}
if (tmp_num_waiters < count) {
new_value = value - 1;
} else {
new_value = value;
}
} else {
new_value = value + 1;
}
new_value = value + 1;
}
} else {
if (count <= 0) {
if ((it != thread_tree.end()) && (it->GetAddressArbiterKey() == addr)) {
new_value = value - 1;
} else {
new_value = value + 1;
}
} else {
if (it != thread_tree.end() && it->GetAddressArbiterKey() == addr) {
auto tmp_it = it;
s32 tmp_num_waiters{};
while ((tmp_it != thread_tree.end()) && (tmp_it->GetAddressArbiterKey() == addr) &&
(tmp_num_waiters < count + 1)) {
++tmp_num_waiters;
++tmp_it;
while (++tmp_it != thread_tree.end() && tmp_it->GetAddressArbiterKey() == addr) {
if (tmp_num_waiters++ >= count) {
break;
}
}
if (tmp_num_waiters == 0) {
new_value = value + 1;
} else if (tmp_num_waiters <= count) {
if (tmp_num_waiters < count) {
new_value = value - 1;
} else {
new_value = value;
}
} else {
new_value = value + 1;
}
}
@@ -205,17 +182,22 @@ ResultCode KAddressArbiter::SignalAndModifyByWaitingCountIfEqual(VAddr addr, s32
s32 user_value{};
bool succeeded{};
if (value != new_value) {
succeeded = UpdateIfEqual(system, std::addressof(user_value), addr, value, new_value);
succeeded = UpdateIfEqual(system, &user_value, addr, value, new_value);
} else {
succeeded = ReadFromUser(system, std::addressof(user_value), addr);
succeeded = ReadFromUser(system, &user_value, addr);
}
R_UNLESS(succeeded, Svc::ResultInvalidCurrentMemory);
R_UNLESS(user_value == value, Svc::ResultInvalidState);
if (!succeeded) {
LOG_ERROR(Kernel, "Invalid current memory!");
return Svc::ResultInvalidCurrentMemory;
}
if (user_value != value) {
return Svc::ResultInvalidState;
}
while ((it != thread_tree.end()) && (count <= 0 || num_waiters < count) &&
(it->GetAddressArbiterKey() == addr)) {
Thread* target_thread = std::addressof(*it);
KThread* target_thread = std::addressof(*it);
target_thread->SetSyncedObject(nullptr, RESULT_SUCCESS);
ASSERT(target_thread->IsWaitingForAddressArbiter());
@@ -231,11 +213,10 @@ ResultCode KAddressArbiter::SignalAndModifyByWaitingCountIfEqual(VAddr addr, s32
ResultCode KAddressArbiter::WaitIfLessThan(VAddr addr, s32 value, bool decrement, s64 timeout) {
// Prepare to wait.
Thread* cur_thread = kernel.CurrentScheduler()->GetCurrentThread();
Handle timer = InvalidHandle;
KThread* cur_thread = kernel.CurrentScheduler()->GetCurrentThread();
{
KScopedSchedulerLockAndSleep slp(kernel, timer, cur_thread, timeout);
KScopedSchedulerLockAndSleep slp{kernel, cur_thread, timeout};
// Check that the thread isn't terminating.
if (cur_thread->IsTerminationRequested()) {
@@ -250,9 +231,9 @@ ResultCode KAddressArbiter::WaitIfLessThan(VAddr addr, s32 value, bool decrement
s32 user_value{};
bool succeeded{};
if (decrement) {
succeeded = DecrementIfLessThan(system, std::addressof(user_value), addr, value);
succeeded = DecrementIfLessThan(system, &user_value, addr, value);
} else {
succeeded = ReadFromUser(system, std::addressof(user_value), addr);
succeeded = ReadFromUser(system, &user_value, addr);
}
if (!succeeded) {
@@ -273,17 +254,14 @@ ResultCode KAddressArbiter::WaitIfLessThan(VAddr addr, s32 value, bool decrement
}
// Set the arbiter.
cur_thread->SetAddressArbiter(std::addressof(thread_tree), addr);
cur_thread->SetAddressArbiter(&thread_tree, addr);
thread_tree.insert(*cur_thread);
cur_thread->SetState(ThreadState::Waiting);
cur_thread->SetWaitReasonForDebugging(ThreadWaitReasonForDebugging::Arbitration);
}
// Cancel the timer wait.
if (timer != InvalidHandle) {
auto& time_manager = kernel.TimeManager();
time_manager.UnscheduleTimeEvent(timer);
}
kernel.TimeManager().UnscheduleTimeEvent(cur_thread);
// Remove from the address arbiter.
{
@@ -297,16 +275,15 @@ ResultCode KAddressArbiter::WaitIfLessThan(VAddr addr, s32 value, bool decrement
// Get the result.
KSynchronizationObject* dummy{};
return cur_thread->GetWaitResult(std::addressof(dummy));
return cur_thread->GetWaitResult(&dummy);
}
ResultCode KAddressArbiter::WaitIfEqual(VAddr addr, s32 value, s64 timeout) {
// Prepare to wait.
Thread* cur_thread = kernel.CurrentScheduler()->GetCurrentThread();
Handle timer = InvalidHandle;
KThread* cur_thread = kernel.CurrentScheduler()->GetCurrentThread();
{
KScopedSchedulerLockAndSleep slp(kernel, timer, cur_thread, timeout);
KScopedSchedulerLockAndSleep slp{kernel, cur_thread, timeout};
// Check that the thread isn't terminating.
if (cur_thread->IsTerminationRequested()) {
@@ -319,7 +296,7 @@ ResultCode KAddressArbiter::WaitIfEqual(VAddr addr, s32 value, s64 timeout) {
// Read the value from userspace.
s32 user_value{};
if (!ReadFromUser(system, std::addressof(user_value), addr)) {
if (!ReadFromUser(system, &user_value, addr)) {
slp.CancelSleep();
return Svc::ResultInvalidCurrentMemory;
}
@@ -337,17 +314,14 @@ ResultCode KAddressArbiter::WaitIfEqual(VAddr addr, s32 value, s64 timeout) {
}
// Set the arbiter.
cur_thread->SetAddressArbiter(std::addressof(thread_tree), addr);
cur_thread->SetAddressArbiter(&thread_tree, addr);
thread_tree.insert(*cur_thread);
cur_thread->SetState(ThreadState::Waiting);
cur_thread->SetWaitReasonForDebugging(ThreadWaitReasonForDebugging::Arbitration);
}
// Cancel the timer wait.
if (timer != InvalidHandle) {
auto& time_manager = kernel.TimeManager();
time_manager.UnscheduleTimeEvent(timer);
}
kernel.TimeManager().UnscheduleTimeEvent(cur_thread);
// Remove from the address arbiter.
{
@@ -361,7 +335,7 @@ ResultCode KAddressArbiter::WaitIfEqual(VAddr addr, s32 value, s64 timeout) {
// Get the result.
KSynchronizationObject* dummy{};
return cur_thread->GetWaitResult(std::addressof(dummy));
return cur_thread->GetWaitResult(&dummy);
}
} // namespace Kernel

View File

@@ -27,7 +27,7 @@ public:
}
[[nodiscard]] constexpr bool GetAffinity(s32 core) const {
return this->mask & GetCoreBit(core);
return (this->mask & GetCoreBit(core)) != 0;
}
constexpr void SetAffinity(s32 core, bool set) {

View File

@@ -10,11 +10,11 @@
#include "core/hle/kernel/k_scheduler.h"
#include "core/hle/kernel/k_scoped_scheduler_lock_and_sleep.h"
#include "core/hle/kernel/k_synchronization_object.h"
#include "core/hle/kernel/k_thread.h"
#include "core/hle/kernel/kernel.h"
#include "core/hle/kernel/process.h"
#include "core/hle/kernel/svc_common.h"
#include "core/hle/kernel/svc_results.h"
#include "core/hle/kernel/thread.h"
#include "core/memory.h"
namespace Kernel {
@@ -66,7 +66,7 @@ KConditionVariable::KConditionVariable(Core::System& system_)
KConditionVariable::~KConditionVariable() = default;
ResultCode KConditionVariable::SignalToAddress(VAddr addr) {
Thread* owner_thread = kernel.CurrentScheduler()->GetCurrentThread();
KThread* owner_thread = kernel.CurrentScheduler()->GetCurrentThread();
// Signal the address.
{
@@ -74,7 +74,7 @@ ResultCode KConditionVariable::SignalToAddress(VAddr addr) {
// Remove waiter thread.
s32 num_waiters{};
Thread* next_owner_thread =
KThread* next_owner_thread =
owner_thread->RemoveWaiterByKey(std::addressof(num_waiters), addr);
// Determine the next tag.
@@ -103,11 +103,11 @@ ResultCode KConditionVariable::SignalToAddress(VAddr addr) {
}
ResultCode KConditionVariable::WaitForAddress(Handle handle, VAddr addr, u32 value) {
Thread* cur_thread = kernel.CurrentScheduler()->GetCurrentThread();
KThread* cur_thread = kernel.CurrentScheduler()->GetCurrentThread();
// Wait for the address.
{
std::shared_ptr<Thread> owner_thread;
std::shared_ptr<KThread> owner_thread;
ASSERT(!owner_thread);
{
KScopedSchedulerLock sl(kernel);
@@ -126,7 +126,7 @@ ResultCode KConditionVariable::WaitForAddress(Handle handle, VAddr addr, u32 val
R_UNLESS(test_tag == (handle | Svc::HandleWaitMask), RESULT_SUCCESS);
// Get the lock owner thread.
owner_thread = kernel.CurrentProcess()->GetHandleTable().Get<Thread>(handle);
owner_thread = kernel.CurrentProcess()->GetHandleTable().Get<KThread>(handle);
R_UNLESS(owner_thread, Svc::ResultInvalidHandle);
// Update the lock.
@@ -143,7 +143,7 @@ ResultCode KConditionVariable::WaitForAddress(Handle handle, VAddr addr, u32 val
// Remove the thread as a waiter from the lock owner.
{
KScopedSchedulerLock sl(kernel);
Thread* owner_thread = cur_thread->GetLockOwner();
KThread* owner_thread = cur_thread->GetLockOwner();
if (owner_thread != nullptr) {
owner_thread->RemoveWaiter(cur_thread);
}
@@ -154,7 +154,7 @@ ResultCode KConditionVariable::WaitForAddress(Handle handle, VAddr addr, u32 val
return cur_thread->GetWaitResult(std::addressof(dummy));
}
Thread* KConditionVariable::SignalImpl(Thread* thread) {
KThread* KConditionVariable::SignalImpl(KThread* thread) {
// Check pre-conditions.
ASSERT(kernel.GlobalSchedulerContext().IsLocked());
@@ -174,7 +174,7 @@ Thread* KConditionVariable::SignalImpl(Thread* thread) {
}
}
Thread* thread_to_close = nullptr;
KThread* thread_to_close = nullptr;
if (can_access) {
if (prev_tag == InvalidHandle) {
// If nobody held the lock previously, we're all good.
@@ -182,7 +182,7 @@ Thread* KConditionVariable::SignalImpl(Thread* thread) {
thread->Wakeup();
} else {
// Get the previous owner.
auto owner_thread = kernel.CurrentProcess()->GetHandleTable().Get<Thread>(
auto owner_thread = kernel.CurrentProcess()->GetHandleTable().Get<KThread>(
prev_tag & ~Svc::HandleWaitMask);
if (owner_thread) {
@@ -210,8 +210,8 @@ void KConditionVariable::Signal(u64 cv_key, s32 count) {
// TODO(bunnei): This should just be Thread once we implement KAutoObject instead of using
// std::shared_ptr.
std::vector<std::shared_ptr<Thread>> thread_list;
std::array<Thread*, MaxThreads> thread_array;
std::vector<std::shared_ptr<KThread>> thread_list;
std::array<KThread*, MaxThreads> thread_array;
s32 num_to_close{};
// Perform signaling.
@@ -222,9 +222,9 @@ void KConditionVariable::Signal(u64 cv_key, s32 count) {
auto it = thread_tree.nfind_light({cv_key, -1});
while ((it != thread_tree.end()) && (count <= 0 || num_waiters < count) &&
(it->GetConditionVariableKey() == cv_key)) {
Thread* target_thread = std::addressof(*it);
KThread* target_thread = std::addressof(*it);
if (Thread* thread = SignalImpl(target_thread); thread != nullptr) {
if (KThread* thread = SignalImpl(target_thread); thread != nullptr) {
if (num_to_close < MaxThreads) {
thread_array[num_to_close++] = thread;
} else {
@@ -257,11 +257,10 @@ void KConditionVariable::Signal(u64 cv_key, s32 count) {
ResultCode KConditionVariable::Wait(VAddr addr, u64 key, u32 value, s64 timeout) {
// Prepare to wait.
Thread* cur_thread = kernel.CurrentScheduler()->GetCurrentThread();
Handle timer = InvalidHandle;
KThread* cur_thread = kernel.CurrentScheduler()->GetCurrentThread();
{
KScopedSchedulerLockAndSleep slp(kernel, timer, cur_thread, timeout);
KScopedSchedulerLockAndSleep slp{kernel, cur_thread, timeout};
// Set the synced object.
cur_thread->SetSyncedObject(nullptr, Svc::ResultTimedOut);
@@ -276,7 +275,7 @@ ResultCode KConditionVariable::Wait(VAddr addr, u64 key, u32 value, s64 timeout)
{
// Remove waiter thread.
s32 num_waiters{};
Thread* next_owner_thread =
KThread* next_owner_thread =
cur_thread->RemoveWaiterByKey(std::addressof(num_waiters), addr);
// Update for the next owner thread.
@@ -322,16 +321,13 @@ ResultCode KConditionVariable::Wait(VAddr addr, u64 key, u32 value, s64 timeout)
}
// Cancel the timer wait.
if (timer != InvalidHandle) {
auto& time_manager = kernel.TimeManager();
time_manager.UnscheduleTimeEvent(timer);
}
kernel.TimeManager().UnscheduleTimeEvent(cur_thread);
// Remove from the condition variable.
{
KScopedSchedulerLock sl(kernel);
if (Thread* owner = cur_thread->GetLockOwner(); owner != nullptr) {
if (KThread* owner = cur_thread->GetLockOwner(); owner != nullptr) {
owner->RemoveWaiter(cur_thread);
}

View File

@@ -8,8 +8,8 @@
#include "common/common_types.h"
#include "core/hle/kernel/k_scheduler.h"
#include "core/hle/kernel/k_thread.h"
#include "core/hle/kernel/kernel.h"
#include "core/hle/kernel/thread.h"
#include "core/hle/result.h"
namespace Core {
@@ -20,7 +20,7 @@ namespace Kernel {
class KConditionVariable {
public:
using ThreadTree = typename Thread::ConditionVariableThreadTreeType;
using ThreadTree = typename KThread::ConditionVariableThreadTreeType;
explicit KConditionVariable(Core::System& system_);
~KConditionVariable();
@@ -34,7 +34,7 @@ public:
[[nodiscard]] ResultCode Wait(VAddr addr, u64 key, u32 value, s64 timeout);
private:
[[nodiscard]] Thread* SignalImpl(Thread* thread);
[[nodiscard]] KThread* SignalImpl(KThread* thread);
ThreadTree thread_tree;
@@ -43,14 +43,14 @@ private:
};
inline void BeforeUpdatePriority(const KernelCore& kernel, KConditionVariable::ThreadTree* tree,
Thread* thread) {
KThread* thread) {
ASSERT(kernel.GlobalSchedulerContext().IsLocked());
tree->erase(tree->iterator_to(*thread));
}
inline void AfterUpdatePriority(const KernelCore& kernel, KConditionVariable::ThreadTree* tree,
Thread* thread) {
KThread* thread) {
ASSERT(kernel.GlobalSchedulerContext().IsLocked());
tree->insert(*thread);

View File

@@ -0,0 +1,32 @@
// Copyright 2021 yuzu emulator team
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include "core/hle/kernel/k_event.h"
#include "core/hle/kernel/k_readable_event.h"
#include "core/hle/kernel/k_writable_event.h"
namespace Kernel {
KEvent::KEvent(KernelCore& kernel, std::string&& name) : Object{kernel, std::move(name)} {}
KEvent::~KEvent() = default;
std::shared_ptr<KEvent> KEvent::Create(KernelCore& kernel, std::string&& name) {
return std::make_shared<KEvent>(kernel, std::move(name));
}
void KEvent::Initialize() {
// Create our sub events.
readable_event = std::make_shared<KReadableEvent>(kernel, GetName() + ":Readable");
writable_event = std::make_shared<KWritableEvent>(kernel, GetName() + ":Writable");
// Initialize our sub sessions.
readable_event->Initialize(this);
writable_event->Initialize(this);
// Mark initialized.
initialized = true;
}
} // namespace Kernel

View File

@@ -0,0 +1,57 @@
// Copyright 2021 yuzu emulator team
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include "core/hle/kernel/object.h"
namespace Kernel {
class KernelCore;
class KReadableEvent;
class KWritableEvent;
class KEvent final : public Object {
public:
explicit KEvent(KernelCore& kernel, std::string&& name);
~KEvent() override;
static std::shared_ptr<KEvent> Create(KernelCore& kernel, std::string&& name);
void Initialize();
void Finalize() override {}
std::string GetTypeName() const override {
return "KEvent";
}
static constexpr HandleType HANDLE_TYPE = HandleType::Event;
HandleType GetHandleType() const override {
return HANDLE_TYPE;
}
std::shared_ptr<KReadableEvent>& GetReadableEvent() {
return readable_event;
}
std::shared_ptr<KWritableEvent>& GetWritableEvent() {
return writable_event;
}
const std::shared_ptr<KReadableEvent>& GetReadableEvent() const {
return readable_event;
}
const std::shared_ptr<KWritableEvent>& GetWritableEvent() const {
return writable_event;
}
private:
std::shared_ptr<KReadableEvent> readable_event;
std::shared_ptr<KWritableEvent> writable_event;
bool initialized{};
};
} // namespace Kernel

View File

@@ -0,0 +1,57 @@
// Copyright 2020 yuzu Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
// This file references various implementation details from Atmosphere, an open-source firmware for
// the Nintendo Switch. Copyright 2018-2020 Atmosphere-NX.
#pragma once
#include "common/common_types.h"
#include "core/hle/kernel/k_scheduler.h"
#include "core/hle/kernel/k_scoped_scheduler_lock_and_sleep.h"
#include "core/hle/kernel/k_thread_queue.h"
#include "core/hle/kernel/time_manager.h"
namespace Kernel {
class KernelCore;
class KLightConditionVariable {
public:
explicit KLightConditionVariable(KernelCore& kernel) : thread_queue(kernel), kernel(kernel) {}
void Wait(KLightLock* lock, s64 timeout = -1) {
WaitImpl(lock, timeout);
lock->Lock();
}
void Broadcast() {
KScopedSchedulerLock lk{kernel};
while (thread_queue.WakeupFrontThread() != nullptr) {
// We want to signal all threads, and so should continue waking up until there's nothing
// to wake.
}
}
private:
void WaitImpl(KLightLock* lock, s64 timeout) {
KThread* owner = GetCurrentThreadPointer(kernel);
// Sleep the thread.
{
KScopedSchedulerLockAndSleep lk(kernel, owner, timeout);
lock->Unlock();
if (!thread_queue.SleepThread(owner)) {
lk.CancelSleep();
return;
}
}
// Cancel the task that the sleep setup.
kernel.TimeManager().UnscheduleTimeEvent(owner);
}
KThreadQueue thread_queue;
KernelCore& kernel;
};
} // namespace Kernel

View File

@@ -0,0 +1,130 @@
// Copyright 2021 yuzu Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include "core/hle/kernel/k_light_lock.h"
#include "core/hle/kernel/k_scheduler.h"
#include "core/hle/kernel/k_thread.h"
#include "core/hle/kernel/kernel.h"
namespace Kernel {
void KLightLock::Lock() {
const uintptr_t cur_thread = reinterpret_cast<uintptr_t>(GetCurrentThreadPointer(kernel));
const uintptr_t cur_thread_tag = (cur_thread | 1);
while (true) {
uintptr_t old_tag = tag.load(std::memory_order_relaxed);
while (!tag.compare_exchange_weak(old_tag, (old_tag == 0) ? cur_thread : old_tag | 1,
std::memory_order_acquire)) {
if ((old_tag | 1) == cur_thread_tag) {
return;
}
}
if ((old_tag == 0) || ((old_tag | 1) == cur_thread_tag)) {
break;
}
LockSlowPath(old_tag | 1, cur_thread);
}
}
void KLightLock::Unlock() {
const uintptr_t cur_thread = reinterpret_cast<uintptr_t>(GetCurrentThreadPointer(kernel));
uintptr_t expected = cur_thread;
do {
if (expected != cur_thread) {
return UnlockSlowPath(cur_thread);
}
} while (!tag.compare_exchange_weak(expected, 0, std::memory_order_release));
}
void KLightLock::LockSlowPath(uintptr_t _owner, uintptr_t _cur_thread) {
KThread* cur_thread = reinterpret_cast<KThread*>(_cur_thread);
// Pend the current thread waiting on the owner thread.
{
KScopedSchedulerLock sl{kernel};
// Ensure we actually have locking to do.
if (tag.load(std::memory_order_relaxed) != _owner) {
return;
}
// Add the current thread as a waiter on the owner.
KThread* owner_thread = reinterpret_cast<KThread*>(_owner & ~1ULL);
cur_thread->SetAddressKey(reinterpret_cast<uintptr_t>(std::addressof(tag)));
owner_thread->AddWaiter(cur_thread);
// Set thread states.
if (cur_thread->GetState() == ThreadState::Runnable) {
cur_thread->SetState(ThreadState::Waiting);
} else {
KScheduler::SetSchedulerUpdateNeeded(kernel);
}
if (owner_thread->IsSuspended()) {
owner_thread->ContinueIfHasKernelWaiters();
}
}
// We're no longer waiting on the lock owner.
{
KScopedSchedulerLock sl{kernel};
KThread* owner_thread = cur_thread->GetLockOwner();
if (owner_thread) {
owner_thread->RemoveWaiter(cur_thread);
KScheduler::SetSchedulerUpdateNeeded(kernel);
}
}
}
void KLightLock::UnlockSlowPath(uintptr_t _cur_thread) {
KThread* owner_thread = reinterpret_cast<KThread*>(_cur_thread);
// Unlock.
{
KScopedSchedulerLock sl{kernel};
// Get the next owner.
s32 num_waiters = 0;
KThread* next_owner = owner_thread->RemoveWaiterByKey(
std::addressof(num_waiters), reinterpret_cast<uintptr_t>(std::addressof(tag)));
// Pass the lock to the next owner.
uintptr_t next_tag = 0;
if (next_owner) {
next_tag = reinterpret_cast<uintptr_t>(next_owner);
if (num_waiters > 1) {
next_tag |= 0x1;
}
if (next_owner->GetState() == ThreadState::Waiting) {
next_owner->SetState(ThreadState::Runnable);
} else {
KScheduler::SetSchedulerUpdateNeeded(kernel);
}
if (next_owner->IsSuspended()) {
next_owner->ContinueIfHasKernelWaiters();
}
}
// We may have unsuspended in the process of acquiring the lock, so we'll re-suspend now if
// so.
if (owner_thread->IsSuspended()) {
owner_thread->TrySuspend();
}
// Write the new tag value.
tag.store(next_tag);
}
}
bool KLightLock::IsLockedByCurrentThread() const {
return (tag | 1ULL) == (reinterpret_cast<uintptr_t>(GetCurrentThreadPointer(kernel)) | 1ULL);
}
} // namespace Kernel

View File

@@ -0,0 +1,41 @@
// Copyright 2021 yuzu Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include <atomic>
#include "common/common_types.h"
#include "core/hle/kernel/k_scoped_lock.h"
namespace Kernel {
class KernelCore;
class KLightLock {
public:
explicit KLightLock(KernelCore& kernel_) : kernel{kernel_} {}
void Lock();
void Unlock();
void LockSlowPath(uintptr_t owner, uintptr_t cur_thread);
void UnlockSlowPath(uintptr_t cur_thread);
bool IsLocked() const {
return tag != 0;
}
bool IsLockedByCurrentThread() const;
private:
std::atomic<uintptr_t> tag{};
KernelCore& kernel;
};
using KScopedLightLock = KScopedLock<KLightLock>;
} // namespace Kernel

View File

@@ -18,17 +18,17 @@
namespace Kernel {
class Thread;
class KThread;
template <typename T>
concept KPriorityQueueAffinityMask = !std::is_reference_v<T> && requires(T & t) {
{ t.GetAffinityMask() }
->Common::ConvertibleTo<u64>;
{t.SetAffinityMask(std::declval<u64>())};
{t.SetAffinityMask(0)};
{ t.GetAffinity(std::declval<int32_t>()) }
{ t.GetAffinity(0) }
->std::same_as<bool>;
{t.SetAffinity(std::declval<int32_t>(), std::declval<bool>())};
{t.SetAffinity(0, false)};
{t.SetAll()};
};
@@ -42,11 +42,11 @@ concept KPriorityQueueMember = !std::is_reference_v<T> && requires(T & t) {
->std::same_as<T*>;
{ (typename T::QueueEntry()).GetPrev() }
->std::same_as<T*>;
{ t.GetPriorityQueueEntry(std::declval<s32>()) }
{ t.GetPriorityQueueEntry(0) }
->std::same_as<typename T::QueueEntry&>;
{t.GetAffinityMask()};
{ typename std::remove_cvref<decltype(t.GetAffinityMask())>::type() }
{ std::remove_cvref_t<decltype(t.GetAffinityMask())>() }
->KPriorityQueueAffinityMask;
{ t.GetActiveCore() }
@@ -55,17 +55,17 @@ concept KPriorityQueueMember = !std::is_reference_v<T> && requires(T & t) {
->Common::ConvertibleTo<s32>;
};
template <typename Member, size_t _NumCores, int LowestPriority, int HighestPriority>
template <typename Member, size_t NumCores_, int LowestPriority, int HighestPriority>
requires KPriorityQueueMember<Member> class KPriorityQueue {
public:
using AffinityMaskType = typename std::remove_cv_t<
typename std::remove_reference<decltype(std::declval<Member>().GetAffinityMask())>::type>;
using AffinityMaskType = std::remove_cv_t<
std::remove_reference_t<decltype(std::declval<Member>().GetAffinityMask())>>;
static_assert(LowestPriority >= 0);
static_assert(HighestPriority >= 0);
static_assert(LowestPriority >= HighestPriority);
static constexpr size_t NumPriority = LowestPriority - HighestPriority + 1;
static constexpr size_t NumCores = _NumCores;
static constexpr size_t NumCores = NumCores_;
static constexpr bool IsValidCore(s32 core) {
return 0 <= core && core < static_cast<s32>(NumCores);
@@ -367,7 +367,7 @@ public:
this->scheduled_queue.MoveToFront(member->GetPriority(), member->GetActiveCore(), member);
}
constexpr Thread* MoveToScheduledBack(Member* member) {
constexpr KThread* MoveToScheduledBack(Member* member) {
return this->scheduled_queue.MoveToBack(member->GetPriority(), member->GetActiveCore(),
member);
}

View File

@@ -0,0 +1,57 @@
// Copyright 2021 yuzu emulator team
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include <algorithm>
#include "common/assert.h"
#include "common/common_funcs.h"
#include "common/logging/log.h"
#include "core/hle/kernel/errors.h"
#include "core/hle/kernel/k_readable_event.h"
#include "core/hle/kernel/k_scheduler.h"
#include "core/hle/kernel/k_thread.h"
#include "core/hle/kernel/kernel.h"
#include "core/hle/kernel/object.h"
#include "core/hle/kernel/svc_results.h"
namespace Kernel {
KReadableEvent::KReadableEvent(KernelCore& kernel, std::string&& name)
: KSynchronizationObject{kernel, std::move(name)} {}
KReadableEvent::~KReadableEvent() = default;
bool KReadableEvent::IsSignaled() const {
ASSERT(kernel.GlobalSchedulerContext().IsLocked());
return is_signaled;
}
ResultCode KReadableEvent::Signal() {
KScopedSchedulerLock lk{kernel};
if (!is_signaled) {
is_signaled = true;
NotifyAvailable();
}
return RESULT_SUCCESS;
}
ResultCode KReadableEvent::Clear() {
Reset();
return RESULT_SUCCESS;
}
ResultCode KReadableEvent::Reset() {
KScopedSchedulerLock lk{kernel};
if (!is_signaled) {
return Svc::ResultInvalidState;
}
is_signaled = false;
return RESULT_SUCCESS;
}
} // namespace Kernel

View File

@@ -0,0 +1,51 @@
// Copyright 2021 yuzu emulator team
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include "core/hle/kernel/k_synchronization_object.h"
#include "core/hle/kernel/object.h"
#include "core/hle/result.h"
namespace Kernel {
class KernelCore;
class KEvent;
class KReadableEvent final : public KSynchronizationObject {
public:
explicit KReadableEvent(KernelCore& kernel, std::string&& name);
~KReadableEvent() override;
std::string GetTypeName() const override {
return "KReadableEvent";
}
static constexpr HandleType HANDLE_TYPE = HandleType::ReadableEvent;
HandleType GetHandleType() const override {
return HANDLE_TYPE;
}
KEvent* GetParent() const {
return parent;
}
void Initialize(KEvent* parent_) {
is_signaled = false;
parent = parent_;
}
bool IsSignaled() const override;
void Finalize() override {}
ResultCode Signal();
ResultCode Clear();
ResultCode Reset();
private:
bool is_signaled{};
KEvent* parent{};
};
} // namespace Kernel

View File

@@ -0,0 +1,152 @@
// Copyright 2020 yuzu Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
// This file references various implementation details from Atmosphere, an open-source firmware for
// the Nintendo Switch. Copyright 2018-2020 Atmosphere-NX.
#include "common/assert.h"
#include "core/core.h"
#include "core/core_timing.h"
#include "core/core_timing_util.h"
#include "core/hle/kernel/k_resource_limit.h"
#include "core/hle/kernel/svc_results.h"
namespace Kernel {
constexpr s64 DefaultTimeout = 10000000000; // 10 seconds
KResourceLimit::KResourceLimit(KernelCore& kernel, Core::System& system)
: Object{kernel}, lock{kernel}, cond_var{kernel}, kernel{kernel}, system(system) {}
KResourceLimit::~KResourceLimit() = default;
s64 KResourceLimit::GetLimitValue(LimitableResource which) const {
const auto index = static_cast<std::size_t>(which);
s64 value{};
{
KScopedLightLock lk{lock};
value = limit_values[index];
ASSERT(value >= 0);
ASSERT(current_values[index] <= limit_values[index]);
ASSERT(current_hints[index] <= current_values[index]);
}
return value;
}
s64 KResourceLimit::GetCurrentValue(LimitableResource which) const {
const auto index = static_cast<std::size_t>(which);
s64 value{};
{
KScopedLightLock lk{lock};
value = current_values[index];
ASSERT(value >= 0);
ASSERT(current_values[index] <= limit_values[index]);
ASSERT(current_hints[index] <= current_values[index]);
}
return value;
}
s64 KResourceLimit::GetPeakValue(LimitableResource which) const {
const auto index = static_cast<std::size_t>(which);
s64 value{};
{
KScopedLightLock lk{lock};
value = peak_values[index];
ASSERT(value >= 0);
ASSERT(current_values[index] <= limit_values[index]);
ASSERT(current_hints[index] <= current_values[index]);
}
return value;
}
s64 KResourceLimit::GetFreeValue(LimitableResource which) const {
const auto index = static_cast<std::size_t>(which);
s64 value{};
{
KScopedLightLock lk(lock);
ASSERT(current_values[index] >= 0);
ASSERT(current_values[index] <= limit_values[index]);
ASSERT(current_hints[index] <= current_values[index]);
value = limit_values[index] - current_values[index];
}
return value;
}
ResultCode KResourceLimit::SetLimitValue(LimitableResource which, s64 value) {
const auto index = static_cast<std::size_t>(which);
KScopedLightLock lk(lock);
R_UNLESS(current_values[index] <= value, Svc::ResultInvalidState);
limit_values[index] = value;
return RESULT_SUCCESS;
}
bool KResourceLimit::Reserve(LimitableResource which, s64 value) {
return Reserve(which, value, system.CoreTiming().GetGlobalTimeNs().count() + DefaultTimeout);
}
bool KResourceLimit::Reserve(LimitableResource which, s64 value, s64 timeout) {
ASSERT(value >= 0);
const auto index = static_cast<std::size_t>(which);
KScopedLightLock lk(lock);
ASSERT(current_hints[index] <= current_values[index]);
if (current_hints[index] >= limit_values[index]) {
return false;
}
// Loop until we reserve or run out of time.
while (true) {
ASSERT(current_values[index] <= limit_values[index]);
ASSERT(current_hints[index] <= current_values[index]);
// If we would overflow, don't allow to succeed.
if (current_values[index] + value <= current_values[index]) {
break;
}
if (current_values[index] + value <= limit_values[index]) {
current_values[index] += value;
current_hints[index] += value;
peak_values[index] = std::max(peak_values[index], current_values[index]);
return true;
}
if (current_hints[index] + value <= limit_values[index] &&
(timeout < 0 || system.CoreTiming().GetGlobalTimeNs().count() < timeout)) {
waiter_count++;
cond_var.Wait(&lock, timeout);
waiter_count--;
} else {
break;
}
}
return false;
}
void KResourceLimit::Release(LimitableResource which, s64 value) {
Release(which, value, value);
}
void KResourceLimit::Release(LimitableResource which, s64 value, s64 hint) {
ASSERT(value >= 0);
ASSERT(hint >= 0);
const auto index = static_cast<std::size_t>(which);
KScopedLightLock lk(lock);
ASSERT(current_values[index] <= limit_values[index]);
ASSERT(current_hints[index] <= current_values[index]);
ASSERT(value <= current_values[index]);
ASSERT(hint <= current_hints[index]);
current_values[index] -= value;
current_hints[index] -= hint;
if (waiter_count != 0) {
cond_var.Broadcast();
}
}
} // namespace Kernel

View File

@@ -0,0 +1,81 @@
// Copyright 2020 yuzu Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
// This file references various implementation details from Atmosphere, an open-source firmware for
// the Nintendo Switch. Copyright 2018-2020 Atmosphere-NX.
#pragma once
#include <array>
#include "common/common_types.h"
#include "core/hle/kernel/k_light_condition_variable.h"
#include "core/hle/kernel/k_light_lock.h"
#include "core/hle/kernel/object.h"
union ResultCode;
namespace Core {
class System;
}
namespace Kernel {
class KernelCore;
enum class LimitableResource : u32 {
PhysicalMemory = 0,
Threads = 1,
Events = 2,
TransferMemory = 3,
Sessions = 4,
Count,
};
constexpr bool IsValidResourceType(LimitableResource type) {
return type < LimitableResource::Count;
}
class KResourceLimit final : public Object {
public:
explicit KResourceLimit(KernelCore& kernel, Core::System& system);
~KResourceLimit();
s64 GetLimitValue(LimitableResource which) const;
s64 GetCurrentValue(LimitableResource which) const;
s64 GetPeakValue(LimitableResource which) const;
s64 GetFreeValue(LimitableResource which) const;
ResultCode SetLimitValue(LimitableResource which, s64 value);
bool Reserve(LimitableResource which, s64 value);
bool Reserve(LimitableResource which, s64 value, s64 timeout);
void Release(LimitableResource which, s64 value);
void Release(LimitableResource which, s64 value, s64 hint);
std::string GetTypeName() const override {
return "KResourceLimit";
}
std::string GetName() const override {
return GetTypeName();
}
static constexpr HandleType HANDLE_TYPE = HandleType::ResourceLimit;
HandleType GetHandleType() const override {
return HANDLE_TYPE;
}
virtual void Finalize() override {}
private:
using ResourceArray = std::array<s64, static_cast<std::size_t>(LimitableResource::Count)>;
ResourceArray limit_values{};
ResourceArray current_values{};
ResourceArray current_hints{};
ResourceArray peak_values{};
mutable KLightLock lock;
s32 waiter_count{};
KLightConditionVariable cond_var;
KernelCore& kernel;
Core::System& system;
};
} // namespace Kernel

View File

@@ -17,25 +17,30 @@
#include "core/cpu_manager.h"
#include "core/hle/kernel/k_scheduler.h"
#include "core/hle/kernel/k_scoped_scheduler_lock_and_sleep.h"
#include "core/hle/kernel/k_thread.h"
#include "core/hle/kernel/kernel.h"
#include "core/hle/kernel/physical_core.h"
#include "core/hle/kernel/process.h"
#include "core/hle/kernel/thread.h"
#include "core/hle/kernel/time_manager.h"
namespace Kernel {
static void IncrementScheduledCount(Kernel::Thread* thread) {
static void IncrementScheduledCount(Kernel::KThread* thread) {
if (auto process = thread->GetOwnerProcess(); process) {
process->IncrementScheduledCount();
}
}
void KScheduler::RescheduleCores(KernelCore& kernel, u64 cores_pending_reschedule,
Core::EmuThreadHandle global_thread) {
const u32 current_core = global_thread.host_handle;
bool must_context_switch = global_thread.guest_handle != InvalidHandle &&
(current_core < Core::Hardware::NUM_CPU_CORES);
void KScheduler::RescheduleCores(KernelCore& kernel, u64 cores_pending_reschedule) {
auto scheduler = kernel.CurrentScheduler();
u32 current_core{0xF};
bool must_context_switch{};
if (scheduler) {
current_core = scheduler->core_id;
// TODO(bunnei): Should be set to true when we deprecate single core
must_context_switch = !kernel.IsPhantomModeForSingleCore();
}
while (cores_pending_reschedule != 0) {
const auto core = static_cast<u32>(std::countr_zero(cores_pending_reschedule));
@@ -56,28 +61,27 @@ void KScheduler::RescheduleCores(KernelCore& kernel, u64 cores_pending_reschedul
}
}
u64 KScheduler::UpdateHighestPriorityThread(Thread* highest_thread) {
u64 KScheduler::UpdateHighestPriorityThread(KThread* highest_thread) {
std::scoped_lock lock{guard};
if (Thread* prev_highest_thread = this->state.highest_priority_thread;
if (KThread* prev_highest_thread = state.highest_priority_thread;
prev_highest_thread != highest_thread) {
if (prev_highest_thread != nullptr) {
IncrementScheduledCount(prev_highest_thread);
prev_highest_thread->SetLastScheduledTick(system.CoreTiming().GetCPUTicks());
}
if (this->state.should_count_idle) {
if (state.should_count_idle) {
if (highest_thread != nullptr) {
// if (Process* process = highest_thread->GetOwnerProcess(); process != nullptr) {
// process->SetRunningThread(this->core_id, highest_thread,
// this->state.idle_count);
//}
if (Process* process = highest_thread->GetOwnerProcess(); process != nullptr) {
process->SetRunningThread(core_id, highest_thread, state.idle_count);
}
} else {
this->state.idle_count++;
state.idle_count++;
}
}
this->state.highest_priority_thread = highest_thread;
this->state.needs_scheduling = true;
return (1ULL << this->core_id);
state.highest_priority_thread = highest_thread;
state.needs_scheduling.store(true);
return (1ULL << core_id);
} else {
return 0;
}
@@ -90,16 +94,29 @@ u64 KScheduler::UpdateHighestPriorityThreadsImpl(KernelCore& kernel) {
ClearSchedulerUpdateNeeded(kernel);
u64 cores_needing_scheduling = 0, idle_cores = 0;
Thread* top_threads[Core::Hardware::NUM_CPU_CORES];
KThread* top_threads[Core::Hardware::NUM_CPU_CORES];
auto& priority_queue = GetPriorityQueue(kernel);
/// We want to go over all cores, finding the highest priority thread and determining if
/// scheduling is needed for that core.
for (size_t core_id = 0; core_id < Core::Hardware::NUM_CPU_CORES; core_id++) {
Thread* top_thread = priority_queue.GetScheduledFront(static_cast<s32>(core_id));
KThread* top_thread = priority_queue.GetScheduledFront(static_cast<s32>(core_id));
if (top_thread != nullptr) {
// If the thread has no waiters, we need to check if the process has a thread pinned.
// TODO(bunnei): Implement thread pinning
if (top_thread->GetNumKernelWaiters() == 0) {
if (Process* parent = top_thread->GetOwnerProcess(); parent != nullptr) {
if (KThread* pinned = parent->GetPinnedThread(static_cast<s32>(core_id));
pinned != nullptr && pinned != top_thread) {
// We prefer our parent's pinned thread if possible. However, we also don't
// want to schedule un-runnable threads.
if (pinned->GetRawState() == ThreadState::Runnable) {
top_thread = pinned;
} else {
top_thread = nullptr;
}
}
}
}
} else {
idle_cores |= (1ULL << core_id);
}
@@ -112,7 +129,7 @@ u64 KScheduler::UpdateHighestPriorityThreadsImpl(KernelCore& kernel) {
// Idle cores are bad. We're going to try to migrate threads to each idle core in turn.
while (idle_cores != 0) {
const auto core_id = static_cast<u32>(std::countr_zero(idle_cores));
if (Thread* suggested = priority_queue.GetSuggestedFront(core_id); suggested != nullptr) {
if (KThread* suggested = priority_queue.GetSuggestedFront(core_id); suggested != nullptr) {
s32 migration_candidates[Core::Hardware::NUM_CPU_CORES];
size_t num_candidates = 0;
@@ -120,7 +137,7 @@ u64 KScheduler::UpdateHighestPriorityThreadsImpl(KernelCore& kernel) {
while (suggested != nullptr) {
// Check if the suggested thread is the top thread on its core.
const s32 suggested_core = suggested->GetActiveCore();
if (Thread* top_thread =
if (KThread* top_thread =
(suggested_core >= 0) ? top_threads[suggested_core] : nullptr;
top_thread != suggested) {
// Make sure we're not dealing with threads too high priority for migration.
@@ -152,7 +169,7 @@ u64 KScheduler::UpdateHighestPriorityThreadsImpl(KernelCore& kernel) {
// Check if there's some other thread that can run on the candidate core.
const s32 candidate_core = migration_candidates[i];
suggested = top_threads[candidate_core];
if (Thread* next_on_candidate_core =
if (KThread* next_on_candidate_core =
priority_queue.GetScheduledNext(candidate_core, suggested);
next_on_candidate_core != nullptr) {
// The candidate core can run some other thread! We'll migrate its current
@@ -182,7 +199,20 @@ u64 KScheduler::UpdateHighestPriorityThreadsImpl(KernelCore& kernel) {
return cores_needing_scheduling;
}
void KScheduler::OnThreadStateChanged(KernelCore& kernel, Thread* thread, ThreadState old_state) {
void KScheduler::ClearPreviousThread(KernelCore& kernel, KThread* thread) {
ASSERT(kernel.GlobalSchedulerContext().IsLocked());
for (size_t i = 0; i < Core::Hardware::NUM_CPU_CORES; ++i) {
// Get an atomic reference to the core scheduler's previous thread.
std::atomic_ref<KThread*> prev_thread(kernel.Scheduler(static_cast<s32>(i)).prev_thread);
static_assert(std::atomic_ref<KThread*>::is_always_lock_free);
// Atomically clear the previous thread if it's our target.
KThread* compare = thread;
prev_thread.compare_exchange_strong(compare, nullptr);
}
}
void KScheduler::OnThreadStateChanged(KernelCore& kernel, KThread* thread, ThreadState old_state) {
ASSERT(kernel.GlobalSchedulerContext().IsLocked());
// Check if the state has changed, because if it hasn't there's nothing to do.
@@ -205,7 +235,7 @@ void KScheduler::OnThreadStateChanged(KernelCore& kernel, Thread* thread, Thread
}
}
void KScheduler::OnThreadPriorityChanged(KernelCore& kernel, Thread* thread, s32 old_priority) {
void KScheduler::OnThreadPriorityChanged(KernelCore& kernel, KThread* thread, s32 old_priority) {
ASSERT(kernel.GlobalSchedulerContext().IsLocked());
// If the thread is runnable, we want to change its priority in the queue.
@@ -217,7 +247,7 @@ void KScheduler::OnThreadPriorityChanged(KernelCore& kernel, Thread* thread, s32
}
}
void KScheduler::OnThreadAffinityMaskChanged(KernelCore& kernel, Thread* thread,
void KScheduler::OnThreadAffinityMaskChanged(KernelCore& kernel, KThread* thread,
const KAffinityMask& old_affinity, s32 old_core) {
ASSERT(kernel.GlobalSchedulerContext().IsLocked());
@@ -237,8 +267,8 @@ void KScheduler::RotateScheduledQueue(s32 core_id, s32 priority) {
auto& priority_queue = GetPriorityQueue(kernel);
// Rotate the front of the queue to the end.
Thread* top_thread = priority_queue.GetScheduledFront(core_id, priority);
Thread* next_thread = nullptr;
KThread* top_thread = priority_queue.GetScheduledFront(core_id, priority);
KThread* next_thread = nullptr;
if (top_thread != nullptr) {
next_thread = priority_queue.MoveToScheduledBack(top_thread);
if (next_thread != top_thread) {
@@ -249,11 +279,11 @@ void KScheduler::RotateScheduledQueue(s32 core_id, s32 priority) {
// While we have a suggested thread, try to migrate it!
{
Thread* suggested = priority_queue.GetSuggestedFront(core_id, priority);
KThread* suggested = priority_queue.GetSuggestedFront(core_id, priority);
while (suggested != nullptr) {
// Check if the suggested thread is the top thread on its core.
const s32 suggested_core = suggested->GetActiveCore();
if (Thread* top_on_suggested_core =
if (KThread* top_on_suggested_core =
(suggested_core >= 0) ? priority_queue.GetScheduledFront(suggested_core)
: nullptr;
top_on_suggested_core != suggested) {
@@ -285,7 +315,7 @@ void KScheduler::RotateScheduledQueue(s32 core_id, s32 priority) {
// Now that we might have migrated a thread with the same priority, check if we can do better.
{
Thread* best_thread = priority_queue.GetScheduledFront(core_id);
KThread* best_thread = priority_queue.GetScheduledFront(core_id);
if (best_thread == GetCurrentThread()) {
best_thread = priority_queue.GetScheduledNext(core_id, best_thread);
}
@@ -293,7 +323,7 @@ void KScheduler::RotateScheduledQueue(s32 core_id, s32 priority) {
// If the best thread we can choose has a priority the same or worse than ours, try to
// migrate a higher priority thread.
if (best_thread != nullptr && best_thread->GetPriority() >= priority) {
Thread* suggested = priority_queue.GetSuggestedFront(core_id);
KThread* suggested = priority_queue.GetSuggestedFront(core_id);
while (suggested != nullptr) {
// If the suggestion's priority is the same as ours, don't bother.
if (suggested->GetPriority() >= best_thread->GetPriority()) {
@@ -302,7 +332,7 @@ void KScheduler::RotateScheduledQueue(s32 core_id, s32 priority) {
// Check if the suggested thread is the top thread on its core.
const s32 suggested_core = suggested->GetActiveCore();
if (Thread* top_on_suggested_core =
if (KThread* top_on_suggested_core =
(suggested_core >= 0) ? priority_queue.GetScheduledFront(suggested_core)
: nullptr;
top_on_suggested_core != suggested) {
@@ -352,12 +382,14 @@ void KScheduler::DisableScheduling(KernelCore& kernel) {
}
}
void KScheduler::EnableScheduling(KernelCore& kernel, u64 cores_needing_scheduling,
Core::EmuThreadHandle global_thread) {
void KScheduler::EnableScheduling(KernelCore& kernel, u64 cores_needing_scheduling) {
if (auto* scheduler = kernel.CurrentScheduler(); scheduler) {
scheduler->GetCurrentThread()->EnableDispatch();
ASSERT(scheduler->GetCurrentThread()->GetDisableDispatchCount() >= 1);
if (scheduler->GetCurrentThread()->GetDisableDispatchCount() >= 1) {
scheduler->GetCurrentThread()->EnableDispatch();
}
}
RescheduleCores(kernel, cores_needing_scheduling, global_thread);
RescheduleCores(kernel, cores_needing_scheduling);
}
u64 KScheduler::UpdateHighestPriorityThreads(KernelCore& kernel) {
@@ -372,15 +404,13 @@ KSchedulerPriorityQueue& KScheduler::GetPriorityQueue(KernelCore& kernel) {
return kernel.GlobalSchedulerContext().priority_queue;
}
void KScheduler::YieldWithoutCoreMigration() {
auto& kernel = system.Kernel();
void KScheduler::YieldWithoutCoreMigration(KernelCore& kernel) {
// Validate preconditions.
ASSERT(CanSchedule(kernel));
ASSERT(kernel.CurrentProcess() != nullptr);
// Get the current thread and process.
Thread& cur_thread = *GetCurrentThread();
KThread& cur_thread = Kernel::GetCurrentThread(kernel);
Process& cur_process = *kernel.CurrentProcess();
// If the thread's yield count matches, there's nothing for us to do.
@@ -398,7 +428,7 @@ void KScheduler::YieldWithoutCoreMigration() {
const auto cur_state = cur_thread.GetRawState();
if (cur_state == ThreadState::Runnable) {
// Put the current thread at the back of the queue.
Thread* next_thread = priority_queue.MoveToScheduledBack(std::addressof(cur_thread));
KThread* next_thread = priority_queue.MoveToScheduledBack(std::addressof(cur_thread));
IncrementScheduledCount(std::addressof(cur_thread));
// If the next thread is different, we have an update to perform.
@@ -413,15 +443,13 @@ void KScheduler::YieldWithoutCoreMigration() {
}
}
void KScheduler::YieldWithCoreMigration() {
auto& kernel = system.Kernel();
void KScheduler::YieldWithCoreMigration(KernelCore& kernel) {
// Validate preconditions.
ASSERT(CanSchedule(kernel));
ASSERT(kernel.CurrentProcess() != nullptr);
// Get the current thread and process.
Thread& cur_thread = *GetCurrentThread();
KThread& cur_thread = Kernel::GetCurrentThread(kernel);
Process& cur_process = *kernel.CurrentProcess();
// If the thread's yield count matches, there's nothing for us to do.
@@ -442,17 +470,17 @@ void KScheduler::YieldWithCoreMigration() {
const s32 core_id = cur_thread.GetActiveCore();
// Put the current thread at the back of the queue.
Thread* next_thread = priority_queue.MoveToScheduledBack(std::addressof(cur_thread));
KThread* next_thread = priority_queue.MoveToScheduledBack(std::addressof(cur_thread));
IncrementScheduledCount(std::addressof(cur_thread));
// While we have a suggested thread, try to migrate it!
bool recheck = false;
Thread* suggested = priority_queue.GetSuggestedFront(core_id);
KThread* suggested = priority_queue.GetSuggestedFront(core_id);
while (suggested != nullptr) {
// Check if the suggested thread is the thread running on its core.
const s32 suggested_core = suggested->GetActiveCore();
if (Thread* running_on_suggested_core =
if (KThread* running_on_suggested_core =
(suggested_core >= 0)
? kernel.Scheduler(suggested_core).state.highest_priority_thread
: nullptr;
@@ -503,15 +531,13 @@ void KScheduler::YieldWithCoreMigration() {
}
}
void KScheduler::YieldToAnyThread() {
auto& kernel = system.Kernel();
void KScheduler::YieldToAnyThread(KernelCore& kernel) {
// Validate preconditions.
ASSERT(CanSchedule(kernel));
ASSERT(kernel.CurrentProcess() != nullptr);
// Get the current thread and process.
Thread& cur_thread = *GetCurrentThread();
KThread& cur_thread = Kernel::GetCurrentThread(kernel);
Process& cur_process = *kernel.CurrentProcess();
// If the thread's yield count matches, there's nothing for us to do.
@@ -539,11 +565,11 @@ void KScheduler::YieldToAnyThread() {
// If there's nothing scheduled, we can try to perform a migration.
if (priority_queue.GetScheduledFront(core_id) == nullptr) {
// While we have a suggested thread, try to migrate it!
Thread* suggested = priority_queue.GetSuggestedFront(core_id);
KThread* suggested = priority_queue.GetSuggestedFront(core_id);
while (suggested != nullptr) {
// Check if the suggested thread is the top thread on its core.
const s32 suggested_core = suggested->GetActiveCore();
if (Thread* top_on_suggested_core =
if (KThread* top_on_suggested_core =
(suggested_core >= 0) ? priority_queue.GetScheduledFront(suggested_core)
: nullptr;
top_on_suggested_core != suggested) {
@@ -581,22 +607,21 @@ void KScheduler::YieldToAnyThread() {
}
}
KScheduler::KScheduler(Core::System& system, std::size_t core_id)
: system(system), core_id(core_id) {
KScheduler::KScheduler(Core::System& system, s32 core_id) : system(system), core_id(core_id) {
switch_fiber = std::make_shared<Common::Fiber>(OnSwitch, this);
this->state.needs_scheduling = true;
this->state.interrupt_task_thread_runnable = false;
this->state.should_count_idle = false;
this->state.idle_count = 0;
this->state.idle_thread_stack = nullptr;
this->state.highest_priority_thread = nullptr;
state.needs_scheduling.store(true);
state.interrupt_task_thread_runnable = false;
state.should_count_idle = false;
state.idle_count = 0;
state.idle_thread_stack = nullptr;
state.highest_priority_thread = nullptr;
}
KScheduler::~KScheduler() = default;
Thread* KScheduler::GetCurrentThread() const {
if (current_thread) {
return current_thread;
KThread* KScheduler::GetCurrentThread() const {
if (auto result = current_thread.load(); result) {
return result;
}
return idle_thread;
}
@@ -613,7 +638,7 @@ void KScheduler::RescheduleCurrentCore() {
phys_core.ClearInterrupt();
}
guard.lock();
if (this->state.needs_scheduling) {
if (state.needs_scheduling.load()) {
Schedule();
} else {
guard.unlock();
@@ -624,66 +649,76 @@ void KScheduler::OnThreadStart() {
SwitchContextStep2();
}
void KScheduler::Unload(Thread* thread) {
void KScheduler::Unload(KThread* thread) {
LOG_TRACE(Kernel, "core {}, unload thread {}", core_id, thread ? thread->GetName() : "nullptr");
if (thread) {
thread->SetIsRunning(false);
if (thread->IsContinuousOnSVC() && !thread->IsHLEThread()) {
if (thread->IsCallingSvc()) {
system.ArmInterface(core_id).ExceptionalExit();
thread->SetContinuousOnSVC(false);
thread->ClearIsCallingSvc();
}
if (!thread->IsHLEThread() && !thread->HasExited()) {
if (!thread->IsTerminationRequested()) {
prev_thread = thread;
Core::ARM_Interface& cpu_core = system.ArmInterface(core_id);
cpu_core.SaveContext(thread->GetContext32());
cpu_core.SaveContext(thread->GetContext64());
// Save the TPIDR_EL0 system register in case it was modified.
thread->SetTPIDR_EL0(cpu_core.GetTPIDR_EL0());
cpu_core.ClearExclusiveState();
} else {
prev_thread = nullptr;
}
thread->context_guard.unlock();
}
}
void KScheduler::Reload(Thread* thread) {
void KScheduler::Reload(KThread* thread) {
LOG_TRACE(Kernel, "core {}, reload thread {}", core_id, thread ? thread->GetName() : "nullptr");
if (thread) {
ASSERT_MSG(thread->GetState() == ThreadState::Runnable, "Thread must be runnable.");
// Cancel any outstanding wakeup events for this thread
thread->SetIsRunning(true);
thread->SetWasRunning(false);
auto* const thread_owner_process = thread->GetOwnerProcess();
if (thread_owner_process != nullptr) {
system.Kernel().MakeCurrentProcess(thread_owner_process);
}
if (!thread->IsHLEThread()) {
Core::ARM_Interface& cpu_core = system.ArmInterface(core_id);
cpu_core.LoadContext(thread->GetContext32());
cpu_core.LoadContext(thread->GetContext64());
cpu_core.SetTlsAddress(thread->GetTLSAddress());
cpu_core.SetTPIDR_EL0(thread->GetTPIDR_EL0());
cpu_core.ClearExclusiveState();
}
Core::ARM_Interface& cpu_core = system.ArmInterface(core_id);
cpu_core.LoadContext(thread->GetContext32());
cpu_core.LoadContext(thread->GetContext64());
cpu_core.SetTlsAddress(thread->GetTLSAddress());
cpu_core.SetTPIDR_EL0(thread->GetTPIDR_EL0());
cpu_core.ClearExclusiveState();
}
}
void KScheduler::SwitchContextStep2() {
// Load context of new thread
Reload(current_thread);
Reload(current_thread.load());
RescheduleCurrentCore();
}
void KScheduler::ScheduleImpl() {
Thread* previous_thread = current_thread;
current_thread = state.highest_priority_thread;
KThread* previous_thread = current_thread.load();
KThread* next_thread = state.highest_priority_thread;
this->state.needs_scheduling = false;
state.needs_scheduling = false;
if (current_thread == previous_thread) {
// We never want to schedule a null thread, so use the idle thread if we don't have a next.
if (next_thread == nullptr) {
next_thread = idle_thread;
}
// If we're not actually switching thread, there's nothing to do.
if (next_thread == current_thread.load()) {
guard.unlock();
return;
}
current_thread.store(next_thread);
Process* const previous_process = system.Kernel().CurrentProcess();
UpdateLastContextSwitchTime(previous_thread, previous_process);
@@ -714,28 +749,29 @@ void KScheduler::SwitchToCurrent() {
while (true) {
{
std::scoped_lock lock{guard};
current_thread = state.highest_priority_thread;
this->state.needs_scheduling = false;
current_thread.store(state.highest_priority_thread);
state.needs_scheduling.store(false);
}
const auto is_switch_pending = [this] {
std::scoped_lock lock{guard};
return state.needs_scheduling.load(std::memory_order_relaxed);
return state.needs_scheduling.load();
};
do {
if (current_thread != nullptr && !current_thread->IsHLEThread()) {
current_thread->context_guard.lock();
if (current_thread->GetRawState() != ThreadState::Runnable) {
current_thread->context_guard.unlock();
auto next_thread = current_thread.load();
if (next_thread != nullptr) {
next_thread->context_guard.lock();
if (next_thread->GetRawState() != ThreadState::Runnable) {
next_thread->context_guard.unlock();
break;
}
if (static_cast<u32>(current_thread->GetProcessorID()) != core_id) {
current_thread->context_guard.unlock();
if (next_thread->GetActiveCore() != core_id) {
next_thread->context_guard.unlock();
break;
}
}
std::shared_ptr<Common::Fiber>* next_context;
if (current_thread != nullptr) {
next_context = &current_thread->GetHostContext();
if (next_thread != nullptr) {
next_context = &next_thread->GetHostContext();
} else {
next_context = &idle_thread->GetHostContext();
}
@@ -744,13 +780,13 @@ void KScheduler::SwitchToCurrent() {
}
}
void KScheduler::UpdateLastContextSwitchTime(Thread* thread, Process* process) {
void KScheduler::UpdateLastContextSwitchTime(KThread* thread, Process* process) {
const u64 prev_switch_ticks = last_context_switch_time;
const u64 most_recent_switch_ticks = system.CoreTiming().GetCPUTicks();
const u64 update_ticks = most_recent_switch_ticks - prev_switch_ticks;
if (thread != nullptr) {
thread->UpdateCPUTimeTicks(update_ticks);
thread->AddCpuTime(core_id, update_ticks);
}
if (process != nullptr) {
@@ -764,15 +800,10 @@ void KScheduler::Initialize() {
std::string name = "Idle Thread Id:" + std::to_string(core_id);
std::function<void(void*)> init_func = Core::CpuManager::GetIdleThreadStartFunc();
void* init_func_parameter = system.GetCpuManager().GetStartFuncParamater();
ThreadType type = static_cast<ThreadType>(THREADTYPE_KERNEL | THREADTYPE_HLE | THREADTYPE_IDLE);
auto thread_res = Thread::Create(system, type, name, 0, 64, 0, static_cast<u32>(core_id), 0,
nullptr, std::move(init_func), init_func_parameter);
auto thread_res = KThread::Create(system, ThreadType::Main, name, 0,
KThread::IdleThreadPriority, 0, static_cast<u32>(core_id), 0,
nullptr, std::move(init_func), init_func_parameter);
idle_thread = thread_res.Unwrap().get();
{
KScopedSchedulerLock lock{system.Kernel()};
idle_thread->SetState(ThreadState::Runnable);
}
}
KScopedSchedulerLock::KScopedSchedulerLock(KernelCore& kernel)

View File

@@ -29,29 +29,33 @@ namespace Kernel {
class KernelCore;
class Process;
class SchedulerLock;
class Thread;
class KThread;
class KScheduler final {
public:
explicit KScheduler(Core::System& system, std::size_t core_id);
explicit KScheduler(Core::System& system, s32 core_id);
~KScheduler();
/// Reschedules to the next available thread (call after current thread is suspended)
void RescheduleCurrentCore();
/// Reschedules cores pending reschedule, to be called on EnableScheduling.
static void RescheduleCores(KernelCore& kernel, u64 cores_pending_reschedule,
Core::EmuThreadHandle global_thread);
static void RescheduleCores(KernelCore& kernel, u64 cores_pending_reschedule);
/// The next two are for SingleCore Only.
/// Unload current thread before preempting core.
void Unload(Thread* thread);
void Unload(KThread* thread);
/// Reload current thread after core preemption.
void Reload(Thread* thread);
void Reload(KThread* thread);
/// Gets the current running thread
[[nodiscard]] Thread* GetCurrentThread() const;
[[nodiscard]] KThread* GetCurrentThread() const;
/// Returns true if the scheduler is idle
[[nodiscard]] bool IsIdle() const {
return GetCurrentThread() == idle_thread;
}
/// Gets the timestamp for the last context switch in ticks.
[[nodiscard]] u64 GetLastContextSwitchTicks() const;
@@ -72,14 +76,14 @@ public:
return switch_fiber;
}
[[nodiscard]] u64 UpdateHighestPriorityThread(Thread* highest_thread);
[[nodiscard]] u64 UpdateHighestPriorityThread(KThread* highest_thread);
/**
* Takes a thread and moves it to the back of the it's priority list.
*
* @note This operation can be redundant and no scheduling is changed if marked as so.
*/
void YieldWithoutCoreMigration();
static void YieldWithoutCoreMigration(KernelCore& kernel);
/**
* Takes a thread and moves it to the back of the it's priority list.
@@ -88,7 +92,7 @@ public:
*
* @note This operation can be redundant and no scheduling is changed if marked as so.
*/
void YieldWithCoreMigration();
static void YieldWithCoreMigration(KernelCore& kernel);
/**
* Takes a thread and moves it out of the scheduling queue.
@@ -97,16 +101,18 @@ public:
*
* @note This operation can be redundant and no scheduling is changed if marked as so.
*/
void YieldToAnyThread();
static void YieldToAnyThread(KernelCore& kernel);
static void ClearPreviousThread(KernelCore& kernel, KThread* thread);
/// Notify the scheduler a thread's status has changed.
static void OnThreadStateChanged(KernelCore& kernel, Thread* thread, ThreadState old_state);
static void OnThreadStateChanged(KernelCore& kernel, KThread* thread, ThreadState old_state);
/// Notify the scheduler a thread's priority has changed.
static void OnThreadPriorityChanged(KernelCore& kernel, Thread* thread, s32 old_priority);
static void OnThreadPriorityChanged(KernelCore& kernel, KThread* thread, s32 old_priority);
/// Notify the scheduler a thread's core and/or affinity mask has changed.
static void OnThreadAffinityMaskChanged(KernelCore& kernel, Thread* thread,
static void OnThreadAffinityMaskChanged(KernelCore& kernel, KThread* thread,
const KAffinityMask& old_affinity, s32 old_core);
static bool CanSchedule(KernelCore& kernel);
@@ -114,8 +120,7 @@ public:
static void SetSchedulerUpdateNeeded(KernelCore& kernel);
static void ClearSchedulerUpdateNeeded(KernelCore& kernel);
static void DisableScheduling(KernelCore& kernel);
static void EnableScheduling(KernelCore& kernel, u64 cores_needing_scheduling,
Core::EmuThreadHandle global_thread);
static void EnableScheduling(KernelCore& kernel, u64 cores_needing_scheduling);
[[nodiscard]] static u64 UpdateHighestPriorityThreads(KernelCore& kernel);
private:
@@ -163,13 +168,15 @@ private:
* most recent tick count retrieved. No special arithmetic is
* applied to it.
*/
void UpdateLastContextSwitchTime(Thread* thread, Process* process);
void UpdateLastContextSwitchTime(KThread* thread, Process* process);
static void OnSwitch(void* this_scheduler);
void SwitchToCurrent();
Thread* current_thread{};
Thread* idle_thread{};
KThread* prev_thread{};
std::atomic<KThread*> current_thread{};
KThread* idle_thread;
std::shared_ptr<Common::Fiber> switch_fiber{};
@@ -178,7 +185,7 @@ private:
bool interrupt_task_thread_runnable{};
bool should_count_idle{};
u64 idle_count{};
Thread* highest_priority_thread{};
KThread* highest_priority_thread{};
void* idle_thread_stack{};
};
@@ -186,7 +193,7 @@ private:
Core::System& system;
u64 last_context_switch_time{};
const std::size_t core_id;
const s32 core_id;
Common::SpinLock guard{};
};

View File

@@ -10,6 +10,7 @@
#include "common/assert.h"
#include "common/spin_lock.h"
#include "core/hardware_properties.h"
#include "core/hle/kernel/k_thread.h"
#include "core/hle/kernel/kernel.h"
namespace Kernel {
@@ -22,46 +23,45 @@ public:
explicit KAbstractSchedulerLock(KernelCore& kernel_) : kernel{kernel_} {}
bool IsLockedByCurrentThread() const {
return this->owner_thread == kernel.GetCurrentEmuThreadID();
return owner_thread == GetCurrentThreadPointer(kernel);
}
void Lock() {
if (this->IsLockedByCurrentThread()) {
if (IsLockedByCurrentThread()) {
// If we already own the lock, we can just increment the count.
ASSERT(this->lock_count > 0);
this->lock_count++;
ASSERT(lock_count > 0);
lock_count++;
} else {
// Otherwise, we want to disable scheduling and acquire the spinlock.
SchedulerType::DisableScheduling(kernel);
this->spin_lock.lock();
spin_lock.lock();
// For debug, ensure that our state is valid.
ASSERT(this->lock_count == 0);
ASSERT(this->owner_thread == Core::EmuThreadHandle::InvalidHandle());
ASSERT(lock_count == 0);
ASSERT(owner_thread == nullptr);
// Increment count, take ownership.
this->lock_count = 1;
this->owner_thread = kernel.GetCurrentEmuThreadID();
lock_count = 1;
owner_thread = GetCurrentThreadPointer(kernel);
}
}
void Unlock() {
ASSERT(this->IsLockedByCurrentThread());
ASSERT(this->lock_count > 0);
ASSERT(IsLockedByCurrentThread());
ASSERT(lock_count > 0);
// Release an instance of the lock.
if ((--this->lock_count) == 0) {
if ((--lock_count) == 0) {
// We're no longer going to hold the lock. Take note of what cores need scheduling.
const u64 cores_needing_scheduling =
SchedulerType::UpdateHighestPriorityThreads(kernel);
Core::EmuThreadHandle leaving_thread = owner_thread;
// Note that we no longer hold the lock, and unlock the spinlock.
this->owner_thread = Core::EmuThreadHandle::InvalidHandle();
this->spin_lock.unlock();
owner_thread = nullptr;
spin_lock.unlock();
// Enable scheduling, and perform a rescheduling operation.
SchedulerType::EnableScheduling(kernel, cores_needing_scheduling, leaving_thread);
SchedulerType::EnableScheduling(kernel, cores_needing_scheduling);
}
}
@@ -69,7 +69,7 @@ private:
KernelCore& kernel;
Common::SpinLock spin_lock{};
s32 lock_count{};
Core::EmuThreadHandle owner_thread{Core::EmuThreadHandle::InvalidHandle()};
KThread* owner_thread{};
};
} // namespace Kernel

View File

@@ -9,27 +9,24 @@
#include "common/common_types.h"
#include "core/hle/kernel/handle_table.h"
#include "core/hle/kernel/k_thread.h"
#include "core/hle/kernel/kernel.h"
#include "core/hle/kernel/thread.h"
#include "core/hle/kernel/time_manager.h"
namespace Kernel {
class KScopedSchedulerLockAndSleep {
public:
explicit KScopedSchedulerLockAndSleep(KernelCore& kernel, Handle& event_handle, Thread* t,
s64 timeout)
: kernel(kernel), event_handle(event_handle), thread(t), timeout_tick(timeout) {
event_handle = InvalidHandle;
explicit KScopedSchedulerLockAndSleep(KernelCore& kernel, KThread* t, s64 timeout)
: kernel(kernel), thread(t), timeout_tick(timeout) {
// Lock the scheduler.
kernel.GlobalSchedulerContext().scheduler_lock.Lock();
}
~KScopedSchedulerLockAndSleep() {
// Register the sleep.
if (this->timeout_tick > 0) {
kernel.TimeManager().ScheduleTimeEvent(event_handle, this->thread, this->timeout_tick);
if (timeout_tick > 0) {
kernel.TimeManager().ScheduleTimeEvent(thread, timeout_tick);
}
// Unlock the scheduler.
@@ -37,13 +34,12 @@ public:
}
void CancelSleep() {
this->timeout_tick = 0;
timeout_tick = 0;
}
private:
KernelCore& kernel;
Handle& event_handle;
Thread* thread{};
KThread* thread{};
s64 timeout_tick{};
};

View File

@@ -7,9 +7,9 @@
#include "core/hle/kernel/k_scheduler.h"
#include "core/hle/kernel/k_scoped_scheduler_lock_and_sleep.h"
#include "core/hle/kernel/k_synchronization_object.h"
#include "core/hle/kernel/k_thread.h"
#include "core/hle/kernel/kernel.h"
#include "core/hle/kernel/svc_results.h"
#include "core/hle/kernel/thread.h"
namespace Kernel {
@@ -20,12 +20,11 @@ ResultCode KSynchronizationObject::Wait(KernelCore& kernel, s32* out_index,
std::vector<ThreadListNode> thread_nodes(num_objects);
// Prepare for wait.
Thread* thread = kernel.CurrentScheduler()->GetCurrentThread();
Handle timer = InvalidHandle;
KThread* thread = kernel.CurrentScheduler()->GetCurrentThread();
{
// Setup the scheduling lock and sleep.
KScopedSchedulerLockAndSleep slp(kernel, timer, thread, timeout);
KScopedSchedulerLockAndSleep slp{kernel, thread, timeout};
// Check if any of the objects are already signaled.
for (auto i = 0; i < num_objects; ++i) {
@@ -90,10 +89,7 @@ ResultCode KSynchronizationObject::Wait(KernelCore& kernel, s32* out_index,
thread->SetWaitObjectsForDebugging({});
// Cancel the timer as needed.
if (timer != InvalidHandle) {
auto& time_manager = kernel.TimeManager();
time_manager.UnscheduleTimeEvent(timer);
}
kernel.TimeManager().UnscheduleTimeEvent(thread);
// Get the wait result.
ResultCode wait_result{RESULT_SUCCESS};
@@ -136,7 +132,10 @@ ResultCode KSynchronizationObject::Wait(KernelCore& kernel, s32* out_index,
KSynchronizationObject::KSynchronizationObject(KernelCore& kernel) : Object{kernel} {}
KSynchronizationObject ::~KSynchronizationObject() = default;
KSynchronizationObject::KSynchronizationObject(KernelCore& kernel, std::string&& name)
: Object{kernel, std::move(name)} {}
KSynchronizationObject::~KSynchronizationObject() = default;
void KSynchronizationObject::NotifyAvailable(ResultCode result) {
KScopedSchedulerLock lock(kernel);
@@ -148,7 +147,7 @@ void KSynchronizationObject::NotifyAvailable(ResultCode result) {
// Iterate over each thread.
for (auto* cur_node = thread_list_head; cur_node != nullptr; cur_node = cur_node->next) {
Thread* thread = cur_node->thread;
KThread* thread = cur_node->thread;
if (thread->GetState() == ThreadState::Waiting) {
thread->SetSyncedObject(this, result);
thread->SetState(ThreadState::Runnable);
@@ -156,8 +155,8 @@ void KSynchronizationObject::NotifyAvailable(ResultCode result) {
}
}
std::vector<Thread*> KSynchronizationObject::GetWaitingThreadsForDebugging() const {
std::vector<Thread*> threads;
std::vector<KThread*> KSynchronizationObject::GetWaitingThreadsForDebugging() const {
std::vector<KThread*> threads;
// If debugging, dump the list of waiters.
{

View File

@@ -13,14 +13,14 @@ namespace Kernel {
class KernelCore;
class Synchronization;
class Thread;
class KThread;
/// Class that represents a Kernel object that a thread can be waiting on
class KSynchronizationObject : public Object {
public:
struct ThreadListNode {
ThreadListNode* next{};
Thread* thread{};
KThread* thread{};
};
[[nodiscard]] static ResultCode Wait(KernelCore& kernel, s32* out_index,
@@ -29,10 +29,11 @@ public:
[[nodiscard]] virtual bool IsSignaled() const = 0;
[[nodiscard]] std::vector<Thread*> GetWaitingThreadsForDebugging() const;
[[nodiscard]] std::vector<KThread*> GetWaitingThreadsForDebugging() const;
protected:
explicit KSynchronizationObject(KernelCore& kernel);
explicit KSynchronizationObject(KernelCore& kernel, std::string&& name);
virtual ~KSynchronizationObject();
void NotifyAvailable(ResultCode result);

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,768 @@
// Copyright 2021 yuzu Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include <array>
#include <span>
#include <string>
#include <utility>
#include <vector>
#include <boost/intrusive/list.hpp>
#include "common/common_types.h"
#include "common/intrusive_red_black_tree.h"
#include "common/spin_lock.h"
#include "core/arm/arm_interface.h"
#include "core/hle/kernel/k_affinity_mask.h"
#include "core/hle/kernel/k_light_lock.h"
#include "core/hle/kernel/k_synchronization_object.h"
#include "core/hle/kernel/object.h"
#include "core/hle/kernel/svc_common.h"
#include "core/hle/kernel/svc_types.h"
#include "core/hle/result.h"
namespace Common {
class Fiber;
}
namespace Core {
class ARM_Interface;
class System;
} // namespace Core
namespace Kernel {
class GlobalSchedulerContext;
class KernelCore;
class Process;
class KScheduler;
class KThreadQueue;
using KThreadFunction = VAddr;
enum class ThreadType : u32 {
Main = 0,
Kernel = 1,
HighPriority = 2,
User = 3,
};
DECLARE_ENUM_FLAG_OPERATORS(ThreadType);
enum class SuspendType : u32 {
Process = 0,
Thread = 1,
Debug = 2,
Backtrace = 3,
Init = 4,
Count,
};
enum class ThreadState : u16 {
Initialized = 0,
Waiting = 1,
Runnable = 2,
Terminated = 3,
SuspendShift = 4,
Mask = (1 << SuspendShift) - 1,
ProcessSuspended = (1 << (0 + SuspendShift)),
ThreadSuspended = (1 << (1 + SuspendShift)),
DebugSuspended = (1 << (2 + SuspendShift)),
BacktraceSuspended = (1 << (3 + SuspendShift)),
InitSuspended = (1 << (4 + SuspendShift)),
SuspendFlagMask = ((1 << 5) - 1) << SuspendShift,
};
DECLARE_ENUM_FLAG_OPERATORS(ThreadState);
enum class DpcFlag : u32 {
Terminating = (1 << 0),
Terminated = (1 << 1),
};
enum class ThreadWaitReasonForDebugging : u32 {
None, ///< Thread is not waiting
Sleep, ///< Thread is waiting due to a SleepThread SVC
IPC, ///< Thread is waiting for the reply from an IPC request
Synchronization, ///< Thread is waiting due to a WaitSynchronization SVC
ConditionVar, ///< Thread is waiting due to a WaitProcessWideKey SVC
Arbitration, ///< Thread is waiting due to a SignalToAddress/WaitForAddress SVC
Suspended, ///< Thread is waiting due to process suspension
};
[[nodiscard]] KThread* GetCurrentThreadPointer(KernelCore& kernel);
[[nodiscard]] KThread& GetCurrentThread(KernelCore& kernel);
[[nodiscard]] s32 GetCurrentCoreId(KernelCore& kernel);
class KThread final : public KSynchronizationObject, public boost::intrusive::list_base_hook<> {
friend class KScheduler;
friend class Process;
public:
static constexpr s32 DefaultThreadPriority = 44;
static constexpr s32 IdleThreadPriority = Svc::LowestThreadPriority + 1;
explicit KThread(KernelCore& kernel);
~KThread() override;
public:
using ThreadContext32 = Core::ARM_Interface::ThreadContext32;
using ThreadContext64 = Core::ARM_Interface::ThreadContext64;
using WaiterList = boost::intrusive::list<KThread>;
/**
* Creates and returns a new thread. The new thread is immediately scheduled
* @param system The instance of the whole system
* @param name The friendly name desired for the thread
* @param entry_point The address at which the thread should start execution
* @param priority The thread's priority
* @param arg User data to pass to the thread
* @param processor_id The ID(s) of the processors on which the thread is desired to be run
* @param stack_top The address of the thread's stack top
* @param owner_process The parent process for the thread, if null, it's a kernel thread
* @return A shared pointer to the newly created thread
*/
[[nodiscard]] static ResultVal<std::shared_ptr<KThread>> Create(
Core::System& system, ThreadType type_flags, std::string name, VAddr entry_point,
u32 priority, u64 arg, s32 processor_id, VAddr stack_top, Process* owner_process);
/**
* Creates and returns a new thread. The new thread is immediately scheduled
* @param system The instance of the whole system
* @param name The friendly name desired for the thread
* @param entry_point The address at which the thread should start execution
* @param priority The thread's priority
* @param arg User data to pass to the thread
* @param processor_id The ID(s) of the processors on which the thread is desired to be run
* @param stack_top The address of the thread's stack top
* @param owner_process The parent process for the thread, if null, it's a kernel thread
* @param thread_start_func The function where the host context will start.
* @param thread_start_parameter The parameter which will passed to host context on init
* @return A shared pointer to the newly created thread
*/
[[nodiscard]] static ResultVal<std::shared_ptr<KThread>> Create(
Core::System& system, ThreadType type_flags, std::string name, VAddr entry_point,
u32 priority, u64 arg, s32 processor_id, VAddr stack_top, Process* owner_process,
std::function<void(void*)>&& thread_start_func, void* thread_start_parameter);
[[nodiscard]] std::string GetName() const override {
return name;
}
void SetName(std::string new_name) {
name = std::move(new_name);
}
[[nodiscard]] std::string GetTypeName() const override {
return "Thread";
}
static constexpr HandleType HANDLE_TYPE = HandleType::Thread;
[[nodiscard]] HandleType GetHandleType() const override {
return HANDLE_TYPE;
}
/**
* Gets the thread's current priority
* @return The current thread's priority
*/
[[nodiscard]] s32 GetPriority() const {
return priority;
}
/**
* Sets the thread's current priority.
* @param priority The new priority.
*/
void SetPriority(s32 value) {
priority = value;
}
/**
* Gets the thread's nominal priority.
* @return The current thread's nominal priority.
*/
[[nodiscard]] s32 GetBasePriority() const {
return base_priority;
}
/**
* Gets the thread's thread ID
* @return The thread's ID
*/
[[nodiscard]] u64 GetThreadID() const {
return thread_id;
}
void ContinueIfHasKernelWaiters() {
if (GetNumKernelWaiters() > 0) {
Continue();
}
}
void Wakeup();
void SetBasePriority(s32 value);
[[nodiscard]] ResultCode Run();
void Exit();
[[nodiscard]] u32 GetSuspendFlags() const {
return suspend_allowed_flags & suspend_request_flags;
}
[[nodiscard]] bool IsSuspended() const {
return GetSuspendFlags() != 0;
}
[[nodiscard]] bool IsSuspendRequested(SuspendType type) const {
return (suspend_request_flags &
(1u << (static_cast<u32>(ThreadState::SuspendShift) + static_cast<u32>(type)))) !=
0;
}
[[nodiscard]] bool IsSuspendRequested() const {
return suspend_request_flags != 0;
}
void RequestSuspend(SuspendType type);
void Resume(SuspendType type);
void TrySuspend();
void Continue();
void Suspend();
void Finalize() override;
bool IsSignaled() const override;
void SetSyncedObject(KSynchronizationObject* obj, ResultCode wait_res) {
synced_object = obj;
wait_result = wait_res;
}
[[nodiscard]] ResultCode GetWaitResult(KSynchronizationObject** out) const {
*out = synced_object;
return wait_result;
}
/*
* Returns the Thread Local Storage address of the current thread
* @returns VAddr of the thread's TLS
*/
[[nodiscard]] VAddr GetTLSAddress() const {
return tls_address;
}
/*
* Returns the value of the TPIDR_EL0 Read/Write system register for this thread.
* @returns The value of the TPIDR_EL0 register.
*/
[[nodiscard]] u64 GetTPIDR_EL0() const {
return thread_context_64.tpidr;
}
/// Sets the value of the TPIDR_EL0 Read/Write system register for this thread.
void SetTPIDR_EL0(u64 value) {
thread_context_64.tpidr = value;
thread_context_32.tpidr = static_cast<u32>(value);
}
[[nodiscard]] ThreadContext32& GetContext32() {
return thread_context_32;
}
[[nodiscard]] const ThreadContext32& GetContext32() const {
return thread_context_32;
}
[[nodiscard]] ThreadContext64& GetContext64() {
return thread_context_64;
}
[[nodiscard]] const ThreadContext64& GetContext64() const {
return thread_context_64;
}
[[nodiscard]] std::shared_ptr<Common::Fiber>& GetHostContext();
[[nodiscard]] ThreadState GetState() const {
return thread_state & ThreadState::Mask;
}
[[nodiscard]] ThreadState GetRawState() const {
return thread_state;
}
void SetState(ThreadState state);
[[nodiscard]] s64 GetLastScheduledTick() const {
return last_scheduled_tick;
}
void SetLastScheduledTick(s64 tick) {
last_scheduled_tick = tick;
}
void AddCpuTime([[maybe_unused]] s32 core_id_, s64 amount) {
cpu_time += amount;
// TODO(bunnei): Debug kernels track per-core tick counts. Should we?
}
[[nodiscard]] s64 GetCpuTime() const {
return cpu_time;
}
[[nodiscard]] s32 GetActiveCore() const {
return core_id;
}
void SetActiveCore(s32 core) {
core_id = core;
}
[[nodiscard]] s32 GetCurrentCore() const {
return current_core_id;
}
void SetCurrentCore(s32 core) {
current_core_id = core;
}
[[nodiscard]] Process* GetOwnerProcess() {
return parent;
}
[[nodiscard]] const Process* GetOwnerProcess() const {
return parent;
}
[[nodiscard]] bool IsUserThread() const {
return parent != nullptr;
}
[[nodiscard]] KThread* GetLockOwner() const {
return lock_owner;
}
void SetLockOwner(KThread* owner) {
lock_owner = owner;
}
[[nodiscard]] const KAffinityMask& GetAffinityMask() const {
return physical_affinity_mask;
}
[[nodiscard]] ResultCode GetCoreMask(s32* out_ideal_core, u64* out_affinity_mask);
[[nodiscard]] ResultCode GetPhysicalCoreMask(s32* out_ideal_core, u64* out_affinity_mask);
[[nodiscard]] ResultCode SetCoreMask(s32 core_id, u64 v_affinity_mask);
[[nodiscard]] ResultCode SetActivity(Svc::ThreadActivity activity);
[[nodiscard]] ResultCode Sleep(s64 timeout);
[[nodiscard]] s64 GetYieldScheduleCount() const {
return schedule_count;
}
void SetYieldScheduleCount(s64 count) {
schedule_count = count;
}
void WaitCancel();
[[nodiscard]] bool IsWaitCancelled() const {
return wait_cancelled;
}
[[nodiscard]] void ClearWaitCancelled() {
wait_cancelled = false;
}
[[nodiscard]] bool IsCancellable() const {
return cancellable;
}
void SetCancellable() {
cancellable = true;
}
void ClearCancellable() {
cancellable = false;
}
[[nodiscard]] bool IsTerminationRequested() const {
return termination_requested || GetRawState() == ThreadState::Terminated;
}
struct StackParameters {
u8 svc_permission[0x10];
std::atomic<u8> dpc_flags;
u8 current_svc_id;
bool is_calling_svc;
bool is_in_exception_handler;
bool is_pinned;
s32 disable_count;
KThread* cur_thread;
};
[[nodiscard]] StackParameters& GetStackParameters() {
return stack_parameters;
}
[[nodiscard]] const StackParameters& GetStackParameters() const {
return stack_parameters;
}
class QueueEntry {
public:
constexpr QueueEntry() = default;
constexpr void Initialize() {
prev = nullptr;
next = nullptr;
}
constexpr KThread* GetPrev() const {
return prev;
}
constexpr KThread* GetNext() const {
return next;
}
constexpr void SetPrev(KThread* thread) {
prev = thread;
}
constexpr void SetNext(KThread* thread) {
next = thread;
}
private:
KThread* prev{};
KThread* next{};
};
[[nodiscard]] QueueEntry& GetPriorityQueueEntry(s32 core) {
return per_core_priority_queue_entry[core];
}
[[nodiscard]] const QueueEntry& GetPriorityQueueEntry(s32 core) const {
return per_core_priority_queue_entry[core];
}
void SetSleepingQueue(KThreadQueue* q) {
sleeping_queue = q;
}
[[nodiscard]] s32 GetDisableDispatchCount() const {
return this->GetStackParameters().disable_count;
}
void DisableDispatch() {
ASSERT(GetCurrentThread(kernel).GetDisableDispatchCount() >= 0);
this->GetStackParameters().disable_count++;
}
void EnableDispatch() {
ASSERT(GetCurrentThread(kernel).GetDisableDispatchCount() > 0);
this->GetStackParameters().disable_count--;
}
void Pin();
void Unpin();
void SetInExceptionHandler() {
this->GetStackParameters().is_in_exception_handler = true;
}
void ClearInExceptionHandler() {
this->GetStackParameters().is_in_exception_handler = false;
}
[[nodiscard]] bool IsInExceptionHandler() const {
return this->GetStackParameters().is_in_exception_handler;
}
void SetIsCallingSvc() {
this->GetStackParameters().is_calling_svc = true;
}
void ClearIsCallingSvc() {
this->GetStackParameters().is_calling_svc = false;
}
[[nodiscard]] bool IsCallingSvc() const {
return this->GetStackParameters().is_calling_svc;
}
[[nodiscard]] u8 GetSvcId() const {
return this->GetStackParameters().current_svc_id;
}
void RegisterDpc(DpcFlag flag) {
this->GetStackParameters().dpc_flags |= static_cast<u8>(flag);
}
void ClearDpc(DpcFlag flag) {
this->GetStackParameters().dpc_flags &= ~static_cast<u8>(flag);
}
[[nodiscard]] u8 GetDpc() const {
return this->GetStackParameters().dpc_flags;
}
[[nodiscard]] bool HasDpc() const {
return this->GetDpc() != 0;
}
void SetWaitReasonForDebugging(ThreadWaitReasonForDebugging reason) {
wait_reason_for_debugging = reason;
}
[[nodiscard]] ThreadWaitReasonForDebugging GetWaitReasonForDebugging() const {
return wait_reason_for_debugging;
}
[[nodiscard]] ThreadType GetThreadTypeForDebugging() const {
return thread_type_for_debugging;
}
void SetWaitObjectsForDebugging(const std::span<KSynchronizationObject*>& objects) {
wait_objects_for_debugging.clear();
wait_objects_for_debugging.reserve(objects.size());
for (const auto& object : objects) {
wait_objects_for_debugging.emplace_back(object);
}
}
[[nodiscard]] const std::vector<KSynchronizationObject*>& GetWaitObjectsForDebugging() const {
return wait_objects_for_debugging;
}
void SetMutexWaitAddressForDebugging(VAddr address) {
mutex_wait_address_for_debugging = address;
}
[[nodiscard]] VAddr GetMutexWaitAddressForDebugging() const {
return mutex_wait_address_for_debugging;
}
[[nodiscard]] s32 GetIdealCoreForDebugging() const {
return virtual_ideal_core_id;
}
void AddWaiter(KThread* thread);
void RemoveWaiter(KThread* thread);
[[nodiscard]] ResultCode GetThreadContext3(std::vector<u8>& out);
[[nodiscard]] KThread* RemoveWaiterByKey(s32* out_num_waiters, VAddr key);
[[nodiscard]] VAddr GetAddressKey() const {
return address_key;
}
[[nodiscard]] u32 GetAddressKeyValue() const {
return address_key_value;
}
void SetAddressKey(VAddr key) {
address_key = key;
}
void SetAddressKey(VAddr key, u32 val) {
address_key = key;
address_key_value = val;
}
[[nodiscard]] bool HasWaiters() const {
return !waiter_list.empty();
}
[[nodiscard]] s32 GetNumKernelWaiters() const {
return num_kernel_waiters;
}
[[nodiscard]] u64 GetConditionVariableKey() const {
return condvar_key;
}
[[nodiscard]] u64 GetAddressArbiterKey() const {
return condvar_key;
}
private:
static constexpr size_t PriorityInheritanceCountMax = 10;
union SyncObjectBuffer {
std::array<KSynchronizationObject*, Svc::ArgumentHandleCountMax> sync_objects{};
std::array<Handle,
Svc::ArgumentHandleCountMax*(sizeof(KSynchronizationObject*) / sizeof(Handle))>
handles;
constexpr SyncObjectBuffer() {}
};
static_assert(sizeof(SyncObjectBuffer::sync_objects) == sizeof(SyncObjectBuffer::handles));
struct ConditionVariableComparator {
struct LightCompareType {
u64 cv_key{};
s32 priority{};
[[nodiscard]] constexpr u64 GetConditionVariableKey() const {
return cv_key;
}
[[nodiscard]] constexpr s32 GetPriority() const {
return priority;
}
};
template <typename T>
requires(
std::same_as<T, KThread> ||
std::same_as<T, LightCompareType>) static constexpr int Compare(const T& lhs,
const KThread& rhs) {
const u64 l_key = lhs.GetConditionVariableKey();
const u64 r_key = rhs.GetConditionVariableKey();
if (l_key < r_key) {
// Sort first by key
return -1;
} else if (l_key == r_key && lhs.GetPriority() < rhs.GetPriority()) {
// And then by priority.
return -1;
} else {
return 1;
}
}
};
void AddWaiterImpl(KThread* thread);
void RemoveWaiterImpl(KThread* thread);
void StartTermination();
[[nodiscard]] ResultCode Initialize(KThreadFunction func, uintptr_t arg, VAddr user_stack_top,
s32 prio, s32 virt_core, Process* owner, ThreadType type);
[[nodiscard]] static ResultCode InitializeThread(KThread* thread, KThreadFunction func,
uintptr_t arg, VAddr user_stack_top, s32 prio,
s32 core, Process* owner, ThreadType type);
static void RestorePriority(KernelCore& kernel, KThread* thread);
// For core KThread implementation
ThreadContext32 thread_context_32{};
ThreadContext64 thread_context_64{};
Common::IntrusiveRedBlackTreeNode condvar_arbiter_tree_node{};
s32 priority{};
using ConditionVariableThreadTreeTraits =
Common::IntrusiveRedBlackTreeMemberTraitsDeferredAssert<
&KThread::condvar_arbiter_tree_node>;
using ConditionVariableThreadTree =
ConditionVariableThreadTreeTraits::TreeType<ConditionVariableComparator>;
ConditionVariableThreadTree* condvar_tree{};
u64 condvar_key{};
u64 virtual_affinity_mask{};
KAffinityMask physical_affinity_mask{};
u64 thread_id{};
std::atomic<s64> cpu_time{};
KSynchronizationObject* synced_object{};
VAddr address_key{};
Process* parent{};
VAddr kernel_stack_top{};
u32* light_ipc_data{};
VAddr tls_address{};
KLightLock activity_pause_lock;
s64 schedule_count{};
s64 last_scheduled_tick{};
std::array<QueueEntry, Core::Hardware::NUM_CPU_CORES> per_core_priority_queue_entry{};
KThreadQueue* sleeping_queue{};
WaiterList waiter_list{};
WaiterList pinned_waiter_list{};
KThread* lock_owner{};
u32 address_key_value{};
u32 suspend_request_flags{};
u32 suspend_allowed_flags{};
ResultCode wait_result{RESULT_SUCCESS};
s32 base_priority{};
s32 physical_ideal_core_id{};
s32 virtual_ideal_core_id{};
s32 num_kernel_waiters{};
s32 current_core_id{};
s32 core_id{};
KAffinityMask original_physical_affinity_mask{};
s32 original_physical_ideal_core_id{};
s32 num_core_migration_disables{};
ThreadState thread_state{};
std::atomic<bool> termination_requested{};
bool wait_cancelled{};
bool cancellable{};
bool signaled{};
bool initialized{};
bool debug_attached{};
s8 priority_inheritance_count{};
bool resource_limit_release_hint{};
StackParameters stack_parameters{};
Common::SpinLock context_guard{};
// For emulation
std::shared_ptr<Common::Fiber> host_context{};
// For debugging
std::vector<KSynchronizationObject*> wait_objects_for_debugging;
VAddr mutex_wait_address_for_debugging{};
ThreadWaitReasonForDebugging wait_reason_for_debugging{};
ThreadType thread_type_for_debugging{};
std::string name;
public:
using ConditionVariableThreadTreeType = ConditionVariableThreadTree;
void SetConditionVariable(ConditionVariableThreadTree* tree, VAddr address, u64 cv_key,
u32 value) {
condvar_tree = tree;
condvar_key = cv_key;
address_key = address;
address_key_value = value;
}
void ClearConditionVariable() {
condvar_tree = nullptr;
}
[[nodiscard]] bool IsWaitingForConditionVariable() const {
return condvar_tree != nullptr;
}
void SetAddressArbiter(ConditionVariableThreadTree* tree, u64 address) {
condvar_tree = tree;
condvar_key = address;
}
void ClearAddressArbiter() {
condvar_tree = nullptr;
}
[[nodiscard]] bool IsWaitingForAddressArbiter() const {
return condvar_tree != nullptr;
}
[[nodiscard]] ConditionVariableThreadTree* GetConditionVariableTree() const {
return condvar_tree;
}
};
} // namespace Kernel

View File

@@ -0,0 +1,81 @@
// Copyright 2021 yuzu Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include "core/hle/kernel/k_thread.h"
namespace Kernel {
class KThreadQueue {
public:
explicit KThreadQueue(KernelCore& kernel) : kernel{kernel} {}
bool IsEmpty() const {
return wait_list.empty();
}
KThread::WaiterList::iterator begin() {
return wait_list.begin();
}
KThread::WaiterList::iterator end() {
return wait_list.end();
}
bool SleepThread(KThread* t) {
KScopedSchedulerLock sl{kernel};
// If the thread needs terminating, don't enqueue it.
if (t->IsTerminationRequested()) {
return false;
}
// Set the thread's queue and mark it as waiting.
t->SetSleepingQueue(this);
t->SetState(ThreadState::Waiting);
// Add the thread to the queue.
wait_list.push_back(*t);
return true;
}
void WakeupThread(KThread* t) {
KScopedSchedulerLock sl{kernel};
// Remove the thread from the queue.
wait_list.erase(wait_list.iterator_to(*t));
// Mark the thread as no longer sleeping.
t->SetState(ThreadState::Runnable);
t->SetSleepingQueue(nullptr);
}
KThread* WakeupFrontThread() {
KScopedSchedulerLock sl{kernel};
if (wait_list.empty()) {
return nullptr;
} else {
// Remove the thread from the queue.
auto it = wait_list.begin();
KThread* thread = std::addressof(*it);
wait_list.erase(it);
ASSERT(thread->GetState() == ThreadState::Waiting);
// Mark the thread as no longer sleeping.
thread->SetState(ThreadState::Runnable);
thread->SetSleepingQueue(nullptr);
return thread;
}
}
private:
KernelCore& kernel;
KThread::WaiterList wait_list{};
};
} // namespace Kernel

View File

@@ -0,0 +1,27 @@
// Copyright 2021 yuzu emulator team
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include "core/hle/kernel/k_event.h"
#include "core/hle/kernel/k_readable_event.h"
#include "core/hle/kernel/k_writable_event.h"
namespace Kernel {
KWritableEvent::KWritableEvent(KernelCore& kernel, std::string&& name)
: Object{kernel, std::move(name)} {}
KWritableEvent::~KWritableEvent() = default;
void KWritableEvent::Initialize(KEvent* parent_) {
parent = parent_;
}
ResultCode KWritableEvent::Signal() {
return parent->GetReadableEvent()->Signal();
}
ResultCode KWritableEvent::Clear() {
return parent->GetReadableEvent()->Clear();
}
} // namespace Kernel

View File

@@ -0,0 +1,44 @@
// Copyright 2021 yuzu Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include "core/hle/kernel/object.h"
#include "core/hle/result.h"
namespace Kernel {
class KernelCore;
class KEvent;
class KWritableEvent final : public Object {
public:
explicit KWritableEvent(KernelCore& kernel, std::string&& name);
~KWritableEvent() override;
std::string GetTypeName() const override {
return "KWritableEvent";
}
static constexpr HandleType HANDLE_TYPE = HandleType::WritableEvent;
HandleType GetHandleType() const override {
return HANDLE_TYPE;
}
void Initialize(KEvent* parent_);
void Finalize() override {}
ResultCode Signal();
ResultCode Clear();
KEvent* GetParent() const {
return parent;
}
private:
KEvent* parent{};
};
} // namespace Kernel

View File

@@ -28,17 +28,17 @@
#include "core/hle/kernel/client_port.h"
#include "core/hle/kernel/errors.h"
#include "core/hle/kernel/handle_table.h"
#include "core/hle/kernel/k_resource_limit.h"
#include "core/hle/kernel/k_scheduler.h"
#include "core/hle/kernel/k_thread.h"
#include "core/hle/kernel/kernel.h"
#include "core/hle/kernel/memory/memory_layout.h"
#include "core/hle/kernel/memory/memory_manager.h"
#include "core/hle/kernel/memory/slab_heap.h"
#include "core/hle/kernel/physical_core.h"
#include "core/hle/kernel/process.h"
#include "core/hle/kernel/resource_limit.h"
#include "core/hle/kernel/service_thread.h"
#include "core/hle/kernel/shared_memory.h"
#include "core/hle/kernel/thread.h"
#include "core/hle/kernel/time_manager.h"
#include "core/hle/lock.h"
#include "core/hle/result.h"
@@ -57,14 +57,16 @@ struct KernelCore::Impl {
}
void Initialize(KernelCore& kernel) {
global_scheduler_context = std::make_unique<Kernel::GlobalSchedulerContext>(kernel);
RegisterHostThread();
global_scheduler_context = std::make_unique<Kernel::GlobalSchedulerContext>(kernel);
service_thread_manager =
std::make_unique<Common::ThreadWorker>(1, "yuzu:ServiceThreadManager");
is_phantom_mode_for_singlecore = false;
InitializePhysicalCores();
InitializeSystemResourceLimit(kernel);
InitializeSystemResourceLimit(kernel, system);
InitializeMemoryLayout();
InitializePreemption(kernel);
InitializeSchedulers();
@@ -116,32 +118,32 @@ struct KernelCore::Impl {
void InitializePhysicalCores() {
exclusive_monitor =
Core::MakeExclusiveMonitor(system.Memory(), Core::Hardware::NUM_CPU_CORES);
for (std::size_t i = 0; i < Core::Hardware::NUM_CPU_CORES; i++) {
for (u32 i = 0; i < Core::Hardware::NUM_CPU_CORES; i++) {
schedulers[i] = std::make_unique<Kernel::KScheduler>(system, i);
cores.emplace_back(i, system, *schedulers[i], interrupts);
}
}
void InitializeSchedulers() {
for (std::size_t i = 0; i < Core::Hardware::NUM_CPU_CORES; i++) {
for (u32 i = 0; i < Core::Hardware::NUM_CPU_CORES; i++) {
cores[i].Scheduler().Initialize();
}
}
// Creates the default system resource limit
void InitializeSystemResourceLimit(KernelCore& kernel) {
system_resource_limit = ResourceLimit::Create(kernel);
void InitializeSystemResourceLimit(KernelCore& kernel, Core::System& system) {
system_resource_limit = std::make_shared<KResourceLimit>(kernel, system);
// If setting the default system values fails, then something seriously wrong has occurred.
ASSERT(system_resource_limit->SetLimitValue(ResourceType::PhysicalMemory, 0x100000000)
ASSERT(system_resource_limit->SetLimitValue(LimitableResource::PhysicalMemory, 0x100000000)
.IsSuccess());
ASSERT(system_resource_limit->SetLimitValue(ResourceType::Threads, 800).IsSuccess());
ASSERT(system_resource_limit->SetLimitValue(ResourceType::Events, 700).IsSuccess());
ASSERT(system_resource_limit->SetLimitValue(ResourceType::TransferMemory, 200).IsSuccess());
ASSERT(system_resource_limit->SetLimitValue(ResourceType::Sessions, 900).IsSuccess());
ASSERT(system_resource_limit->SetLimitValue(LimitableResource::Threads, 800).IsSuccess());
ASSERT(system_resource_limit->SetLimitValue(LimitableResource::Events, 700).IsSuccess());
ASSERT(system_resource_limit->SetLimitValue(LimitableResource::TransferMemory, 200)
.IsSuccess());
ASSERT(system_resource_limit->SetLimitValue(LimitableResource::Sessions, 900).IsSuccess());
if (!system_resource_limit->Reserve(ResourceType::PhysicalMemory, 0) ||
!system_resource_limit->Reserve(ResourceType::PhysicalMemory, 0x60000)) {
if (!system_resource_limit->Reserve(LimitableResource::PhysicalMemory, 0x60000)) {
UNREACHABLE();
}
}
@@ -168,11 +170,9 @@ struct KernelCore::Impl {
std::string name = "Suspend Thread Id:" + std::to_string(i);
std::function<void(void*)> init_func = Core::CpuManager::GetSuspendThreadStartFunc();
void* init_func_parameter = system.GetCpuManager().GetStartFuncParamater();
const auto type =
static_cast<ThreadType>(THREADTYPE_KERNEL | THREADTYPE_HLE | THREADTYPE_SUSPEND);
auto thread_res =
Thread::Create(system, type, std::move(name), 0, 0, 0, static_cast<u32>(i), 0,
nullptr, std::move(init_func), init_func_parameter);
auto thread_res = KThread::Create(system, ThreadType::HighPriority, std::move(name), 0,
0, 0, static_cast<u32>(i), 0, nullptr,
std::move(init_func), init_func_parameter);
suspend_threads[i] = std::move(thread_res).Unwrap();
}
@@ -207,6 +207,17 @@ struct KernelCore::Impl {
return host_thread_id;
}
// Gets the dummy KThread for the caller, allocating a new one if this is the first time
KThread* GetHostDummyThread() {
const thread_local auto thread =
KThread::Create(
system, ThreadType::Main, fmt::format("DummyThread:{}", GetHostThreadId()), 0,
KThread::DefaultThreadPriority, 0, static_cast<u32>(3), 0, nullptr,
[]([[maybe_unused]] void* arg) { UNREACHABLE(); }, nullptr)
.Unwrap();
return thread.get();
}
/// Registers a CPU core thread by allocating a host thread ID for it
void RegisterCoreThread(std::size_t core_id) {
ASSERT(core_id < Core::Hardware::NUM_CPU_CORES);
@@ -219,6 +230,7 @@ struct KernelCore::Impl {
/// Registers a new host thread by allocating a host thread ID for it
void RegisterHostThread() {
[[maybe_unused]] const auto this_id = GetHostThreadId();
[[maybe_unused]] const auto dummy_thread = GetHostDummyThread();
}
[[nodiscard]] u32 GetCurrentHostThreadID() {
@@ -229,20 +241,21 @@ struct KernelCore::Impl {
return this_id;
}
[[nodiscard]] Core::EmuThreadHandle GetCurrentEmuThreadID() {
Core::EmuThreadHandle result = Core::EmuThreadHandle::InvalidHandle();
result.host_handle = GetCurrentHostThreadID();
if (result.host_handle >= Core::Hardware::NUM_CPU_CORES) {
return result;
bool IsPhantomModeForSingleCore() const {
return is_phantom_mode_for_singlecore;
}
void SetIsPhantomModeForSingleCore(bool value) {
ASSERT(!is_multicore);
is_phantom_mode_for_singlecore = value;
}
KThread* GetCurrentEmuThread() {
const auto thread_id = GetCurrentHostThreadID();
if (thread_id >= Core::Hardware::NUM_CPU_CORES) {
return GetHostDummyThread();
}
const Kernel::KScheduler& sched = cores[result.host_handle].Scheduler();
const Kernel::Thread* current = sched.GetCurrentThread();
if (current != nullptr && !current->IsPhantomMode()) {
result.guest_handle = current->GetGlobalHandle();
} else {
result.guest_handle = InvalidHandle;
}
return result;
return schedulers[thread_id]->GetCurrentThread();
}
void InitializeMemoryLayout() {
@@ -307,7 +320,7 @@ struct KernelCore::Impl {
std::unique_ptr<Kernel::GlobalSchedulerContext> global_scheduler_context;
Kernel::TimeManager time_manager;
std::shared_ptr<ResourceLimit> system_resource_limit;
std::shared_ptr<KResourceLimit> system_resource_limit;
std::shared_ptr<Core::Timing::EventType> preemption_event;
@@ -342,11 +355,12 @@ struct KernelCore::Impl {
// the release of itself
std::unique_ptr<Common::ThreadWorker> service_thread_manager;
std::array<std::shared_ptr<Thread>, Core::Hardware::NUM_CPU_CORES> suspend_threads{};
std::array<std::shared_ptr<KThread>, Core::Hardware::NUM_CPU_CORES> suspend_threads{};
std::array<Core::CPUInterruptHandler, Core::Hardware::NUM_CPU_CORES> interrupts{};
std::array<std::unique_ptr<Kernel::KScheduler>, Core::Hardware::NUM_CPU_CORES> schedulers{};
bool is_multicore{};
bool is_phantom_mode_for_singlecore{};
u32 single_core_thread_id{};
std::array<u64, Core::Hardware::NUM_CPU_CORES> svc_ticks{};
@@ -376,12 +390,12 @@ void KernelCore::Shutdown() {
impl->Shutdown();
}
std::shared_ptr<ResourceLimit> KernelCore::GetSystemResourceLimit() const {
std::shared_ptr<KResourceLimit> KernelCore::GetSystemResourceLimit() const {
return impl->system_resource_limit;
}
std::shared_ptr<Thread> KernelCore::RetrieveThreadFromGlobalHandleTable(Handle handle) const {
return impl->global_handle_table.Get<Thread>(handle);
std::shared_ptr<KThread> KernelCore::RetrieveThreadFromGlobalHandleTable(Handle handle) const {
return impl->global_handle_table.Get<KThread>(handle);
}
void KernelCore::AppendNewProcess(std::shared_ptr<Process> process) {
@@ -546,8 +560,8 @@ u32 KernelCore::GetCurrentHostThreadID() const {
return impl->GetCurrentHostThreadID();
}
Core::EmuThreadHandle KernelCore::GetCurrentEmuThreadID() const {
return impl->GetCurrentEmuThreadID();
KThread* KernelCore::GetCurrentEmuThread() const {
return impl->GetCurrentEmuThread();
}
Memory::MemoryManager& KernelCore::MemoryManager() {
@@ -645,4 +659,12 @@ void KernelCore::ReleaseServiceThread(std::weak_ptr<Kernel::ServiceThread> servi
});
}
bool KernelCore::IsPhantomModeForSingleCore() const {
return impl->IsPhantomModeForSingleCore();
}
void KernelCore::SetIsPhantomModeForSingleCore(bool value) {
impl->SetIsPhantomModeForSingleCore(value);
}
} // namespace Kernel

View File

@@ -38,14 +38,18 @@ class GlobalSchedulerContext;
class HandleTable;
class PhysicalCore;
class Process;
class ResourceLimit;
class KResourceLimit;
class KScheduler;
class SharedMemory;
class ServiceThread;
class Synchronization;
class Thread;
class KThread;
class TimeManager;
using EmuThreadHandle = uintptr_t;
constexpr EmuThreadHandle EmuThreadHandleInvalid{};
constexpr EmuThreadHandle EmuThreadHandleReserved{1ULL << 63};
/// Represents a single instance of the kernel.
class KernelCore {
private:
@@ -81,10 +85,10 @@ public:
void Shutdown();
/// Retrieves a shared pointer to the system resource limit instance.
std::shared_ptr<ResourceLimit> GetSystemResourceLimit() const;
std::shared_ptr<KResourceLimit> GetSystemResourceLimit() const;
/// Retrieves a shared pointer to a Thread instance within the thread wakeup handle table.
std::shared_ptr<Thread> RetrieveThreadFromGlobalHandleTable(Handle handle) const;
std::shared_ptr<KThread> RetrieveThreadFromGlobalHandleTable(Handle handle) const;
/// Adds the given shared pointer to an internal list of active processes.
void AppendNewProcess(std::shared_ptr<Process> process);
@@ -161,8 +165,8 @@ public:
/// Determines whether or not the given port is a valid named port.
bool IsValidNamedPort(NamedPortTable::const_iterator port) const;
/// Gets the current host_thread/guest_thread handle.
Core::EmuThreadHandle GetCurrentEmuThreadID() const;
/// Gets the current host_thread/guest_thread pointer.
KThread* GetCurrentEmuThread() const;
/// Gets the current host_thread handle.
u32 GetCurrentHostThreadID() const;
@@ -237,10 +241,14 @@ public:
*/
void ReleaseServiceThread(std::weak_ptr<Kernel::ServiceThread> service_thread);
/// Workaround for single-core mode when preempting threads while idle.
bool IsPhantomModeForSingleCore() const;
void SetIsPhantomModeForSingleCore(bool value);
private:
friend class Object;
friend class Process;
friend class Thread;
friend class KThread;
/// Creates a new object ID, incrementing the internal object ID counter.
u32 CreateNewObjectID();

View File

@@ -7,6 +7,7 @@
#include "common/scope_exit.h"
#include "core/core.h"
#include "core/hle/kernel/errors.h"
#include "core/hle/kernel/k_resource_limit.h"
#include "core/hle/kernel/kernel.h"
#include "core/hle/kernel/memory/address_space_info.h"
#include "core/hle/kernel/memory/memory_block.h"
@@ -15,7 +16,6 @@
#include "core/hle/kernel/memory/page_table.h"
#include "core/hle/kernel/memory/system_control.h"
#include "core/hle/kernel/process.h"
#include "core/hle/kernel/resource_limit.h"
#include "core/memory.h"
namespace Kernel::Memory {
@@ -414,7 +414,7 @@ ResultCode PageTable::MapPhysicalMemory(VAddr addr, std::size_t size) {
const std::size_t remaining_pages{remaining_size / PageSize};
if (process->GetResourceLimit() &&
!process->GetResourceLimit()->Reserve(ResourceType::PhysicalMemory, remaining_size)) {
!process->GetResourceLimit()->Reserve(LimitableResource::PhysicalMemory, remaining_size)) {
return ERR_RESOURCE_LIMIT_EXCEEDED;
}
@@ -422,7 +422,7 @@ ResultCode PageTable::MapPhysicalMemory(VAddr addr, std::size_t size) {
{
auto block_guard = detail::ScopeExit([&] {
system.Kernel().MemoryManager().Free(page_linked_list, remaining_pages, memory_pool);
process->GetResourceLimit()->Release(ResourceType::PhysicalMemory, remaining_size);
process->GetResourceLimit()->Release(LimitableResource::PhysicalMemory, remaining_size);
});
CASCADE_CODE(system.Kernel().MemoryManager().Allocate(page_linked_list, remaining_pages,
@@ -474,7 +474,7 @@ ResultCode PageTable::UnmapPhysicalMemory(VAddr addr, std::size_t size) {
CASCADE_CODE(UnmapMemory(addr, size));
auto process{system.Kernel().CurrentProcess()};
process->GetResourceLimit()->Release(ResourceType::PhysicalMemory, mapped_size);
process->GetResourceLimit()->Release(LimitableResource::PhysicalMemory, mapped_size);
physical_memory_usage -= mapped_size;
return RESULT_SUCCESS;
@@ -783,7 +783,7 @@ ResultVal<VAddr> PageTable::SetHeapSize(std::size_t size) {
auto process{system.Kernel().CurrentProcess()};
if (process->GetResourceLimit() && delta != 0 &&
!process->GetResourceLimit()->Reserve(ResourceType::PhysicalMemory, delta)) {
!process->GetResourceLimit()->Reserve(LimitableResource::PhysicalMemory, delta)) {
return ERR_RESOURCE_LIMIT_EXCEEDED;
}

View File

@@ -8,7 +8,10 @@
namespace Kernel {
Object::Object(KernelCore& kernel) : kernel{kernel}, object_id{kernel.CreateNewObjectID()} {}
Object::Object(KernelCore& kernel_)
: kernel{kernel_}, object_id{kernel_.CreateNewObjectID()}, name{"[UNKNOWN KERNEL OBJECT]"} {}
Object::Object(KernelCore& kernel_, std::string&& name_)
: kernel{kernel_}, object_id{kernel_.CreateNewObjectID()}, name{std::move(name_)} {}
Object::~Object() = default;
bool Object::IsWaitable() const {
@@ -21,6 +24,7 @@ bool Object::IsWaitable() const {
return true;
case HandleType::Unknown:
case HandleType::Event:
case HandleType::WritableEvent:
case HandleType::SharedMemory:
case HandleType::TransferMemory:

View File

@@ -18,6 +18,7 @@ using Handle = u32;
enum class HandleType : u32 {
Unknown,
Event,
WritableEvent,
ReadableEvent,
SharedMemory,
@@ -34,7 +35,8 @@ enum class HandleType : u32 {
class Object : NonCopyable, public std::enable_shared_from_this<Object> {
public:
explicit Object(KernelCore& kernel);
explicit Object(KernelCore& kernel_);
explicit Object(KernelCore& kernel_, std::string&& name_);
virtual ~Object();
/// Returns a unique identifier for the object. For debugging purposes only.
@@ -46,7 +48,7 @@ public:
return "[BAD KERNEL OBJECT TYPE]";
}
virtual std::string GetName() const {
return "[UNKNOWN KERNEL OBJECT]";
return name;
}
virtual HandleType GetHandleType() const = 0;
@@ -61,12 +63,15 @@ public:
*/
bool IsWaitable() const;
virtual void Finalize() = 0;
protected:
/// The kernel instance this object was created under.
KernelCore& kernel;
private:
std::atomic<u32> object_id{0};
std::string name;
};
template <typename T>

View File

@@ -15,14 +15,15 @@
#include "core/file_sys/program_metadata.h"
#include "core/hle/kernel/code_set.h"
#include "core/hle/kernel/errors.h"
#include "core/hle/kernel/k_resource_limit.h"
#include "core/hle/kernel/k_scheduler.h"
#include "core/hle/kernel/k_thread.h"
#include "core/hle/kernel/kernel.h"
#include "core/hle/kernel/memory/memory_block_manager.h"
#include "core/hle/kernel/memory/page_table.h"
#include "core/hle/kernel/memory/slab_heap.h"
#include "core/hle/kernel/process.h"
#include "core/hle/kernel/resource_limit.h"
#include "core/hle/kernel/thread.h"
#include "core/hle/kernel/svc_results.h"
#include "core/hle/lock.h"
#include "core/memory.h"
#include "core/settings.h"
@@ -38,11 +39,10 @@ namespace {
*/
void SetupMainThread(Core::System& system, Process& owner_process, u32 priority, VAddr stack_top) {
const VAddr entry_point = owner_process.PageTable().GetCodeRegionStart();
ThreadType type = THREADTYPE_USER;
auto thread_res = Thread::Create(system, type, "main", entry_point, priority, 0,
owner_process.GetIdealCore(), stack_top, &owner_process);
auto thread_res = KThread::Create(system, ThreadType::User, "main", entry_point, priority, 0,
owner_process.GetIdealCoreId(), stack_top, &owner_process);
std::shared_ptr<Thread> thread = std::move(thread_res).Unwrap();
std::shared_ptr<KThread> thread = std::move(thread_res).Unwrap();
// Register 1 must be a handle to the main thread
const Handle thread_handle = owner_process.GetHandleTable().Create(thread).Unwrap();
@@ -117,7 +117,7 @@ std::shared_ptr<Process> Process::Create(Core::System& system, std::string name,
std::shared_ptr<Process> process = std::make_shared<Process>(system);
process->name = std::move(name);
process->resource_limit = ResourceLimit::Create(kernel);
process->resource_limit = std::make_shared<KResourceLimit>(kernel, system);
process->status = ProcessStatus::Created;
process->program_id = 0;
process->process_id = type == ProcessType::KernelInternal ? kernel.CreateNewKernelProcessID()
@@ -133,12 +133,29 @@ std::shared_ptr<Process> Process::Create(Core::System& system, std::string name,
return process;
}
std::shared_ptr<ResourceLimit> Process::GetResourceLimit() const {
std::shared_ptr<KResourceLimit> Process::GetResourceLimit() const {
return resource_limit;
}
void Process::IncrementThreadCount() {
ASSERT(num_threads >= 0);
num_created_threads++;
if (const auto count = ++num_threads; count > peak_num_threads) {
peak_num_threads = count;
}
}
void Process::DecrementThreadCount() {
ASSERT(num_threads > 0);
if (const auto count = --num_threads; count == 0) {
UNIMPLEMENTED_MSG("Process termination is not implemented!");
}
}
u64 Process::GetTotalPhysicalMemoryAvailable() const {
const u64 capacity{resource_limit->GetCurrentResourceValue(ResourceType::PhysicalMemory) +
const u64 capacity{resource_limit->GetFreeValue(LimitableResource::PhysicalMemory) +
page_table->GetTotalHeapSize() + GetSystemResourceSize() + image_size +
main_thread_stack_size};
@@ -162,26 +179,79 @@ u64 Process::GetTotalPhysicalMemoryUsedWithoutSystemResource() const {
return GetTotalPhysicalMemoryUsed() - GetSystemResourceUsage();
}
void Process::RegisterThread(const Thread* thread) {
bool Process::ReleaseUserException(KThread* thread) {
KScopedSchedulerLock sl{kernel};
if (exception_thread == thread) {
exception_thread = nullptr;
// Remove waiter thread.
s32 num_waiters{};
KThread* next = thread->RemoveWaiterByKey(
std::addressof(num_waiters),
reinterpret_cast<uintptr_t>(std::addressof(exception_thread)));
if (next != nullptr) {
if (next->GetState() == ThreadState::Waiting) {
next->SetState(ThreadState::Runnable);
} else {
KScheduler::SetSchedulerUpdateNeeded(kernel);
}
}
return true;
} else {
return false;
}
}
void Process::PinCurrentThread() {
ASSERT(kernel.GlobalSchedulerContext().IsLocked());
// Get the current thread.
const s32 core_id = GetCurrentCoreId(kernel);
KThread* cur_thread = GetCurrentThreadPointer(kernel);
// Pin it.
PinThread(core_id, cur_thread);
cur_thread->Pin();
// An update is needed.
KScheduler::SetSchedulerUpdateNeeded(kernel);
}
void Process::UnpinCurrentThread() {
ASSERT(kernel.GlobalSchedulerContext().IsLocked());
// Get the current thread.
const s32 core_id = GetCurrentCoreId(kernel);
KThread* cur_thread = GetCurrentThreadPointer(kernel);
// Unpin it.
cur_thread->Unpin();
UnpinThread(core_id, cur_thread);
// An update is needed.
KScheduler::SetSchedulerUpdateNeeded(kernel);
}
void Process::RegisterThread(const KThread* thread) {
thread_list.push_back(thread);
}
void Process::UnregisterThread(const Thread* thread) {
void Process::UnregisterThread(const KThread* thread) {
thread_list.remove(thread);
}
ResultCode Process::ClearSignalState() {
KScopedSchedulerLock lock(system.Kernel());
if (status == ProcessStatus::Exited) {
LOG_ERROR(Kernel, "called on a terminated process instance.");
return ERR_INVALID_STATE;
}
ResultCode Process::Reset() {
// Lock the process and the scheduler.
KScopedLightLock lk(state_lock);
KScopedSchedulerLock sl{kernel};
if (!is_signaled) {
LOG_ERROR(Kernel, "called on a process instance that isn't signaled.");
return ERR_INVALID_STATE;
}
// Validate that we're in a state that we can reset.
R_UNLESS(status != ProcessStatus::Exited, Svc::ResultInvalidState);
R_UNLESS(is_signaled, Svc::ResultInvalidState);
// Clear signaled.
is_signaled = false;
return RESULT_SUCCESS;
}
@@ -237,13 +307,13 @@ ResultCode Process::LoadFromMetadata(const FileSys::ProgramMetadata& metadata,
// Set initial resource limits
resource_limit->SetLimitValue(
ResourceType::PhysicalMemory,
LimitableResource::PhysicalMemory,
kernel.MemoryManager().GetSize(Memory::MemoryManager::Pool::Application));
resource_limit->SetLimitValue(ResourceType::Threads, 608);
resource_limit->SetLimitValue(ResourceType::Events, 700);
resource_limit->SetLimitValue(ResourceType::TransferMemory, 128);
resource_limit->SetLimitValue(ResourceType::Sessions, 894);
ASSERT(resource_limit->Reserve(ResourceType::PhysicalMemory, code_size));
resource_limit->SetLimitValue(LimitableResource::Threads, 608);
resource_limit->SetLimitValue(LimitableResource::Events, 700);
resource_limit->SetLimitValue(LimitableResource::TransferMemory, 128);
resource_limit->SetLimitValue(LimitableResource::Sessions, 894);
ASSERT(resource_limit->Reserve(LimitableResource::PhysicalMemory, code_size));
// Create TLS region
tls_region_address = CreateTLSRegion();
@@ -260,14 +330,14 @@ void Process::Run(s32 main_thread_priority, u64 stack_size) {
ChangeStatus(ProcessStatus::Running);
SetupMainThread(system, *this, main_thread_priority, main_thread_stack_top);
resource_limit->Reserve(ResourceType::Threads, 1);
resource_limit->Reserve(ResourceType::PhysicalMemory, main_thread_stack_size);
resource_limit->Reserve(LimitableResource::Threads, 1);
resource_limit->Reserve(LimitableResource::PhysicalMemory, main_thread_stack_size);
}
void Process::PrepareForTermination() {
ChangeStatus(ProcessStatus::Exiting);
const auto stop_threads = [this](const std::vector<std::shared_ptr<Thread>>& thread_list) {
const auto stop_threads = [this](const std::vector<std::shared_ptr<KThread>>& thread_list) {
for (auto& thread : thread_list) {
if (thread->GetOwnerProcess() != this)
continue;
@@ -279,7 +349,7 @@ void Process::PrepareForTermination() {
ASSERT_MSG(thread->GetState() == ThreadState::Waiting,
"Exiting processes with non-waiting threads is currently unimplemented");
thread->Stop();
thread->Exit();
}
};
@@ -372,7 +442,7 @@ bool Process::IsSignaled() const {
Process::Process(Core::System& system)
: KSynchronizationObject{system.Kernel()},
page_table{std::make_unique<Memory::PageTable>(system)}, handle_table{system.Kernel()},
address_arbiter{system}, condition_var{system}, system{system} {}
address_arbiter{system}, condition_var{system}, state_lock{system.Kernel()}, system{system} {}
Process::~Process() = default;

View File

@@ -29,8 +29,8 @@ class ProgramMetadata;
namespace Kernel {
class KernelCore;
class ResourceLimit;
class Thread;
class KResourceLimit;
class KThread;
class TLSPage;
struct CodeSet;
@@ -170,13 +170,18 @@ public:
}
/// Gets the resource limit descriptor for this process
std::shared_ptr<ResourceLimit> GetResourceLimit() const;
std::shared_ptr<KResourceLimit> GetResourceLimit() const;
/// Gets the ideal CPU core ID for this process
u8 GetIdealCore() const {
u8 GetIdealCoreId() const {
return ideal_core;
}
/// Checks if the specified thread priority is valid.
bool CheckThreadPriority(s32 prio) const {
return ((1ULL << prio) & GetPriorityMask()) != 0;
}
/// Gets the bitmask of allowed cores that this process' threads can run on.
u64 GetCoreMask() const {
return capabilities.GetCoreMask();
@@ -212,6 +217,14 @@ public:
return is_64bit_process;
}
[[nodiscard]] bool IsSuspended() const {
return is_suspended;
}
void SetSuspended(bool suspended) {
is_suspended = suspended;
}
/// Gets the total running time of the process instance in ticks.
u64 GetCPUTimeTicks() const {
return total_process_running_time_ticks;
@@ -232,6 +245,33 @@ public:
++schedule_count;
}
void IncrementThreadCount();
void DecrementThreadCount();
void SetRunningThread(s32 core, KThread* thread, u64 idle_count) {
running_threads[core] = thread;
running_thread_idle_counts[core] = idle_count;
}
void ClearRunningThread(KThread* thread) {
for (size_t i = 0; i < running_threads.size(); ++i) {
if (running_threads[i] == thread) {
running_threads[i] = nullptr;
}
}
}
[[nodiscard]] KThread* GetRunningThread(s32 core) const {
return running_threads[core];
}
bool ReleaseUserException(KThread* thread);
[[nodiscard]] KThread* GetPinnedThread(s32 core_id) const {
ASSERT(0 <= core_id && core_id < static_cast<s32>(Core::Hardware::NUM_CPU_CORES));
return pinned_threads[core_id];
}
/// Gets 8 bytes of random data for svcGetInfo RandomEntropy
u64 GetRandomEntropy(std::size_t index) const {
return random_entropy.at(index);
@@ -252,17 +292,17 @@ public:
u64 GetTotalPhysicalMemoryUsedWithoutSystemResource() const;
/// Gets the list of all threads created with this process as their owner.
const std::list<const Thread*>& GetThreadList() const {
const std::list<const KThread*>& GetThreadList() const {
return thread_list;
}
/// Registers a thread as being created under this process,
/// adding it to this process' thread list.
void RegisterThread(const Thread* thread);
void RegisterThread(const KThread* thread);
/// Unregisters a thread from this process, removing it
/// from this process' thread list.
void UnregisterThread(const Thread* thread);
void UnregisterThread(const KThread* thread);
/// Clears the signaled state of the process if and only if it's signaled.
///
@@ -272,7 +312,7 @@ public:
/// @pre The process must be in a signaled state. If this is called on a
/// process instance that is not signaled, ERR_INVALID_STATE will be
/// returned.
ResultCode ClearSignalState();
ResultCode Reset();
/**
* Loads process-specifics configuration info with metadata provided
@@ -303,6 +343,15 @@ public:
bool IsSignaled() const override;
void Finalize() override {}
void PinCurrentThread();
void UnpinCurrentThread();
KLightLock& GetStateLock() {
return state_lock;
}
///////////////////////////////////////////////////////////////////////////////////////////////
// Thread-local storage management
@@ -313,6 +362,20 @@ public:
void FreeTLSRegion(VAddr tls_address);
private:
void PinThread(s32 core_id, KThread* thread) {
ASSERT(0 <= core_id && core_id < static_cast<s32>(Core::Hardware::NUM_CPU_CORES));
ASSERT(thread != nullptr);
ASSERT(pinned_threads[core_id] == nullptr);
pinned_threads[core_id] = thread;
}
void UnpinThread(s32 core_id, KThread* thread) {
ASSERT(0 <= core_id && core_id < static_cast<s32>(Core::Hardware::NUM_CPU_CORES));
ASSERT(thread != nullptr);
ASSERT(pinned_threads[core_id] == thread);
pinned_threads[core_id] = nullptr;
}
/// Changes the process status. If the status is different
/// from the current process status, then this will trigger
/// a process signal.
@@ -339,7 +402,7 @@ private:
u32 system_resource_size = 0;
/// Resource limit descriptor for this process
std::shared_ptr<ResourceLimit> resource_limit;
std::shared_ptr<KResourceLimit> resource_limit;
/// The ideal CPU core for this process, threads are scheduled on this core by default.
u8 ideal_core = 0;
@@ -380,7 +443,7 @@ private:
std::array<u64, RANDOM_ENTROPY_SIZE> random_entropy{};
/// List of threads that are running with this process as their owner.
std::list<const Thread*> thread_list;
std::list<const KThread*> thread_list;
/// Address of the top of the main thread's stack
VAddr main_thread_stack_top{};
@@ -401,6 +464,19 @@ private:
s64 schedule_count{};
bool is_signaled{};
bool is_suspended{};
std::atomic<s32> num_created_threads{};
std::atomic<u16> num_threads{};
u16 peak_num_threads{};
std::array<KThread*, Core::Hardware::NUM_CPU_CORES> running_threads{};
std::array<u64, Core::Hardware::NUM_CPU_CORES> running_thread_idle_counts{};
std::array<KThread*, Core::Hardware::NUM_CPU_CORES> pinned_threads{};
KThread* exception_thread{};
KLightLock state_lock;
/// System context
Core::System& system;

View File

@@ -1,52 +0,0 @@
// Copyright 2014 Citra Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include <algorithm>
#include "common/assert.h"
#include "common/logging/log.h"
#include "core/hle/kernel/errors.h"
#include "core/hle/kernel/k_scheduler.h"
#include "core/hle/kernel/kernel.h"
#include "core/hle/kernel/object.h"
#include "core/hle/kernel/readable_event.h"
#include "core/hle/kernel/thread.h"
namespace Kernel {
ReadableEvent::ReadableEvent(KernelCore& kernel) : KSynchronizationObject{kernel} {}
ReadableEvent::~ReadableEvent() = default;
void ReadableEvent::Signal() {
if (is_signaled) {
return;
}
is_signaled = true;
NotifyAvailable();
}
bool ReadableEvent::IsSignaled() const {
ASSERT(kernel.GlobalSchedulerContext().IsLocked());
return is_signaled;
}
void ReadableEvent::Clear() {
is_signaled = false;
}
ResultCode ReadableEvent::Reset() {
KScopedSchedulerLock lock(kernel);
if (!is_signaled) {
LOG_TRACE(Kernel, "Handle is not signaled! object_id={}, object_type={}, object_name={}",
GetObjectId(), GetTypeName(), GetName());
return ERR_INVALID_STATE;
}
Clear();
return RESULT_SUCCESS;
}
} // namespace Kernel

View File

@@ -1,57 +0,0 @@
// Copyright 2014 Citra Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include "core/hle/kernel/k_synchronization_object.h"
#include "core/hle/kernel/object.h"
union ResultCode;
namespace Kernel {
class KernelCore;
class WritableEvent;
class ReadableEvent final : public KSynchronizationObject {
friend class WritableEvent;
public:
~ReadableEvent() override;
std::string GetTypeName() const override {
return "ReadableEvent";
}
std::string GetName() const override {
return name;
}
static constexpr HandleType HANDLE_TYPE = HandleType::ReadableEvent;
HandleType GetHandleType() const override {
return HANDLE_TYPE;
}
/// Unconditionally clears the readable event's state.
void Clear();
/// Clears the readable event's state if and only if it
/// has already been signaled.
///
/// @pre The event must be in a signaled state. If this event
/// is in an unsignaled state and this function is called,
/// then ERR_INVALID_STATE will be returned.
ResultCode Reset();
void Signal();
bool IsSignaled() const override;
private:
explicit ReadableEvent(KernelCore& kernel);
bool is_signaled{};
std::string name; ///< Name of event (optional)
};
} // namespace Kernel

View File

@@ -1,73 +0,0 @@
// Copyright 2015 Citra Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include "core/hle/kernel/errors.h"
#include "core/hle/kernel/resource_limit.h"
#include "core/hle/result.h"
namespace Kernel {
namespace {
constexpr std::size_t ResourceTypeToIndex(ResourceType type) {
return static_cast<std::size_t>(type);
}
} // Anonymous namespace
ResourceLimit::ResourceLimit(KernelCore& kernel) : Object{kernel} {}
ResourceLimit::~ResourceLimit() = default;
bool ResourceLimit::Reserve(ResourceType resource, s64 amount) {
return Reserve(resource, amount, 10000000000);
}
bool ResourceLimit::Reserve(ResourceType resource, s64 amount, u64 timeout) {
const std::size_t index{ResourceTypeToIndex(resource)};
s64 new_value = current[index] + amount;
if (new_value > limit[index] && available[index] + amount <= limit[index]) {
// TODO(bunnei): This is wrong for multicore, we should wait the calling thread for timeout
new_value = current[index] + amount;
}
if (new_value <= limit[index]) {
current[index] = new_value;
return true;
}
return false;
}
void ResourceLimit::Release(ResourceType resource, u64 amount) {
Release(resource, amount, amount);
}
void ResourceLimit::Release(ResourceType resource, u64 used_amount, u64 available_amount) {
const std::size_t index{ResourceTypeToIndex(resource)};
current[index] -= used_amount;
available[index] -= available_amount;
}
std::shared_ptr<ResourceLimit> ResourceLimit::Create(KernelCore& kernel) {
return std::make_shared<ResourceLimit>(kernel);
}
s64 ResourceLimit::GetCurrentResourceValue(ResourceType resource) const {
return limit.at(ResourceTypeToIndex(resource)) - current.at(ResourceTypeToIndex(resource));
}
s64 ResourceLimit::GetMaxResourceValue(ResourceType resource) const {
return limit.at(ResourceTypeToIndex(resource));
}
ResultCode ResourceLimit::SetLimitValue(ResourceType resource, s64 value) {
const std::size_t index{ResourceTypeToIndex(resource)};
if (current[index] <= value) {
limit[index] = value;
return RESULT_SUCCESS;
} else {
LOG_ERROR(Kernel, "Limit value is too large! resource={}, value={}, index={}", resource,
value, index);
return ERR_INVALID_STATE;
}
}
} // namespace Kernel

View File

@@ -1,104 +0,0 @@
// Copyright 2015 Citra Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include <array>
#include <memory>
#include "common/common_types.h"
#include "core/hle/kernel/object.h"
union ResultCode;
namespace Kernel {
class KernelCore;
enum class ResourceType : u32 {
PhysicalMemory,
Threads,
Events,
TransferMemory,
Sessions,
// Used as a count, not an actual type.
ResourceTypeCount
};
constexpr bool IsValidResourceType(ResourceType type) {
return type < ResourceType::ResourceTypeCount;
}
class ResourceLimit final : public Object {
public:
explicit ResourceLimit(KernelCore& kernel);
~ResourceLimit() override;
/// Creates a resource limit object.
static std::shared_ptr<ResourceLimit> Create(KernelCore& kernel);
std::string GetTypeName() const override {
return "ResourceLimit";
}
std::string GetName() const override {
return GetTypeName();
}
static constexpr HandleType HANDLE_TYPE = HandleType::ResourceLimit;
HandleType GetHandleType() const override {
return HANDLE_TYPE;
}
bool Reserve(ResourceType resource, s64 amount);
bool Reserve(ResourceType resource, s64 amount, u64 timeout);
void Release(ResourceType resource, u64 amount);
void Release(ResourceType resource, u64 used_amount, u64 available_amount);
/**
* Gets the current value for the specified resource.
* @param resource Requested resource type
* @returns The current value of the resource type
*/
s64 GetCurrentResourceValue(ResourceType resource) const;
/**
* Gets the max value for the specified resource.
* @param resource Requested resource type
* @returns The max value of the resource type
*/
s64 GetMaxResourceValue(ResourceType resource) const;
/**
* Sets the limit value for a given resource type.
*
* @param resource The resource type to apply the limit to.
* @param value The limit to apply to the given resource type.
*
* @return A result code indicating if setting the limit value
* was successful or not.
*
* @note The supplied limit value *must* be greater than or equal to
* the current resource value for the given resource type,
* otherwise ERR_INVALID_STATE will be returned.
*/
ResultCode SetLimitValue(ResourceType resource, s64 value);
private:
// TODO(Subv): Increment resource limit current values in their respective Kernel::T::Create
// functions
//
// Currently we have no way of distinguishing if a Create was called by the running application,
// or by a service module. Approach this once we have separated the service modules into their
// own processes
using ResourceArray =
std::array<s64, static_cast<std::size_t>(ResourceType::ResourceTypeCount)>;
ResourceArray limit{};
ResourceArray current{};
ResourceArray available{};
};
} // namespace Kernel

View File

@@ -6,10 +6,10 @@
#include "common/assert.h"
#include "core/hle/kernel/client_port.h"
#include "core/hle/kernel/errors.h"
#include "core/hle/kernel/k_thread.h"
#include "core/hle/kernel/object.h"
#include "core/hle/kernel/server_port.h"
#include "core/hle/kernel/server_session.h"
#include "core/hle/kernel/thread.h"
namespace Kernel {

View File

@@ -81,6 +81,8 @@ public:
bool IsSignaled() const override;
void Finalize() override {}
private:
/// ServerSessions waiting to be accepted by the port
std::vector<std::shared_ptr<ServerSession>> pending_sessions;

View File

@@ -15,11 +15,11 @@
#include "core/hle/kernel/handle_table.h"
#include "core/hle/kernel/hle_ipc.h"
#include "core/hle/kernel/k_scheduler.h"
#include "core/hle/kernel/k_thread.h"
#include "core/hle/kernel/kernel.h"
#include "core/hle/kernel/process.h"
#include "core/hle/kernel/server_session.h"
#include "core/hle/kernel/session.h"
#include "core/hle/kernel/thread.h"
#include "core/memory.h"
namespace Kernel {
@@ -116,7 +116,7 @@ ResultCode ServerSession::HandleDomainSyncRequest(Kernel::HLERequestContext& con
return RESULT_SUCCESS;
}
ResultCode ServerSession::QueueSyncRequest(std::shared_ptr<Thread> thread,
ResultCode ServerSession::QueueSyncRequest(std::shared_ptr<KThread> thread,
Core::Memory::Memory& memory) {
u32* cmd_buf{reinterpret_cast<u32*>(memory.GetPointer(thread->GetTLSAddress()))};
auto context =
@@ -154,14 +154,14 @@ ResultCode ServerSession::CompleteSyncRequest(HLERequestContext& context) {
KScopedSchedulerLock lock(kernel);
if (!context.IsThreadWaiting()) {
context.GetThread().Wakeup();
context.GetThread().SetSynchronizationResults(nullptr, result);
context.GetThread().SetSyncedObject(nullptr, result);
}
}
return result;
}
ResultCode ServerSession::HandleSyncRequest(std::shared_ptr<Thread> thread,
ResultCode ServerSession::HandleSyncRequest(std::shared_ptr<KThread> thread,
Core::Memory::Memory& memory,
Core::Timing::CoreTiming& core_timing) {
return QueueSyncRequest(std::move(thread), memory);

Some files were not shown because too many files have changed in this diff Show More