diff options
Diffstat (limited to 'src/core/hle/kernel/thread.cpp')
| -rw-r--r-- | src/core/hle/kernel/thread.cpp | 226 |
1 files changed, 140 insertions, 86 deletions
diff --git a/src/core/hle/kernel/thread.cpp b/src/core/hle/kernel/thread.cpp index dd20ca30e..3987f9608 100644 --- a/src/core/hle/kernel/thread.cpp +++ b/src/core/hle/kernel/thread.cpp @@ -4,7 +4,6 @@ #include <algorithm> #include <list> -#include <map> #include <vector> #include "common/common.h" @@ -22,17 +21,12 @@ namespace Kernel { -ResultVal<bool> Thread::WaitSynchronization() { - const bool wait = status != THREADSTATUS_DORMANT; - if (wait) { - Thread* thread = GetCurrentThread(); - if (std::find(waiting_threads.begin(), waiting_threads.end(), thread) == waiting_threads.end()) { - waiting_threads.push_back(thread); - } - WaitCurrentThread(WAITTYPE_THREADEND, this); - } +bool Thread::ShouldWait() { + return status != THREADSTATUS_DORMANT; +} - return MakeResult<bool>(wait); +void Thread::Acquire() { + _assert_msg_(Kernel, !ShouldWait(), "object unavailable!"); } // Lists all thread ids that aren't deleted/etc. @@ -46,6 +40,9 @@ static Thread* current_thread; static const u32 INITIAL_THREAD_ID = 1; ///< The first available thread id at startup static u32 next_thread_id; ///< The next available thread id +Thread::Thread() {} +Thread::~Thread() {} + Thread* GetCurrentThread() { return current_thread; } @@ -55,7 +52,7 @@ static void ResetThread(Thread* t, u32 arg, s32 lowest_priority) { memset(&t->context, 0, sizeof(Core::ThreadContext)); t->context.cpu_registers[0] = arg; - t->context.pc = t->context.reg_15 = t->entry_point; + t->context.pc = t->entry_point; t->context.sp = t->stack_top; t->context.cpsr = 0x1F; // Usermode @@ -67,8 +64,8 @@ static void ResetThread(Thread* t, u32 arg, s32 lowest_priority) { if (t->current_priority < lowest_priority) { t->current_priority = t->initial_priority; } - t->wait_type = WAITTYPE_NONE; - t->wait_object = nullptr; + + t->wait_objects.clear(); t->wait_address = 0; } @@ -88,37 +85,35 @@ static void ChangeReadyState(Thread* t, bool ready) { } } -/// Check if a thread is blocking on a specified wait type -static bool CheckWaitType(const Thread* thread, WaitType type) { - return (type == thread->wait_type) && (thread->IsWaiting()); -} +/// Check if a thread is waiting on a the specified wait object +static bool CheckWait_WaitObject(const Thread* thread, WaitObject* wait_object) { + auto itr = std::find(thread->wait_objects.begin(), thread->wait_objects.end(), wait_object); + + if (itr != thread->wait_objects.end()) + return thread->IsWaiting(); -/// Check if a thread is blocking on a specified wait type with a specified handle -static bool CheckWaitType(const Thread* thread, WaitType type, Object* wait_object) { - return CheckWaitType(thread, type) && wait_object == thread->wait_object; + return false; } -/// Check if a thread is blocking on a specified wait type with a specified handle and address -static bool CheckWaitType(const Thread* thread, WaitType type, Object* wait_object, VAddr wait_address) { - return CheckWaitType(thread, type, wait_object) && (wait_address == thread->wait_address); +/// Check if the specified thread is waiting on the specified address to be arbitrated +static bool CheckWait_AddressArbiter(const Thread* thread, VAddr wait_address) { + return thread->IsWaiting() && thread->wait_objects.empty() && wait_address == thread->wait_address; } /// Stops the current thread void Thread::Stop(const char* reason) { // Release all the mutexes that this thread holds - ReleaseThreadMutexes(GetHandle()); + ReleaseThreadMutexes(this); ChangeReadyState(this, false); status = THREADSTATUS_DORMANT; - for (auto& waiting_thread : waiting_threads) { - if (CheckWaitType(waiting_thread.get(), WAITTYPE_THREADEND, this)) - waiting_thread->ResumeFromWait(); - } - waiting_threads.clear(); + WakeupAllWaitingThreads(); // Stopped threads are never waiting. - wait_type = WAITTYPE_NONE; - wait_object = nullptr; + for (auto& wait_object : wait_objects) { + wait_object->RemoveWaitingThread(this); + } + wait_objects.clear(); wait_address = 0; } @@ -129,26 +124,20 @@ static void ChangeThreadState(Thread* t, ThreadStatus new_status) { } ChangeReadyState(t, (new_status & THREADSTATUS_READY) != 0); t->status = new_status; - - if (new_status == THREADSTATUS_WAIT) { - if (t->wait_type == WAITTYPE_NONE) { - LOG_ERROR(Kernel, "Waittype none not allowed"); - } - } } /// Arbitrate the highest priority thread that is waiting -Thread* ArbitrateHighestPriorityThread(Object* arbiter, u32 address) { +Thread* ArbitrateHighestPriorityThread(u32 address) { Thread* highest_priority_thread = nullptr; s32 priority = THREADPRIO_LOWEST; // Iterate through threads, find highest priority thread that is waiting to be arbitrated... for (auto& thread : thread_list) { - if (!CheckWaitType(thread.get(), WAITTYPE_ARB, arbiter, address)) + if (!CheckWait_AddressArbiter(thread.get(), address)) continue; if (thread == nullptr) - continue; // TODO(yuriks): Thread handle will hang around forever. Should clean up. + continue; if(thread->current_priority <= priority) { highest_priority_thread = thread.get(); @@ -165,11 +154,11 @@ Thread* ArbitrateHighestPriorityThread(Object* arbiter, u32 address) { } /// Arbitrate all threads currently waiting -void ArbitrateAllThreads(Object* arbiter, u32 address) { +void ArbitrateAllThreads(u32 address) { // Iterate through threads, find highest priority thread that is waiting to be arbitrated... for (auto& thread : thread_list) { - if (CheckWaitType(thread.get(), WAITTYPE_ARB, arbiter, address)) + if (CheckWait_AddressArbiter(thread.get(), address)) thread->ResumeFromWait(); } } @@ -177,9 +166,6 @@ void ArbitrateAllThreads(Object* arbiter, u32 address) { /// Calls a thread by marking it as "ready" (note: will not actually execute until current thread yields) static void CallThread(Thread* t) { // Stop waiting - if (t->wait_type != WAITTYPE_NONE) { - t->wait_type = WAITTYPE_NONE; - } ChangeThreadState(t, THREADSTATUS_READY); } @@ -200,7 +186,6 @@ static void SwitchContext(Thread* t) { current_thread = t; ChangeReadyState(t, false); t->status = (t->status | THREADSTATUS_RUNNING) & ~THREADSTATUS_READY; - t->wait_type = WAITTYPE_NONE; Core::g_app_core->LoadContext(t->context); } else { current_thread = nullptr; @@ -223,49 +208,119 @@ static Thread* NextThread() { return next; } -void WaitCurrentThread(WaitType wait_type, Object* wait_object) { +void WaitCurrentThread_Sleep() { Thread* thread = GetCurrentThread(); - thread->wait_type = wait_type; - thread->wait_object = wait_object; ChangeThreadState(thread, ThreadStatus(THREADSTATUS_WAIT | (thread->status & THREADSTATUS_SUSPEND))); } -void WaitCurrentThread(WaitType wait_type, Object* wait_object, VAddr wait_address) { - WaitCurrentThread(wait_type, wait_object); - GetCurrentThread()->wait_address = wait_address; +void WaitCurrentThread_WaitSynchronization(SharedPtr<WaitObject> wait_object, bool wait_set_output, bool wait_all) { + Thread* thread = GetCurrentThread(); + thread->wait_set_output = wait_set_output; + thread->wait_all = wait_all; + + // It's possible to call WaitSynchronizationN without any objects passed in... + if (wait_object != nullptr) + thread->wait_objects.push_back(wait_object); + + ChangeThreadState(thread, ThreadStatus(THREADSTATUS_WAIT | (thread->status & THREADSTATUS_SUSPEND))); +} + +void WaitCurrentThread_ArbitrateAddress(VAddr wait_address) { + Thread* thread = GetCurrentThread(); + thread->wait_address = wait_address; + ChangeThreadState(thread, ThreadStatus(THREADSTATUS_WAIT | (thread->status & THREADSTATUS_SUSPEND))); } /// Event type for the thread wake up event static int ThreadWakeupEventType = -1; +// TODO(yuriks): This can be removed if Thread objects are explicitly pooled in the future, allowing +// us to simply use a pool index or similar. +static Kernel::HandleTable wakeup_callback_handle_table; /// Callback that will wake up the thread it was scheduled for -static void ThreadWakeupCallback(u64 parameter, int cycles_late) { - Handle handle = static_cast<Handle>(parameter); - SharedPtr<Thread> thread = Kernel::g_handle_table.Get<Thread>(handle); +static void ThreadWakeupCallback(u64 thread_handle, int cycles_late) { + SharedPtr<Thread> thread = wakeup_callback_handle_table.Get<Thread>((Handle)thread_handle); if (thread == nullptr) { - LOG_ERROR(Kernel, "Thread doesn't exist %u", handle); + LOG_CRITICAL(Kernel, "Callback fired for invalid thread %08X", thread_handle); return; } + thread->SetWaitSynchronizationResult(ResultCode(ErrorDescription::Timeout, ErrorModule::OS, + ErrorSummary::StatusChanged, ErrorLevel::Info)); + + if (thread->wait_set_output) + thread->SetWaitSynchronizationOutput(-1); + thread->ResumeFromWait(); } -void WakeThreadAfterDelay(Thread* thread, s64 nanoseconds) { +void Thread::WakeAfterDelay(s64 nanoseconds) { // Don't schedule a wakeup if the thread wants to wait forever if (nanoseconds == -1) return; - _dbg_assert_(Kernel, thread != nullptr); u64 microseconds = nanoseconds / 1000; - CoreTiming::ScheduleEvent(usToCycles(microseconds), ThreadWakeupEventType, thread->GetHandle()); + CoreTiming::ScheduleEvent(usToCycles(microseconds), ThreadWakeupEventType, callback_handle); +} + +void Thread::ReleaseWaitObject(WaitObject* wait_object) { + if (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(); + } } -/// Resumes a thread from waiting by marking it as "ready" void Thread::ResumeFromWait() { + // Cancel any outstanding wakeup events + CoreTiming::UnscheduleEvent(ThreadWakeupEventType, callback_handle); + status &= ~THREADSTATUS_WAIT; - wait_object = nullptr; - wait_type = WAITTYPE_NONE; + + // Remove this thread from all other WaitObjects + for (auto wait_object : wait_objects) + wait_object->RemoveWaitingThread(this); + + wait_objects.clear(); + wait_set_output = false; + wait_all = false; + wait_address = 0; + if (!(status & (THREADSTATUS_WAITSUSPEND | THREADSTATUS_DORMANT | THREADSTATUS_DEAD))) { ChangeReadyState(this, true); } @@ -277,11 +332,11 @@ static void DebugThreadQueue() { if (!thread) { return; } - LOG_DEBUG(Kernel, "0x%02X 0x%08X (current)", thread->current_priority, GetCurrentThread()->GetHandle()); + LOG_DEBUG(Kernel, "0x%02X %u (current)", thread->current_priority, GetCurrentThread()->GetObjectId()); for (auto& t : thread_list) { s32 priority = thread_ready_queue.contains(t.get()); if (priority != -1) { - LOG_DEBUG(Kernel, "0x%02X 0x%08X", priority, t->GetHandle()); + LOG_DEBUG(Kernel, "0x%02X %u", priority, t->GetObjectId()); } } } @@ -313,14 +368,6 @@ ResultVal<SharedPtr<Thread>> Thread::Create(std::string name, VAddr entry_point, SharedPtr<Thread> thread(new Thread); - // TODO(yuriks): Thread requires a handle to be inserted into the various scheduling queues for - // the time being. Create a handle here, it will be copied to the handle field in - // the object and use by the rest of the code. This should be removed when other - // code doesn't rely on the handle anymore. - ResultVal<Handle> handle = Kernel::g_handle_table.Create(thread); - if (handle.Failed()) - return handle.Code(); - thread_list.push_back(thread); thread_ready_queue.prepare(priority); @@ -331,10 +378,12 @@ ResultVal<SharedPtr<Thread>> Thread::Create(std::string name, VAddr entry_point, thread->stack_size = stack_size; thread->initial_priority = thread->current_priority = priority; thread->processor_id = processor_id; - thread->wait_type = WAITTYPE_NONE; - thread->wait_object = nullptr; + thread->wait_set_output = false; + thread->wait_all = false; + thread->wait_objects.clear(); thread->wait_address = 0; thread->name = std::move(name); + thread->callback_handle = wakeup_callback_handle_table.Create(thread).MoveFrom(); ResetThread(thread.get(), arg, 0); CallThread(thread.get()); @@ -368,16 +417,14 @@ void Thread::SetPriority(s32 priority) { } } -Handle SetupIdleThread() { +SharedPtr<Thread> SetupIdleThread() { // We need to pass a few valid values to get around parameter checking in Thread::Create. - auto thread_res = Thread::Create("idle", Memory::KERNEL_MEMORY_VADDR, THREADPRIO_LOWEST, 0, - THREADPROCESSORID_0, 0, Kernel::DEFAULT_STACK_SIZE); - _dbg_assert_(Kernel, thread_res.Succeeded()); - SharedPtr<Thread> thread = std::move(*thread_res); + auto thread = Thread::Create("idle", Memory::KERNEL_MEMORY_VADDR, THREADPRIO_LOWEST, 0, + THREADPROCESSORID_0, 0, Kernel::DEFAULT_STACK_SIZE).MoveFrom(); thread->idle = true; CallThread(thread.get()); - return thread->GetHandle(); + return thread; } SharedPtr<Thread> SetupMainThread(s32 priority, u32 stack_size) { @@ -410,19 +457,26 @@ void Reschedule() { HLE::g_reschedule = false; if (next != nullptr) { - LOG_TRACE(Kernel, "context switch 0x%08X -> 0x%08X", prev->GetHandle(), next->GetHandle()); + LOG_TRACE(Kernel, "context switch %u -> %u", prev->GetObjectId(), next->GetObjectId()); SwitchContext(next); } else { - LOG_TRACE(Kernel, "cannot context switch from 0x%08X, no higher priority thread!", prev->GetHandle()); + LOG_TRACE(Kernel, "cannot context switch from %u, no higher priority thread!", prev->GetObjectId()); for (auto& thread : thread_list) { - LOG_TRACE(Kernel, "\thandle=0x%08X prio=0x%02X, status=0x%08X wait_type=0x%08X wait_handle=0x%08X", - thread->GetHandle(), thread->current_priority, thread->status, thread->wait_type, - (thread->wait_object ? thread->wait_object->GetHandle() : INVALID_HANDLE)); + LOG_TRACE(Kernel, "\tid=%u prio=0x%02X, status=0x%08X", thread->GetObjectId(), + thread->current_priority, thread->status); } } } +void Thread::SetWaitSynchronizationResult(ResultCode result) { + context.cpu_registers[0] = result.raw; +} + +void Thread::SetWaitSynchronizationOutput(s32 output) { + context.cpu_registers[1] = output; +} + //////////////////////////////////////////////////////////////////////////////////////////////////// void ThreadingInit() { |
