summaryrefslogtreecommitdiff
path: root/src/core/hle/kernel/thread.cpp
diff options
context:
space:
mode:
authorGravatar bunnei2015-01-21 21:09:47 -0500
committerGravatar bunnei2015-01-21 21:09:47 -0500
commit24a63662ba6c7816001bba399e85d8c131a89489 (patch)
treea9959e69723b4f19550834171c962ec06c9e34b7 /src/core/hle/kernel/thread.cpp
parentMerge pull request #491 from archshift/hidspvr (diff)
parentWaitSynchronization: Added a result code for invalid result, fixed bug. (diff)
downloadyuzu-24a63662ba6c7816001bba399e85d8c131a89489.tar.gz
yuzu-24a63662ba6c7816001bba399e85d8c131a89489.tar.xz
yuzu-24a63662ba6c7816001bba399e85d8c131a89489.zip
Merge pull request #495 from bunnei/fix-waitsynch
Fix WaitSynchronization
Diffstat (limited to 'src/core/hle/kernel/thread.cpp')
-rw-r--r--src/core/hle/kernel/thread.cpp172
1 files changed, 113 insertions, 59 deletions
diff --git a/src/core/hle/kernel/thread.cpp b/src/core/hle/kernel/thread.cpp
index bc86a7c59..03b492c75 100644
--- a/src/core/hle/kernel/thread.cpp
+++ b/src/core/hle/kernel/thread.cpp
@@ -22,17 +22,12 @@
22 22
23namespace Kernel { 23namespace Kernel {
24 24
25ResultVal<bool> Thread::WaitSynchronization() { 25bool Thread::ShouldWait() {
26 const bool wait = status != THREADSTATUS_DORMANT; 26 return status != THREADSTATUS_DORMANT;
27 if (wait) { 27}
28 Thread* thread = GetCurrentThread();
29 if (std::find(waiting_threads.begin(), waiting_threads.end(), thread) == waiting_threads.end()) {
30 waiting_threads.push_back(thread);
31 }
32 WaitCurrentThread(WAITTYPE_THREADEND, this);
33 }
34 28
35 return MakeResult<bool>(wait); 29void Thread::Acquire() {
30 _assert_msg_(Kernel, !ShouldWait(), "object unavailable!");
36} 31}
37 32
38// Lists all thread ids that aren't deleted/etc. 33// Lists all thread ids that aren't deleted/etc.
@@ -67,8 +62,8 @@ static void ResetThread(Thread* t, u32 arg, s32 lowest_priority) {
67 if (t->current_priority < lowest_priority) { 62 if (t->current_priority < lowest_priority) {
68 t->current_priority = t->initial_priority; 63 t->current_priority = t->initial_priority;
69 } 64 }
70 t->wait_type = WAITTYPE_NONE; 65
71 t->wait_object = nullptr; 66 t->wait_objects.clear();
72 t->wait_address = 0; 67 t->wait_address = 0;
73} 68}
74 69
@@ -88,37 +83,32 @@ static void ChangeReadyState(Thread* t, bool ready) {
88 } 83 }
89} 84}
90 85
91/// Check if a thread is blocking on a specified wait type 86/// Check if a thread is waiting on a the specified wait object
92static bool CheckWaitType(const Thread* thread, WaitType type) { 87static bool CheckWait_WaitObject(const Thread* thread, WaitObject* wait_object) {
93 return (type == thread->wait_type) && (thread->IsWaiting()); 88 auto itr = std::find(thread->wait_objects.begin(), thread->wait_objects.end(), wait_object);
94} 89
90 if (itr != thread->wait_objects.end())
91 return thread->IsWaiting();
95 92
96/// Check if a thread is blocking on a specified wait type with a specified handle 93 return false;
97static bool CheckWaitType(const Thread* thread, WaitType type, Object* wait_object) {
98 return CheckWaitType(thread, type) && wait_object == thread->wait_object;
99} 94}
100 95
101/// Check if a thread is blocking on a specified wait type with a specified handle and address 96/// Check if the specified thread is waiting on the specified address to be arbitrated
102static bool CheckWaitType(const Thread* thread, WaitType type, Object* wait_object, VAddr wait_address) { 97static bool CheckWait_AddressArbiter(const Thread* thread, VAddr wait_address) {
103 return CheckWaitType(thread, type, wait_object) && (wait_address == thread->wait_address); 98 return thread->IsWaiting() && thread->wait_objects.empty() && wait_address == thread->wait_address;
104} 99}
105 100
106/// Stops the current thread 101/// Stops the current thread
107void Thread::Stop(const char* reason) { 102void Thread::Stop(const char* reason) {
108 // Release all the mutexes that this thread holds 103 // Release all the mutexes that this thread holds
109 ReleaseThreadMutexes(GetHandle()); 104 ReleaseThreadMutexes(this);
110 105
111 ChangeReadyState(this, false); 106 ChangeReadyState(this, false);
112 status = THREADSTATUS_DORMANT; 107 status = THREADSTATUS_DORMANT;
113 for (auto& waiting_thread : waiting_threads) { 108 WakeupAllWaitingThreads();
114 if (CheckWaitType(waiting_thread.get(), WAITTYPE_THREADEND, this))
115 waiting_thread->ResumeFromWait();
116 }
117 waiting_threads.clear();
118 109
119 // Stopped threads are never waiting. 110 // Stopped threads are never waiting.
120 wait_type = WAITTYPE_NONE; 111 wait_objects.clear();
121 wait_object = nullptr;
122 wait_address = 0; 112 wait_address = 0;
123} 113}
124 114
@@ -129,26 +119,20 @@ static void ChangeThreadState(Thread* t, ThreadStatus new_status) {
129 } 119 }
130 ChangeReadyState(t, (new_status & THREADSTATUS_READY) != 0); 120 ChangeReadyState(t, (new_status & THREADSTATUS_READY) != 0);
131 t->status = new_status; 121 t->status = new_status;
132
133 if (new_status == THREADSTATUS_WAIT) {
134 if (t->wait_type == WAITTYPE_NONE) {
135 LOG_ERROR(Kernel, "Waittype none not allowed");
136 }
137 }
138} 122}
139 123
140/// Arbitrate the highest priority thread that is waiting 124/// Arbitrate the highest priority thread that is waiting
141Thread* ArbitrateHighestPriorityThread(Object* arbiter, u32 address) { 125Thread* ArbitrateHighestPriorityThread(u32 address) {
142 Thread* highest_priority_thread = nullptr; 126 Thread* highest_priority_thread = nullptr;
143 s32 priority = THREADPRIO_LOWEST; 127 s32 priority = THREADPRIO_LOWEST;
144 128
145 // Iterate through threads, find highest priority thread that is waiting to be arbitrated... 129 // Iterate through threads, find highest priority thread that is waiting to be arbitrated...
146 for (auto& thread : thread_list) { 130 for (auto& thread : thread_list) {
147 if (!CheckWaitType(thread.get(), WAITTYPE_ARB, arbiter, address)) 131 if (!CheckWait_AddressArbiter(thread.get(), address))
148 continue; 132 continue;
149 133
150 if (thread == nullptr) 134 if (thread == nullptr)
151 continue; // TODO(yuriks): Thread handle will hang around forever. Should clean up. 135 continue;
152 136
153 if(thread->current_priority <= priority) { 137 if(thread->current_priority <= priority) {
154 highest_priority_thread = thread.get(); 138 highest_priority_thread = thread.get();
@@ -165,11 +149,11 @@ Thread* ArbitrateHighestPriorityThread(Object* arbiter, u32 address) {
165} 149}
166 150
167/// Arbitrate all threads currently waiting 151/// Arbitrate all threads currently waiting
168void ArbitrateAllThreads(Object* arbiter, u32 address) { 152void ArbitrateAllThreads(u32 address) {
169 153
170 // Iterate through threads, find highest priority thread that is waiting to be arbitrated... 154 // Iterate through threads, find highest priority thread that is waiting to be arbitrated...
171 for (auto& thread : thread_list) { 155 for (auto& thread : thread_list) {
172 if (CheckWaitType(thread.get(), WAITTYPE_ARB, arbiter, address)) 156 if (CheckWait_AddressArbiter(thread.get(), address))
173 thread->ResumeFromWait(); 157 thread->ResumeFromWait();
174 } 158 }
175} 159}
@@ -177,9 +161,6 @@ void ArbitrateAllThreads(Object* arbiter, u32 address) {
177/// Calls a thread by marking it as "ready" (note: will not actually execute until current thread yields) 161/// Calls a thread by marking it as "ready" (note: will not actually execute until current thread yields)
178static void CallThread(Thread* t) { 162static void CallThread(Thread* t) {
179 // Stop waiting 163 // Stop waiting
180 if (t->wait_type != WAITTYPE_NONE) {
181 t->wait_type = WAITTYPE_NONE;
182 }
183 ChangeThreadState(t, THREADSTATUS_READY); 164 ChangeThreadState(t, THREADSTATUS_READY);
184} 165}
185 166
@@ -200,7 +181,6 @@ static void SwitchContext(Thread* t) {
200 current_thread = t; 181 current_thread = t;
201 ChangeReadyState(t, false); 182 ChangeReadyState(t, false);
202 t->status = (t->status | THREADSTATUS_RUNNING) & ~THREADSTATUS_READY; 183 t->status = (t->status | THREADSTATUS_RUNNING) & ~THREADSTATUS_READY;
203 t->wait_type = WAITTYPE_NONE;
204 Core::g_app_core->LoadContext(t->context); 184 Core::g_app_core->LoadContext(t->context);
205 } else { 185 } else {
206 current_thread = nullptr; 186 current_thread = nullptr;
@@ -223,16 +203,27 @@ static Thread* NextThread() {
223 return next; 203 return next;
224} 204}
225 205
226void WaitCurrentThread(WaitType wait_type, Object* wait_object) { 206void WaitCurrentThread_Sleep() {
227 Thread* thread = GetCurrentThread(); 207 Thread* thread = GetCurrentThread();
228 thread->wait_type = wait_type;
229 thread->wait_object = wait_object;
230 ChangeThreadState(thread, ThreadStatus(THREADSTATUS_WAIT | (thread->status & THREADSTATUS_SUSPEND))); 208 ChangeThreadState(thread, ThreadStatus(THREADSTATUS_WAIT | (thread->status & THREADSTATUS_SUSPEND)));
231} 209}
232 210
233void WaitCurrentThread(WaitType wait_type, Object* wait_object, VAddr wait_address) { 211void WaitCurrentThread_WaitSynchronization(SharedPtr<WaitObject> wait_object, bool wait_set_output, bool wait_all) {
234 WaitCurrentThread(wait_type, wait_object); 212 Thread* thread = GetCurrentThread();
235 GetCurrentThread()->wait_address = wait_address; 213 thread->wait_set_output = wait_set_output;
214 thread->wait_all = wait_all;
215
216 // It's possible to call WaitSynchronizationN without any objects passed in...
217 if (wait_object != nullptr)
218 thread->wait_objects.push_back(wait_object);
219
220 ChangeThreadState(thread, ThreadStatus(THREADSTATUS_WAIT | (thread->status & THREADSTATUS_SUSPEND)));
221}
222
223void WaitCurrentThread_ArbitrateAddress(VAddr wait_address) {
224 Thread* thread = GetCurrentThread();
225 thread->wait_address = wait_address;
226 ChangeThreadState(thread, ThreadStatus(THREADSTATUS_WAIT | (thread->status & THREADSTATUS_SUSPEND)));
236} 227}
237 228
238/// Event type for the thread wake up event 229/// Event type for the thread wake up event
@@ -247,6 +238,12 @@ static void ThreadWakeupCallback(u64 parameter, int cycles_late) {
247 return; 238 return;
248 } 239 }
249 240
241 thread->SetWaitSynchronizationResult(ResultCode(ErrorDescription::Timeout, ErrorModule::OS,
242 ErrorSummary::StatusChanged, ErrorLevel::Info));
243
244 if (thread->wait_set_output)
245 thread->SetWaitSynchronizationOutput(-1);
246
250 thread->ResumeFromWait(); 247 thread->ResumeFromWait();
251} 248}
252 249
@@ -261,14 +258,63 @@ void WakeThreadAfterDelay(Thread* thread, s64 nanoseconds) {
261 CoreTiming::ScheduleEvent(usToCycles(microseconds), ThreadWakeupEventType, thread->GetHandle()); 258 CoreTiming::ScheduleEvent(usToCycles(microseconds), ThreadWakeupEventType, thread->GetHandle());
262} 259}
263 260
264/// Resumes a thread from waiting by marking it as "ready" 261void Thread::ReleaseWaitObject(WaitObject* wait_object) {
262 if (wait_objects.empty()) {
263 LOG_CRITICAL(Kernel, "thread is not waiting on any objects!");
264 return;
265 }
266
267 // Remove this thread from the waiting object's thread list
268 wait_object->RemoveWaitingThread(this);
269
270 unsigned index = 0;
271 bool wait_all_failed = false; // Will be set to true if any object is unavailable
272
273 // Iterate through all waiting objects to check availability...
274 for (auto itr = wait_objects.begin(); itr != wait_objects.end(); ++itr) {
275 if ((*itr)->ShouldWait())
276 wait_all_failed = true;
277
278 // The output should be the last index of wait_object
279 if (*itr == wait_object)
280 index = itr - wait_objects.begin();
281 }
282
283 // If we are waiting on all objects...
284 if (wait_all) {
285 // Resume the thread only if all are available...
286 if (!wait_all_failed) {
287 SetWaitSynchronizationResult(RESULT_SUCCESS);
288 SetWaitSynchronizationOutput(-1);
289
290 ResumeFromWait();
291 }
292 } else {
293 // Otherwise, resume
294 SetWaitSynchronizationResult(RESULT_SUCCESS);
295
296 if (wait_set_output)
297 SetWaitSynchronizationOutput(index);
298
299 ResumeFromWait();
300 }
301}
302
265void Thread::ResumeFromWait() { 303void Thread::ResumeFromWait() {
266 // Cancel any outstanding wakeup events 304 // Cancel any outstanding wakeup events
267 CoreTiming::UnscheduleEvent(ThreadWakeupEventType, GetHandle()); 305 CoreTiming::UnscheduleEvent(ThreadWakeupEventType, GetHandle());
268 306
269 status &= ~THREADSTATUS_WAIT; 307 status &= ~THREADSTATUS_WAIT;
270 wait_object = nullptr; 308
271 wait_type = WAITTYPE_NONE; 309 // Remove this thread from all other WaitObjects
310 for (auto wait_object : wait_objects)
311 wait_object->RemoveWaitingThread(this);
312
313 wait_objects.clear();
314 wait_set_output = false;
315 wait_all = false;
316 wait_address = 0;
317
272 if (!(status & (THREADSTATUS_WAITSUSPEND | THREADSTATUS_DORMANT | THREADSTATUS_DEAD))) { 318 if (!(status & (THREADSTATUS_WAITSUSPEND | THREADSTATUS_DORMANT | THREADSTATUS_DEAD))) {
273 ChangeReadyState(this, true); 319 ChangeReadyState(this, true);
274 } 320 }
@@ -334,8 +380,9 @@ ResultVal<SharedPtr<Thread>> Thread::Create(std::string name, VAddr entry_point,
334 thread->stack_size = stack_size; 380 thread->stack_size = stack_size;
335 thread->initial_priority = thread->current_priority = priority; 381 thread->initial_priority = thread->current_priority = priority;
336 thread->processor_id = processor_id; 382 thread->processor_id = processor_id;
337 thread->wait_type = WAITTYPE_NONE; 383 thread->wait_set_output = false;
338 thread->wait_object = nullptr; 384 thread->wait_all = false;
385 thread->wait_objects.clear();
339 thread->wait_address = 0; 386 thread->wait_address = 0;
340 thread->name = std::move(name); 387 thread->name = std::move(name);
341 388
@@ -419,13 +466,20 @@ void Reschedule() {
419 LOG_TRACE(Kernel, "cannot context switch from 0x%08X, no higher priority thread!", prev->GetHandle()); 466 LOG_TRACE(Kernel, "cannot context switch from 0x%08X, no higher priority thread!", prev->GetHandle());
420 467
421 for (auto& thread : thread_list) { 468 for (auto& thread : thread_list) {
422 LOG_TRACE(Kernel, "\thandle=0x%08X prio=0x%02X, status=0x%08X wait_type=0x%08X wait_handle=0x%08X", 469 LOG_TRACE(Kernel, "\thandle=0x%08X prio=0x%02X, status=0x%08X", thread->GetHandle(),
423 thread->GetHandle(), thread->current_priority, thread->status, thread->wait_type, 470 thread->current_priority, thread->status);
424 (thread->wait_object ? thread->wait_object->GetHandle() : INVALID_HANDLE));
425 } 471 }
426 } 472 }
427} 473}
428 474
475void Thread::SetWaitSynchronizationResult(ResultCode result) {
476 context.cpu_registers[0] = result.raw;
477}
478
479void Thread::SetWaitSynchronizationOutput(s32 output) {
480 context.cpu_registers[1] = output;
481}
482
429//////////////////////////////////////////////////////////////////////////////////////////////////// 483////////////////////////////////////////////////////////////////////////////////////////////////////
430 484
431void ThreadingInit() { 485void ThreadingInit() {