|
|
|
@ -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;
|
|
|
|
|