|
|
@@ -13,6 +13,7 @@
|
|
|
#include "common/thread_queue_list.h"
|
|
|
|
|
|
#include "core/arm/arm_interface.h"
|
|
|
+#include "core/arm/skyeye_common/armdefs.h"
|
|
|
#include "core/core.h"
|
|
|
#include "core/core_timing.h"
|
|
|
#include "core/hle/hle.h"
|
|
|
@@ -193,8 +194,22 @@ static void SwitchContext(Thread* new_thread) {
|
|
|
if (new_thread) {
|
|
|
DEBUG_ASSERT_MSG(new_thread->status == THREADSTATUS_READY, "Thread must be ready to become running.");
|
|
|
|
|
|
+ // Cancel any outstanding wakeup events for this thread
|
|
|
+ CoreTiming::UnscheduleEvent(ThreadWakeupEventType, new_thread->callback_handle);
|
|
|
+
|
|
|
current_thread = new_thread;
|
|
|
|
|
|
+ // If the thread was waited by a svcWaitSynch call, step back PC by one instruction to rerun
|
|
|
+ // the SVC when the thread wakes up. This is necessary to ensure that the thread can acquire
|
|
|
+ // the requested wait object(s) before continuing.
|
|
|
+ if (new_thread->waitsynch_waited) {
|
|
|
+ // CPSR flag indicates CPU mode
|
|
|
+ bool thumb_mode = (new_thread->context.cpsr & TBIT) != 0;
|
|
|
+
|
|
|
+ // SVC instruction is 2 bytes for THUMB, 4 bytes for ARM
|
|
|
+ new_thread->context.pc -= thumb_mode ? 2 : 4;
|
|
|
+ }
|
|
|
+
|
|
|
ready_queue.remove(new_thread->current_priority, new_thread);
|
|
|
new_thread->status = THREADSTATUS_RUNNING;
|
|
|
|
|
|
@@ -243,6 +258,7 @@ void WaitCurrentThread_WaitSynchronization(std::vector<SharedPtr<WaitObject>> wa
|
|
|
thread->wait_set_output = wait_set_output;
|
|
|
thread->wait_all = wait_all;
|
|
|
thread->wait_objects = std::move(wait_objects);
|
|
|
+ thread->waitsynch_waited = true;
|
|
|
thread->status = THREADSTATUS_WAIT_SYNCH;
|
|
|
}
|
|
|
|
|
|
@@ -268,6 +284,8 @@ static void ThreadWakeupCallback(u64 thread_handle, int cycles_late) {
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
+ thread->waitsynch_waited = false;
|
|
|
+
|
|
|
if (thread->status == THREADSTATUS_WAIT_SYNCH) {
|
|
|
thread->SetWaitSynchronizationResult(ResultCode(ErrorDescription::Timeout, ErrorModule::OS,
|
|
|
ErrorSummary::StatusChanged, ErrorLevel::Info));
|
|
|
@@ -288,63 +306,20 @@ void Thread::WakeAfterDelay(s64 nanoseconds) {
|
|
|
CoreTiming::ScheduleEvent(usToCycles(microseconds), ThreadWakeupEventType, callback_handle);
|
|
|
}
|
|
|
|
|
|
-void Thread::ReleaseWaitObject(WaitObject* wait_object) {
|
|
|
- if (status != THREADSTATUS_WAIT_SYNCH || wait_objects.empty()) {
|
|
|
- LOG_CRITICAL(Kernel, "thread is not waiting on any objects!");
|
|
|
- return;
|
|
|
- }
|
|
|
-
|
|
|
- // Remove this thread from the waiting object's thread list
|
|
|
- wait_object->RemoveWaitingThread(this);
|
|
|
-
|
|
|
- unsigned index = 0;
|
|
|
- bool wait_all_failed = false; // Will be set to true if any object is unavailable
|
|
|
-
|
|
|
- // Iterate through all waiting objects to check availability...
|
|
|
- for (auto itr = wait_objects.begin(); itr != wait_objects.end(); ++itr) {
|
|
|
- if ((*itr)->ShouldWait())
|
|
|
- wait_all_failed = true;
|
|
|
-
|
|
|
- // The output should be the last index of wait_object
|
|
|
- if (*itr == wait_object)
|
|
|
- index = itr - wait_objects.begin();
|
|
|
- }
|
|
|
-
|
|
|
- // If we are waiting on all objects...
|
|
|
- if (wait_all) {
|
|
|
- // Resume the thread only if all are available...
|
|
|
- if (!wait_all_failed) {
|
|
|
- SetWaitSynchronizationResult(RESULT_SUCCESS);
|
|
|
- SetWaitSynchronizationOutput(-1);
|
|
|
-
|
|
|
- ResumeFromWait();
|
|
|
- }
|
|
|
- } else {
|
|
|
- // Otherwise, resume
|
|
|
- SetWaitSynchronizationResult(RESULT_SUCCESS);
|
|
|
-
|
|
|
- if (wait_set_output)
|
|
|
- SetWaitSynchronizationOutput(index);
|
|
|
-
|
|
|
- ResumeFromWait();
|
|
|
- }
|
|
|
-}
|
|
|
-
|
|
|
void Thread::ResumeFromWait() {
|
|
|
- // Cancel any outstanding wakeup events for this thread
|
|
|
- CoreTiming::UnscheduleEvent(ThreadWakeupEventType, callback_handle);
|
|
|
-
|
|
|
switch (status) {
|
|
|
case THREADSTATUS_WAIT_SYNCH:
|
|
|
- // Remove this thread from all other WaitObjects
|
|
|
- for (auto wait_object : wait_objects)
|
|
|
- wait_object->RemoveWaitingThread(this);
|
|
|
- break;
|
|
|
case THREADSTATUS_WAIT_ARB:
|
|
|
case THREADSTATUS_WAIT_SLEEP:
|
|
|
break;
|
|
|
- case THREADSTATUS_RUNNING:
|
|
|
+
|
|
|
case THREADSTATUS_READY:
|
|
|
+ // If the thread is waiting on multiple wait objects, it might be awoken more than once
|
|
|
+ // before actually resuming. We can ignore subsequent wakeups if the thread status has
|
|
|
+ // already been set to THREADSTATUS_READY.
|
|
|
+ return;
|
|
|
+
|
|
|
+ case THREADSTATUS_RUNNING:
|
|
|
DEBUG_ASSERT_MSG(false, "Thread with object id %u has already resumed.", GetObjectId());
|
|
|
return;
|
|
|
case THREADSTATUS_DEAD:
|
|
|
@@ -415,6 +390,7 @@ ResultVal<SharedPtr<Thread>> Thread::Create(std::string name, VAddr entry_point,
|
|
|
thread->callback_handle = wakeup_callback_handle_table.Create(thread).MoveFrom();
|
|
|
thread->owner_process = g_current_process;
|
|
|
thread->tls_index = -1;
|
|
|
+ thread->waitsynch_waited = false;
|
|
|
|
|
|
// Find the next available TLS index, and mark it as used
|
|
|
auto& used_tls_slots = Kernel::g_current_process->used_tls_slots;
|