mirror of
https://github.com/yuzu-emu/yuzu.git
synced 2024-07-04 23:31:19 +01:00
Merge pull request #2416 from lioncash/wait
kernel/svc: Clean up wait synchronization related functionality
This commit is contained in:
commit
78574e7a47
7 changed files with 54 additions and 51 deletions
|
@ -46,8 +46,7 @@ static void ThreadWakeupCallback(u64 thread_handle, [[maybe_unused]] s64 cycles_
|
||||||
|
|
||||||
bool resume = true;
|
bool resume = true;
|
||||||
|
|
||||||
if (thread->GetStatus() == ThreadStatus::WaitSynchAny ||
|
if (thread->GetStatus() == ThreadStatus::WaitSynch ||
|
||||||
thread->GetStatus() == ThreadStatus::WaitSynchAll ||
|
|
||||||
thread->GetStatus() == ThreadStatus::WaitHLEEvent) {
|
thread->GetStatus() == ThreadStatus::WaitHLEEvent) {
|
||||||
// Remove the thread from each of its waiting objects' waitlists
|
// Remove the thread from each of its waiting objects' waitlists
|
||||||
for (const auto& object : thread->GetWaitObjects()) {
|
for (const auto& object : thread->GetWaitObjects()) {
|
||||||
|
|
|
@ -147,8 +147,7 @@ void Process::PrepareForTermination() {
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
// TODO(Subv): When are the other running/ready threads terminated?
|
// TODO(Subv): When are the other running/ready threads terminated?
|
||||||
ASSERT_MSG(thread->GetStatus() == ThreadStatus::WaitSynchAny ||
|
ASSERT_MSG(thread->GetStatus() == ThreadStatus::WaitSynch,
|
||||||
thread->GetStatus() == ThreadStatus::WaitSynchAll,
|
|
||||||
"Exiting processes with non-waiting threads is currently unimplemented");
|
"Exiting processes with non-waiting threads is currently unimplemented");
|
||||||
|
|
||||||
thread->Stop();
|
thread->Stop();
|
||||||
|
|
|
@ -424,7 +424,7 @@ static ResultCode GetProcessId(Core::System& system, u64* process_id, Handle han
|
||||||
/// Default thread wakeup callback for WaitSynchronization
|
/// Default thread wakeup callback for WaitSynchronization
|
||||||
static bool DefaultThreadWakeupCallback(ThreadWakeupReason reason, SharedPtr<Thread> thread,
|
static bool DefaultThreadWakeupCallback(ThreadWakeupReason reason, SharedPtr<Thread> thread,
|
||||||
SharedPtr<WaitObject> object, std::size_t index) {
|
SharedPtr<WaitObject> object, std::size_t index) {
|
||||||
ASSERT(thread->GetStatus() == ThreadStatus::WaitSynchAny);
|
ASSERT(thread->GetStatus() == ThreadStatus::WaitSynch);
|
||||||
|
|
||||||
if (reason == ThreadWakeupReason::Timeout) {
|
if (reason == ThreadWakeupReason::Timeout) {
|
||||||
thread->SetWaitSynchronizationResult(RESULT_TIMEOUT);
|
thread->SetWaitSynchronizationResult(RESULT_TIMEOUT);
|
||||||
|
@ -502,7 +502,7 @@ static ResultCode WaitSynchronization(Core::System& system, Handle* index, VAddr
|
||||||
}
|
}
|
||||||
|
|
||||||
thread->SetWaitObjects(std::move(objects));
|
thread->SetWaitObjects(std::move(objects));
|
||||||
thread->SetStatus(ThreadStatus::WaitSynchAny);
|
thread->SetStatus(ThreadStatus::WaitSynch);
|
||||||
|
|
||||||
// Create an event to wake the thread up after the specified nanosecond delay has passed
|
// Create an event to wake the thread up after the specified nanosecond delay has passed
|
||||||
thread->WakeAfterDelay(nano_seconds);
|
thread->WakeAfterDelay(nano_seconds);
|
||||||
|
@ -518,16 +518,14 @@ static ResultCode CancelSynchronization(Core::System& system, Handle thread_hand
|
||||||
LOG_TRACE(Kernel_SVC, "called thread=0x{:X}", thread_handle);
|
LOG_TRACE(Kernel_SVC, "called thread=0x{:X}", thread_handle);
|
||||||
|
|
||||||
const auto& handle_table = system.Kernel().CurrentProcess()->GetHandleTable();
|
const auto& handle_table = system.Kernel().CurrentProcess()->GetHandleTable();
|
||||||
const SharedPtr<Thread> thread = handle_table.Get<Thread>(thread_handle);
|
SharedPtr<Thread> thread = handle_table.Get<Thread>(thread_handle);
|
||||||
if (!thread) {
|
if (!thread) {
|
||||||
LOG_ERROR(Kernel_SVC, "Thread handle does not exist, thread_handle=0x{:08X}",
|
LOG_ERROR(Kernel_SVC, "Thread handle does not exist, thread_handle=0x{:08X}",
|
||||||
thread_handle);
|
thread_handle);
|
||||||
return ERR_INVALID_HANDLE;
|
return ERR_INVALID_HANDLE;
|
||||||
}
|
}
|
||||||
|
|
||||||
ASSERT(thread->GetStatus() == ThreadStatus::WaitSynchAny);
|
thread->CancelWait();
|
||||||
thread->SetWaitSynchronizationResult(ERR_SYNCHRONIZATION_CANCELED);
|
|
||||||
thread->ResumeFromWait();
|
|
||||||
return RESULT_SUCCESS;
|
return RESULT_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -101,8 +101,7 @@ void Thread::ResumeFromWait() {
|
||||||
ASSERT_MSG(wait_objects.empty(), "Thread is waking up while waiting for objects");
|
ASSERT_MSG(wait_objects.empty(), "Thread is waking up while waiting for objects");
|
||||||
|
|
||||||
switch (status) {
|
switch (status) {
|
||||||
case ThreadStatus::WaitSynchAll:
|
case ThreadStatus::WaitSynch:
|
||||||
case ThreadStatus::WaitSynchAny:
|
|
||||||
case ThreadStatus::WaitHLEEvent:
|
case ThreadStatus::WaitHLEEvent:
|
||||||
case ThreadStatus::WaitSleep:
|
case ThreadStatus::WaitSleep:
|
||||||
case ThreadStatus::WaitIPC:
|
case ThreadStatus::WaitIPC:
|
||||||
|
@ -142,6 +141,12 @@ void Thread::ResumeFromWait() {
|
||||||
ChangeScheduler();
|
ChangeScheduler();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Thread::CancelWait() {
|
||||||
|
ASSERT(GetStatus() == ThreadStatus::WaitSynch);
|
||||||
|
SetWaitSynchronizationResult(ERR_SYNCHRONIZATION_CANCELED);
|
||||||
|
ResumeFromWait();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Resets a thread context, making it ready to be scheduled and run by the CPU
|
* Resets a thread context, making it ready to be scheduled and run by the CPU
|
||||||
* @param context Thread context to reset
|
* @param context Thread context to reset
|
||||||
|
|
|
@ -49,8 +49,7 @@ enum class ThreadStatus {
|
||||||
WaitHLEEvent, ///< Waiting for hle event to finish
|
WaitHLEEvent, ///< Waiting for hle event to finish
|
||||||
WaitSleep, ///< Waiting due to a SleepThread SVC
|
WaitSleep, ///< Waiting due to a SleepThread SVC
|
||||||
WaitIPC, ///< Waiting for the reply from an IPC request
|
WaitIPC, ///< Waiting for the reply from an IPC request
|
||||||
WaitSynchAny, ///< Waiting due to WaitSynch1 or WaitSynchN with wait_all = false
|
WaitSynch, ///< Waiting due to WaitSynchronization
|
||||||
WaitSynchAll, ///< Waiting due to WaitSynchronizationN with wait_all = true
|
|
||||||
WaitMutex, ///< Waiting due to an ArbitrateLock svc
|
WaitMutex, ///< Waiting due to an ArbitrateLock svc
|
||||||
WaitCondVar, ///< Waiting due to an WaitProcessWideKey svc
|
WaitCondVar, ///< Waiting due to an WaitProcessWideKey svc
|
||||||
WaitArb, ///< Waiting due to a SignalToAddress/WaitForAddress svc
|
WaitArb, ///< Waiting due to a SignalToAddress/WaitForAddress svc
|
||||||
|
@ -169,11 +168,17 @@ public:
|
||||||
return tls_memory;
|
return tls_memory;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/// Resumes a thread from waiting
|
||||||
* Resumes a thread from waiting
|
|
||||||
*/
|
|
||||||
void ResumeFromWait();
|
void ResumeFromWait();
|
||||||
|
|
||||||
|
/// Cancels a waiting operation that this thread may or may not be within.
|
||||||
|
///
|
||||||
|
/// When the thread is within a waiting state, this will set the thread's
|
||||||
|
/// waiting result to signal a canceled wait. The function will then resume
|
||||||
|
/// this thread.
|
||||||
|
///
|
||||||
|
void CancelWait();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Schedules an event to wake up the specified thread after the specified delay
|
* Schedules an event to wake up the specified thread after the specified delay
|
||||||
* @param nanoseconds The time this thread will be allowed to sleep for
|
* @param nanoseconds The time this thread will be allowed to sleep for
|
||||||
|
@ -184,24 +189,27 @@ public:
|
||||||
void CancelWakeupTimer();
|
void CancelWakeupTimer();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets the result after the thread awakens (from either WaitSynchronization SVC)
|
* Sets the result after the thread awakens (from svcWaitSynchronization)
|
||||||
* @param result Value to set to the returned result
|
* @param result Value to set to the returned result
|
||||||
*/
|
*/
|
||||||
void SetWaitSynchronizationResult(ResultCode result);
|
void SetWaitSynchronizationResult(ResultCode result);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets the output parameter value after the thread awakens (from WaitSynchronizationN SVC only)
|
* Sets the output parameter value after the thread awakens (from svcWaitSynchronization)
|
||||||
* @param output Value to set to the output parameter
|
* @param output Value to set to the output parameter
|
||||||
*/
|
*/
|
||||||
void SetWaitSynchronizationOutput(s32 output);
|
void SetWaitSynchronizationOutput(s32 output);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Retrieves the index that this particular object occupies in the list of objects
|
* Retrieves the index that this particular object occupies in the list of objects
|
||||||
* that the thread passed to WaitSynchronizationN, starting the search from the last element.
|
* that the thread passed to WaitSynchronization, starting the search from the last element.
|
||||||
* It is used to set the output value of WaitSynchronizationN when the thread is awakened.
|
*
|
||||||
|
* It is used to set the output index of WaitSynchronization when the thread is awakened.
|
||||||
|
*
|
||||||
* When a thread wakes up due to an object signal, the kernel will use the index of the last
|
* When a thread wakes up due to an object signal, the kernel will use the index of the last
|
||||||
* matching object in the wait objects list in case of having multiple instances of the same
|
* matching object in the wait objects list in case of having multiple instances of the same
|
||||||
* object in the list.
|
* object in the list.
|
||||||
|
*
|
||||||
* @param object Object to query the index of.
|
* @param object Object to query the index of.
|
||||||
*/
|
*/
|
||||||
s32 GetWaitObjectIndex(const WaitObject* object) const;
|
s32 GetWaitObjectIndex(const WaitObject* object) const;
|
||||||
|
@ -238,13 +246,9 @@ public:
|
||||||
*/
|
*/
|
||||||
VAddr GetCommandBufferAddress() const;
|
VAddr GetCommandBufferAddress() const;
|
||||||
|
|
||||||
/**
|
/// Returns whether this thread is waiting on objects from a WaitSynchronization call.
|
||||||
* Returns whether this thread is waiting for all the objects in
|
bool IsSleepingOnWait() const {
|
||||||
* its wait list to become ready, as a result of a WaitSynchronizationN call
|
return status == ThreadStatus::WaitSynch;
|
||||||
* with wait_all = true.
|
|
||||||
*/
|
|
||||||
bool IsSleepingOnWaitAll() const {
|
|
||||||
return status == ThreadStatus::WaitSynchAll;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ThreadContext& GetContext() {
|
ThreadContext& GetContext() {
|
||||||
|
@ -418,7 +422,7 @@ private:
|
||||||
Process* owner_process;
|
Process* owner_process;
|
||||||
|
|
||||||
/// Objects that the thread is waiting on, in the same order as they were
|
/// Objects that the thread is waiting on, in the same order as they were
|
||||||
/// passed to WaitSynchronization1/N.
|
/// passed to WaitSynchronization.
|
||||||
ThreadWaitObjects wait_objects;
|
ThreadWaitObjects wait_objects;
|
||||||
|
|
||||||
/// List of threads that are waiting for a mutex that is held by this thread.
|
/// List of threads that are waiting for a mutex that is held by this thread.
|
||||||
|
@ -441,7 +445,7 @@ private:
|
||||||
Handle callback_handle = 0;
|
Handle callback_handle = 0;
|
||||||
|
|
||||||
/// Callback that will be invoked when the thread is resumed from a waiting state. If the thread
|
/// Callback that will be invoked when the thread is resumed from a waiting state. If the thread
|
||||||
/// was waiting via WaitSynchronizationN then the object will be the last object that became
|
/// was waiting via WaitSynchronization then the object will be the last object that became
|
||||||
/// available. In case of a timeout, the object will be nullptr.
|
/// available. In case of a timeout, the object will be nullptr.
|
||||||
WakeupCallback wakeup_callback;
|
WakeupCallback wakeup_callback;
|
||||||
|
|
||||||
|
|
|
@ -38,8 +38,7 @@ SharedPtr<Thread> WaitObject::GetHighestPriorityReadyThread() const {
|
||||||
const ThreadStatus thread_status = thread->GetStatus();
|
const ThreadStatus thread_status = thread->GetStatus();
|
||||||
|
|
||||||
// The list of waiting threads must not contain threads that are not waiting to be awakened.
|
// The list of waiting threads must not contain threads that are not waiting to be awakened.
|
||||||
ASSERT_MSG(thread_status == ThreadStatus::WaitSynchAny ||
|
ASSERT_MSG(thread_status == ThreadStatus::WaitSynch ||
|
||||||
thread_status == ThreadStatus::WaitSynchAll ||
|
|
||||||
thread_status == ThreadStatus::WaitHLEEvent,
|
thread_status == ThreadStatus::WaitHLEEvent,
|
||||||
"Inconsistent thread statuses in waiting_threads");
|
"Inconsistent thread statuses in waiting_threads");
|
||||||
|
|
||||||
|
@ -49,10 +48,10 @@ SharedPtr<Thread> WaitObject::GetHighestPriorityReadyThread() const {
|
||||||
if (ShouldWait(thread.get()))
|
if (ShouldWait(thread.get()))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
// A thread is ready to run if it's either in ThreadStatus::WaitSynchAny or
|
// A thread is ready to run if it's either in ThreadStatus::WaitSynch
|
||||||
// in ThreadStatus::WaitSynchAll and the rest of the objects it is waiting on are ready.
|
// and the rest of the objects it is waiting on are ready.
|
||||||
bool ready_to_run = true;
|
bool ready_to_run = true;
|
||||||
if (thread_status == ThreadStatus::WaitSynchAll) {
|
if (thread_status == ThreadStatus::WaitSynch) {
|
||||||
ready_to_run = thread->AllWaitObjectsReady();
|
ready_to_run = thread->AllWaitObjectsReady();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -68,33 +67,35 @@ SharedPtr<Thread> WaitObject::GetHighestPriorityReadyThread() const {
|
||||||
void WaitObject::WakeupWaitingThread(SharedPtr<Thread> thread) {
|
void WaitObject::WakeupWaitingThread(SharedPtr<Thread> thread) {
|
||||||
ASSERT(!ShouldWait(thread.get()));
|
ASSERT(!ShouldWait(thread.get()));
|
||||||
|
|
||||||
if (!thread)
|
if (!thread) {
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (!thread->IsSleepingOnWaitAll()) {
|
if (thread->IsSleepingOnWait()) {
|
||||||
Acquire(thread.get());
|
|
||||||
} else {
|
|
||||||
for (const auto& object : thread->GetWaitObjects()) {
|
for (const auto& object : thread->GetWaitObjects()) {
|
||||||
ASSERT(!object->ShouldWait(thread.get()));
|
ASSERT(!object->ShouldWait(thread.get()));
|
||||||
object->Acquire(thread.get());
|
object->Acquire(thread.get());
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
Acquire(thread.get());
|
||||||
}
|
}
|
||||||
|
|
||||||
const std::size_t index = thread->GetWaitObjectIndex(this);
|
const std::size_t index = thread->GetWaitObjectIndex(this);
|
||||||
|
|
||||||
for (const auto& object : thread->GetWaitObjects())
|
for (const auto& object : thread->GetWaitObjects()) {
|
||||||
object->RemoveWaitingThread(thread.get());
|
object->RemoveWaitingThread(thread.get());
|
||||||
|
}
|
||||||
thread->ClearWaitObjects();
|
thread->ClearWaitObjects();
|
||||||
|
|
||||||
thread->CancelWakeupTimer();
|
thread->CancelWakeupTimer();
|
||||||
|
|
||||||
bool resume = true;
|
bool resume = true;
|
||||||
|
if (thread->HasWakeupCallback()) {
|
||||||
if (thread->HasWakeupCallback())
|
|
||||||
resume = thread->InvokeWakeupCallback(ThreadWakeupReason::Signal, thread, this, index);
|
resume = thread->InvokeWakeupCallback(ThreadWakeupReason::Signal, thread, this, index);
|
||||||
|
}
|
||||||
if (resume)
|
if (resume) {
|
||||||
thread->ResumeFromWait();
|
thread->ResumeFromWait();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void WaitObject::WakeupAllWaitingThreads() {
|
void WaitObject::WakeupAllWaitingThreads() {
|
||||||
|
|
|
@ -227,8 +227,7 @@ QString WaitTreeThread::GetText() const {
|
||||||
case Kernel::ThreadStatus::WaitIPC:
|
case Kernel::ThreadStatus::WaitIPC:
|
||||||
status = tr("waiting for IPC reply");
|
status = tr("waiting for IPC reply");
|
||||||
break;
|
break;
|
||||||
case Kernel::ThreadStatus::WaitSynchAll:
|
case Kernel::ThreadStatus::WaitSynch:
|
||||||
case Kernel::ThreadStatus::WaitSynchAny:
|
|
||||||
status = tr("waiting for objects");
|
status = tr("waiting for objects");
|
||||||
break;
|
break;
|
||||||
case Kernel::ThreadStatus::WaitMutex:
|
case Kernel::ThreadStatus::WaitMutex:
|
||||||
|
@ -269,8 +268,7 @@ QColor WaitTreeThread::GetColor() const {
|
||||||
return QColor(Qt::GlobalColor::darkRed);
|
return QColor(Qt::GlobalColor::darkRed);
|
||||||
case Kernel::ThreadStatus::WaitSleep:
|
case Kernel::ThreadStatus::WaitSleep:
|
||||||
return QColor(Qt::GlobalColor::darkYellow);
|
return QColor(Qt::GlobalColor::darkYellow);
|
||||||
case Kernel::ThreadStatus::WaitSynchAll:
|
case Kernel::ThreadStatus::WaitSynch:
|
||||||
case Kernel::ThreadStatus::WaitSynchAny:
|
|
||||||
case Kernel::ThreadStatus::WaitMutex:
|
case Kernel::ThreadStatus::WaitMutex:
|
||||||
case Kernel::ThreadStatus::WaitCondVar:
|
case Kernel::ThreadStatus::WaitCondVar:
|
||||||
case Kernel::ThreadStatus::WaitArb:
|
case Kernel::ThreadStatus::WaitArb:
|
||||||
|
@ -325,10 +323,9 @@ std::vector<std::unique_ptr<WaitTreeItem>> WaitTreeThread::GetChildren() const {
|
||||||
list.push_back(std::make_unique<WaitTreeText>(tr("not waiting for mutex")));
|
list.push_back(std::make_unique<WaitTreeText>(tr("not waiting for mutex")));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (thread.GetStatus() == Kernel::ThreadStatus::WaitSynchAny ||
|
if (thread.GetStatus() == Kernel::ThreadStatus::WaitSynch) {
|
||||||
thread.GetStatus() == Kernel::ThreadStatus::WaitSynchAll) {
|
|
||||||
list.push_back(std::make_unique<WaitTreeObjectList>(thread.GetWaitObjects(),
|
list.push_back(std::make_unique<WaitTreeObjectList>(thread.GetWaitObjects(),
|
||||||
thread.IsSleepingOnWaitAll()));
|
thread.IsSleepingOnWait()));
|
||||||
}
|
}
|
||||||
|
|
||||||
list.push_back(std::make_unique<WaitTreeCallstack>(thread));
|
list.push_back(std::make_unique<WaitTreeCallstack>(thread));
|
||||||
|
|
Loading…
Reference in a new issue