summaryrefslogtreecommitdiff
path: root/src/core/hle/kernel
diff options
context:
space:
mode:
Diffstat (limited to 'src/core/hle/kernel')
-rw-r--r--src/core/hle/kernel/address_arbiter.cpp313
-rw-r--r--src/core/hle/kernel/address_arbiter.h44
-rw-r--r--src/core/hle/kernel/thread.h4
3 files changed, 181 insertions, 180 deletions
diff --git a/src/core/hle/kernel/address_arbiter.cpp b/src/core/hle/kernel/address_arbiter.cpp
index 01c5bf61b..972911e42 100644
--- a/src/core/hle/kernel/address_arbiter.cpp
+++ b/src/core/hle/kernel/address_arbiter.cpp
@@ -14,160 +14,161 @@
14#include "core/memory.h" 14#include "core/memory.h"
15 15
16namespace Kernel { 16namespace Kernel {
17 namespace AddressArbiter { 17namespace AddressArbiter {
18 18
19 // Performs actual address waiting logic. 19// Performs actual address waiting logic.
20 ResultCode WaitForAddress(VAddr address, s64 timeout) { 20ResultCode WaitForAddress(VAddr address, s64 timeout) {
21 SharedPtr<Thread> current_thread = GetCurrentThread(); 21 SharedPtr<Thread> current_thread = GetCurrentThread();
22 current_thread->arb_wait_address = address; 22 current_thread->arb_wait_address = address;
23 current_thread->status = THREADSTATUS_WAIT_ARB; 23 current_thread->status = THREADSTATUS_WAIT_ARB;
24 current_thread->wakeup_callback = nullptr; 24 current_thread->wakeup_callback = nullptr;
25 25
26 current_thread->WakeAfterDelay(timeout); 26 current_thread->WakeAfterDelay(timeout);
27 27
28 Core::System::GetInstance().CpuCore(current_thread->processor_id).PrepareReschedule(); 28 Core::System::GetInstance().CpuCore(current_thread->processor_id).PrepareReschedule();
29 // This should never actually execute. 29 // This should never actually execute.
30 return RESULT_SUCCESS; 30 return RESULT_SUCCESS;
31 } 31}
32 32
33 // Gets the threads waiting on an address. 33// Gets the threads waiting on an address.
34 void GetThreadsWaitingOnAddress(std::vector<SharedPtr<Thread>> &waiting_threads, VAddr address) { 34void GetThreadsWaitingOnAddress(std::vector<SharedPtr<Thread>>& waiting_threads, VAddr address) {
35 auto RetrieveWaitingThreads = 35 auto RetrieveWaitingThreads =
36 [](size_t core_index, std::vector<SharedPtr<Thread>>& waiting_threads, VAddr arb_addr) { 36 [](size_t core_index, std::vector<SharedPtr<Thread>>& waiting_threads, VAddr arb_addr) {
37 const auto& scheduler = Core::System::GetInstance().Scheduler(core_index); 37 const auto& scheduler = Core::System::GetInstance().Scheduler(core_index);
38 auto& thread_list = scheduler->GetThreadList(); 38 auto& thread_list = scheduler->GetThreadList();
39 39
40 for (auto& thread : thread_list) { 40 for (auto& thread : thread_list) {
41 if (thread->arb_wait_address == arb_addr) 41 if (thread->arb_wait_address == arb_addr)
42 waiting_threads.push_back(thread); 42 waiting_threads.push_back(thread);
43 }
44 };
45
46 // Retrieve a list of all threads that are waiting for this address.
47 RetrieveWaitingThreads(0, waiting_threads, address);
48 RetrieveWaitingThreads(1, waiting_threads, address);
49 RetrieveWaitingThreads(2, waiting_threads, address);
50 RetrieveWaitingThreads(3, waiting_threads, address);
51 // Sort them by priority, such that the highest priority ones come first.
52 std::sort(waiting_threads.begin(), waiting_threads.end(),
53 [](const SharedPtr<Thread>& lhs, const SharedPtr<Thread>& rhs) {
54 return lhs->current_priority < rhs->current_priority;
55 });
56 }
57
58 // Wake up num_to_wake (or all) threads in a vector.
59 void WakeThreads(std::vector<SharedPtr<Thread>> &waiting_threads, s32 num_to_wake) {
60 // Only process up to 'target' threads, unless 'target' is <= 0, in which case process
61 // them all.
62 size_t last = waiting_threads.size();
63 if (num_to_wake > 0)
64 last = num_to_wake;
65
66 // Signal the waiting threads.
67 // TODO: Rescheduling should not occur while waking threads. How can it be prevented?
68 for (size_t i = 0; i < last; i++) {
69 ASSERT(waiting_threads[i]->status = THREADSTATUS_WAIT_ARB);
70 waiting_threads[i]->SetWaitSynchronizationResult(RESULT_SUCCESS);
71 waiting_threads[i]->arb_wait_address = 0;
72 waiting_threads[i]->ResumeFromWait();
73 } 43 }
74 44 };
75 } 45
76 46 // Retrieve a list of all threads that are waiting for this address.
77 // Signals an address being waited on. 47 RetrieveWaitingThreads(0, waiting_threads, address);
78 ResultCode SignalToAddress(VAddr address, s32 num_to_wake) { 48 RetrieveWaitingThreads(1, waiting_threads, address);
79 // Get threads waiting on the address. 49 RetrieveWaitingThreads(2, waiting_threads, address);
80 std::vector<SharedPtr<Thread>> waiting_threads; 50 RetrieveWaitingThreads(3, waiting_threads, address);
81 GetThreadsWaitingOnAddress(waiting_threads, address); 51 // Sort them by priority, such that the highest priority ones come first.
82 52 std::sort(waiting_threads.begin(), waiting_threads.end(),
83 WakeThreads(waiting_threads, num_to_wake); 53 [](const SharedPtr<Thread>& lhs, const SharedPtr<Thread>& rhs) {
84 return RESULT_SUCCESS; 54 return lhs->current_priority < rhs->current_priority;
85 } 55 });
86 56}
87 // Signals an address being waited on and increments its value if equal to the value argument. 57
88 ResultCode IncrementAndSignalToAddressIfEqual(VAddr address, s32 value, s32 num_to_wake) { 58// Wake up num_to_wake (or all) threads in a vector.
89 // Ensure that we can write to the address. 59void WakeThreads(std::vector<SharedPtr<Thread>>& waiting_threads, s32 num_to_wake) {
90 if (!Memory::IsValidVirtualAddress(address)) { 60 // Only process up to 'target' threads, unless 'target' is <= 0, in which case process
91 return ERR_INVALID_ADDRESS_STATE; 61 // them all.
92 } 62 size_t last = waiting_threads.size();
93 63 if (num_to_wake > 0)
94 if ((s32)Memory::Read32(address) == value) { 64 last = num_to_wake;
95 Memory::Write32(address, (u32)(value + 1)); 65
96 } else { 66 // Signal the waiting threads.
97 return ERR_INVALID_STATE; 67 // TODO: Rescheduling should not occur while waking threads. How can it be prevented?
98 } 68 for (size_t i = 0; i < last; i++) {
99 69 ASSERT(waiting_threads[i]->status = THREADSTATUS_WAIT_ARB);
100 return SignalToAddress(address, num_to_wake); 70 waiting_threads[i]->SetWaitSynchronizationResult(RESULT_SUCCESS);
101 } 71 waiting_threads[i]->arb_wait_address = 0;
102 72 waiting_threads[i]->ResumeFromWait();
103 // Signals an address being waited on and modifies its value based on waiting thread count if equal to the value argument. 73 }
104 ResultCode ModifyByWaitingCountAndSignalToAddressIfEqual(VAddr address, s32 value, s32 num_to_wake) { 74}
105 // Ensure that we can write to the address. 75
106 if (!Memory::IsValidVirtualAddress(address)) { 76// Signals an address being waited on.
107 return ERR_INVALID_ADDRESS_STATE; 77ResultCode SignalToAddress(VAddr address, s32 num_to_wake) {
108 } 78 // Get threads waiting on the address.
109 79 std::vector<SharedPtr<Thread>> waiting_threads;
110 // Get threads waiting on the address. 80 GetThreadsWaitingOnAddress(waiting_threads, address);
111 std::vector<SharedPtr<Thread>> waiting_threads; 81
112 GetThreadsWaitingOnAddress(waiting_threads, address); 82 WakeThreads(waiting_threads, num_to_wake);
113 83 return RESULT_SUCCESS;
114 // Determine the modified value depending on the waiting count. 84}
115 s32 updated_value; 85
116 if (waiting_threads.size() == 0) { 86// Signals an address being waited on and increments its value if equal to the value argument.
117 updated_value = value - 1; 87ResultCode IncrementAndSignalToAddressIfEqual(VAddr address, s32 value, s32 num_to_wake) {
118 } else if (num_to_wake <= 0 || waiting_threads.size() <= num_to_wake) { 88 // Ensure that we can write to the address.
119 updated_value = value + 1; 89 if (!Memory::IsValidVirtualAddress(address)) {
120 } else { 90 return ERR_INVALID_ADDRESS_STATE;
121 updated_value = value; 91 }
122 } 92
123 93 if ((s32)Memory::Read32(address) == value) {
124 if ((s32)Memory::Read32(address) == value) { 94 Memory::Write32(address, (u32)(value + 1));
125 Memory::Write32(address, (u32)(updated_value)); 95 } else {
126 } else { 96 return ERR_INVALID_STATE;
127 return ERR_INVALID_STATE; 97 }
128 } 98
129 99 return SignalToAddress(address, num_to_wake);
130 WakeThreads(waiting_threads, num_to_wake); 100}
131 return RESULT_SUCCESS; 101
132 } 102// Signals an address being waited on and modifies its value based on waiting thread count if equal
133 103// to the value argument.
134 // Waits on an address if the value passed is less than the argument value, optionally decrementing. 104ResultCode ModifyByWaitingCountAndSignalToAddressIfEqual(VAddr address, s32 value,
135 ResultCode WaitForAddressIfLessThan(VAddr address, s32 value, s64 timeout, bool should_decrement) { 105 s32 num_to_wake) {
136 // Ensure that we can read the address. 106 // Ensure that we can write to the address.
137 if (!Memory::IsValidVirtualAddress(address)) { 107 if (!Memory::IsValidVirtualAddress(address)) {
138 return ERR_INVALID_ADDRESS_STATE; 108 return ERR_INVALID_ADDRESS_STATE;
139 } 109 }
140 110
141 s32 cur_value = (s32)Memory::Read32(address); 111 // Get threads waiting on the address.
142 if (cur_value < value) { 112 std::vector<SharedPtr<Thread>> waiting_threads;
143 Memory::Write32(address, (u32)(cur_value - 1)); 113 GetThreadsWaitingOnAddress(waiting_threads, address);
144 } else { 114
145 return ERR_INVALID_STATE; 115 // Determine the modified value depending on the waiting count.
146 } 116 s32 updated_value;
147 // Short-circuit without rescheduling, if timeout is zero. 117 if (waiting_threads.size() == 0) {
148 if (timeout == 0) { 118 updated_value = value - 1;
149 return RESULT_TIMEOUT; 119 } else if (num_to_wake <= 0 || waiting_threads.size() <= num_to_wake) {
150 } 120 updated_value = value + 1;
151 121 } else {
152 return WaitForAddress(address, timeout); 122 updated_value = value;
153 } 123 }
154 124
155 // Waits on an address if the value passed is equal to the argument value. 125 if ((s32)Memory::Read32(address) == value) {
156 ResultCode WaitForAddressIfEqual(VAddr address, s32 value, s64 timeout) { 126 Memory::Write32(address, (u32)(updated_value));
157 // Ensure that we can read the address. 127 } else {
158 if (!Memory::IsValidVirtualAddress(address)) { 128 return ERR_INVALID_STATE;
159 return ERR_INVALID_ADDRESS_STATE; 129 }
160 } 130
161 // Only wait for the address if equal. 131 WakeThreads(waiting_threads, num_to_wake);
162 if ((s32)Memory::Read32(address) != value) { 132 return RESULT_SUCCESS;
163 return ERR_INVALID_STATE; 133}
164 } 134
165 // Short-circuit without rescheduling, if timeout is zero. 135// Waits on an address if the value passed is less than the argument value, optionally decrementing.
166 if (timeout == 0) { 136ResultCode WaitForAddressIfLessThan(VAddr address, s32 value, s64 timeout, bool should_decrement) {
167 return RESULT_TIMEOUT; 137 // Ensure that we can read the address.
168 } 138 if (!Memory::IsValidVirtualAddress(address)) {
169 139 return ERR_INVALID_ADDRESS_STATE;
170 return WaitForAddress(address, timeout); 140 }
171 } 141
172 } // namespace AddressArbiter 142 s32 cur_value = (s32)Memory::Read32(address);
173} // namespace Kernel \ No newline at end of file 143 if (cur_value < value) {
144 Memory::Write32(address, (u32)(cur_value - 1));
145 } else {
146 return ERR_INVALID_STATE;
147 }
148 // Short-circuit without rescheduling, if timeout is zero.
149 if (timeout == 0) {
150 return RESULT_TIMEOUT;
151 }
152
153 return WaitForAddress(address, timeout);
154}
155
156// Waits on an address if the value passed is equal to the argument value.
157ResultCode WaitForAddressIfEqual(VAddr address, s32 value, s64 timeout) {
158 // Ensure that we can read the address.
159 if (!Memory::IsValidVirtualAddress(address)) {
160 return ERR_INVALID_ADDRESS_STATE;
161 }
162 // Only wait for the address if equal.
163 if ((s32)Memory::Read32(address) != value) {
164 return ERR_INVALID_STATE;
165 }
166 // Short-circuit without rescheduling, if timeout is zero.
167 if (timeout == 0) {
168 return RESULT_TIMEOUT;
169 }
170
171 return WaitForAddress(address, timeout);
172}
173} // namespace AddressArbiter
174} // namespace Kernel
diff --git a/src/core/hle/kernel/address_arbiter.h b/src/core/hle/kernel/address_arbiter.h
index 32d4a77a9..f20f3dbc0 100644
--- a/src/core/hle/kernel/address_arbiter.h
+++ b/src/core/hle/kernel/address_arbiter.h
@@ -8,25 +8,25 @@
8 8
9namespace Kernel { 9namespace Kernel {
10 10
11 namespace AddressArbiter { 11namespace AddressArbiter {
12 enum class ArbitrationType { 12enum class ArbitrationType {
13 WaitIfLessThan = 0, 13 WaitIfLessThan = 0,
14 DecrementAndWaitIfLessThan = 1, 14 DecrementAndWaitIfLessThan = 1,
15 WaitIfEqual = 2, 15 WaitIfEqual = 2,
16 }; 16};
17 17
18 enum class SignalType { 18enum class SignalType {
19 Signal = 0, 19 Signal = 0,
20 IncrementAndSignalIfEqual = 1, 20 IncrementAndSignalIfEqual = 1,
21 ModifyByWaitingCountAndSignalIfEqual = 2, 21 ModifyByWaitingCountAndSignalIfEqual = 2,
22 }; 22};
23 23
24 ResultCode SignalToAddress(VAddr address, s32 num_to_wake); 24ResultCode SignalToAddress(VAddr address, s32 num_to_wake);
25 ResultCode IncrementAndSignalToAddressIfEqual(VAddr address, s32 value, s32 num_to_wake); 25ResultCode IncrementAndSignalToAddressIfEqual(VAddr address, s32 value, s32 num_to_wake);
26 ResultCode ModifyByWaitingCountAndSignalToAddressIfEqual(VAddr address, s32 value, s32 num_to_wake); 26ResultCode ModifyByWaitingCountAndSignalToAddressIfEqual(VAddr address, s32 value, s32 num_to_wake);
27 27
28 ResultCode WaitForAddressIfLessThan(VAddr address, s32 value, s64 timeout, bool should_decrement); 28ResultCode WaitForAddressIfLessThan(VAddr address, s32 value, s64 timeout, bool should_decrement);
29 ResultCode WaitForAddressIfEqual(VAddr address, s32 value, s64 timeout); 29ResultCode WaitForAddressIfEqual(VAddr address, s32 value, s64 timeout);
30 } // namespace AddressArbiter 30} // namespace AddressArbiter
31 31
32} // namespace Kernel \ No newline at end of file 32} // namespace Kernel
diff --git a/src/core/hle/kernel/thread.h b/src/core/hle/kernel/thread.h
index 3851d1085..f1e759802 100644
--- a/src/core/hle/kernel/thread.h
+++ b/src/core/hle/kernel/thread.h
@@ -228,8 +228,8 @@ public:
228 228
229 // If waiting on a ConditionVariable, this is the ConditionVariable address 229 // If waiting on a ConditionVariable, this is the ConditionVariable address
230 VAddr condvar_wait_address; 230 VAddr condvar_wait_address;
231 VAddr mutex_wait_address; ///< If waiting on a Mutex, this is the mutex address 231 VAddr mutex_wait_address; ///< If waiting on a Mutex, this is the mutex address
232 Handle wait_handle; ///< The handle used to wait for the mutex. 232 Handle wait_handle; ///< The handle used to wait for the mutex.
233 233
234 // If waiting for an AddressArbiter, this is the address being waited on. 234 // If waiting for an AddressArbiter, this is the address being waited on.
235 VAddr arb_wait_address{0}; 235 VAddr arb_wait_address{0};