summaryrefslogtreecommitdiff
path: root/src/core/hle/kernel/thread.cpp
diff options
context:
space:
mode:
authorGravatar bunnei2015-06-07 23:39:37 -0400
committerGravatar bunnei2015-06-16 22:34:39 -0400
commit71e8822d23c030311858e6fcc8480b9c52f13f39 (patch)
tree0a5f53cbcebc2c98c7c9c224cbc1a91c1b783366 /src/core/hle/kernel/thread.cpp
parentMerge pull request #866 from lioncash/typo (diff)
downloadyuzu-71e8822d23c030311858e6fcc8480b9c52f13f39.tar.gz
yuzu-71e8822d23c030311858e6fcc8480b9c52f13f39.tar.xz
yuzu-71e8822d23c030311858e6fcc8480b9c52f13f39.zip
kernel: Fix svcWaitSynch to always acquire requested wait objects.
Diffstat (limited to 'src/core/hle/kernel/thread.cpp')
-rw-r--r--src/core/hle/kernel/thread.cpp76
1 files changed, 26 insertions, 50 deletions
diff --git a/src/core/hle/kernel/thread.cpp b/src/core/hle/kernel/thread.cpp
index 22c795ad4..4729a7fe0 100644
--- a/src/core/hle/kernel/thread.cpp
+++ b/src/core/hle/kernel/thread.cpp
@@ -13,6 +13,7 @@
13#include "common/thread_queue_list.h" 13#include "common/thread_queue_list.h"
14 14
15#include "core/arm/arm_interface.h" 15#include "core/arm/arm_interface.h"
16#include "core/arm/skyeye_common/armdefs.h"
16#include "core/core.h" 17#include "core/core.h"
17#include "core/core_timing.h" 18#include "core/core_timing.h"
18#include "core/hle/hle.h" 19#include "core/hle/hle.h"
@@ -193,8 +194,22 @@ static void SwitchContext(Thread* new_thread) {
193 if (new_thread) { 194 if (new_thread) {
194 DEBUG_ASSERT_MSG(new_thread->status == THREADSTATUS_READY, "Thread must be ready to become running."); 195 DEBUG_ASSERT_MSG(new_thread->status == THREADSTATUS_READY, "Thread must be ready to become running.");
195 196
197 // Cancel any outstanding wakeup events for this thread
198 CoreTiming::UnscheduleEvent(ThreadWakeupEventType, new_thread->callback_handle);
199
196 current_thread = new_thread; 200 current_thread = new_thread;
197 201
202 // If the thread was waited by a svcWaitSynch call, step back PC by one instruction to rerun
203 // the SVC when the thread wakes up. This is necessary to ensure that the thread can acquire
204 // the requested wait object(s) before continuing.
205 if (new_thread->waitsynch_waited) {
206 // CPSR flag indicates CPU mode
207 bool thumb_mode = (new_thread->context.cpsr & TBIT) != 0;
208
209 // SVC instruction is 2 bytes for THUMB, 4 bytes for ARM
210 new_thread->context.pc -= thumb_mode ? 2 : 4;
211 }
212
198 ready_queue.remove(new_thread->current_priority, new_thread); 213 ready_queue.remove(new_thread->current_priority, new_thread);
199 new_thread->status = THREADSTATUS_RUNNING; 214 new_thread->status = THREADSTATUS_RUNNING;
200 215
@@ -243,6 +258,7 @@ void WaitCurrentThread_WaitSynchronization(std::vector<SharedPtr<WaitObject>> wa
243 thread->wait_set_output = wait_set_output; 258 thread->wait_set_output = wait_set_output;
244 thread->wait_all = wait_all; 259 thread->wait_all = wait_all;
245 thread->wait_objects = std::move(wait_objects); 260 thread->wait_objects = std::move(wait_objects);
261 thread->waitsynch_waited = true;
246 thread->status = THREADSTATUS_WAIT_SYNCH; 262 thread->status = THREADSTATUS_WAIT_SYNCH;
247} 263}
248 264
@@ -268,6 +284,8 @@ static void ThreadWakeupCallback(u64 thread_handle, int cycles_late) {
268 return; 284 return;
269 } 285 }
270 286
287 thread->waitsynch_waited = false;
288
271 if (thread->status == THREADSTATUS_WAIT_SYNCH) { 289 if (thread->status == THREADSTATUS_WAIT_SYNCH) {
272 thread->SetWaitSynchronizationResult(ResultCode(ErrorDescription::Timeout, ErrorModule::OS, 290 thread->SetWaitSynchronizationResult(ResultCode(ErrorDescription::Timeout, ErrorModule::OS,
273 ErrorSummary::StatusChanged, ErrorLevel::Info)); 291 ErrorSummary::StatusChanged, ErrorLevel::Info));
@@ -288,63 +306,20 @@ void Thread::WakeAfterDelay(s64 nanoseconds) {
288 CoreTiming::ScheduleEvent(usToCycles(microseconds), ThreadWakeupEventType, callback_handle); 306 CoreTiming::ScheduleEvent(usToCycles(microseconds), ThreadWakeupEventType, callback_handle);
289} 307}
290 308
291void Thread::ReleaseWaitObject(WaitObject* wait_object) {
292 if (status != THREADSTATUS_WAIT_SYNCH || wait_objects.empty()) {
293 LOG_CRITICAL(Kernel, "thread is not waiting on any objects!");
294 return;
295 }
296
297 // Remove this thread from the waiting object's thread list
298 wait_object->RemoveWaitingThread(this);
299
300 unsigned index = 0;
301 bool wait_all_failed = false; // Will be set to true if any object is unavailable
302
303 // Iterate through all waiting objects to check availability...
304 for (auto itr = wait_objects.begin(); itr != wait_objects.end(); ++itr) {
305 if ((*itr)->ShouldWait())
306 wait_all_failed = true;
307
308 // The output should be the last index of wait_object
309 if (*itr == wait_object)
310 index = itr - wait_objects.begin();
311 }
312
313 // If we are waiting on all objects...
314 if (wait_all) {
315 // Resume the thread only if all are available...
316 if (!wait_all_failed) {
317 SetWaitSynchronizationResult(RESULT_SUCCESS);
318 SetWaitSynchronizationOutput(-1);
319
320 ResumeFromWait();
321 }
322 } else {
323 // Otherwise, resume
324 SetWaitSynchronizationResult(RESULT_SUCCESS);
325
326 if (wait_set_output)
327 SetWaitSynchronizationOutput(index);
328
329 ResumeFromWait();
330 }
331}
332
333void Thread::ResumeFromWait() { 309void Thread::ResumeFromWait() {
334 // Cancel any outstanding wakeup events for this thread
335 CoreTiming::UnscheduleEvent(ThreadWakeupEventType, callback_handle);
336
337 switch (status) { 310 switch (status) {
338 case THREADSTATUS_WAIT_SYNCH: 311 case THREADSTATUS_WAIT_SYNCH:
339 // Remove this thread from all other WaitObjects
340 for (auto wait_object : wait_objects)
341 wait_object->RemoveWaitingThread(this);
342 break;
343 case THREADSTATUS_WAIT_ARB: 312 case THREADSTATUS_WAIT_ARB:
344 case THREADSTATUS_WAIT_SLEEP: 313 case THREADSTATUS_WAIT_SLEEP:
345 break; 314 break;
346 case THREADSTATUS_RUNNING: 315
347 case THREADSTATUS_READY: 316 case THREADSTATUS_READY:
317 // If the thread is waiting on multiple wait objects, it might be awoken more than once
318 // before actually resuming. We can ignore subsequent wakeups if the thread status has
319 // already been set to THREADSTATUS_READY.
320 return;
321
322 case THREADSTATUS_RUNNING:
348 DEBUG_ASSERT_MSG(false, "Thread with object id %u has already resumed.", GetObjectId()); 323 DEBUG_ASSERT_MSG(false, "Thread with object id %u has already resumed.", GetObjectId());
349 return; 324 return;
350 case THREADSTATUS_DEAD: 325 case THREADSTATUS_DEAD:
@@ -415,6 +390,7 @@ ResultVal<SharedPtr<Thread>> Thread::Create(std::string name, VAddr entry_point,
415 thread->callback_handle = wakeup_callback_handle_table.Create(thread).MoveFrom(); 390 thread->callback_handle = wakeup_callback_handle_table.Create(thread).MoveFrom();
416 thread->owner_process = g_current_process; 391 thread->owner_process = g_current_process;
417 thread->tls_index = -1; 392 thread->tls_index = -1;
393 thread->waitsynch_waited = false;
418 394
419 // Find the next available TLS index, and mark it as used 395 // Find the next available TLS index, and mark it as used
420 auto& used_tls_slots = Kernel::g_current_process->used_tls_slots; 396 auto& used_tls_slots = Kernel::g_current_process->used_tls_slots;