mirror of
https://github.com/yuzu-mirror/yuzu.git
synced 2024-09-07 12:39:09 +01:00
SVC: Correct SignalEvent, ClearEvent, ResetSignal, WaitSynchronization, CancelSynchronization, ArbitrateLock
This commit is contained in:
parent
ef4afa9760
commit
3b5b950c89
8 changed files with 136 additions and 92 deletions
|
@ -72,42 +72,55 @@ ResultCode Mutex::TryAcquire(VAddr address, Handle holding_thread_handle,
|
||||||
return ERR_INVALID_ADDRESS;
|
return ERR_INVALID_ADDRESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
const auto& handle_table = system.Kernel().CurrentProcess()->GetHandleTable();
|
auto& kernel = system.Kernel();
|
||||||
std::shared_ptr<Thread> current_thread =
|
std::shared_ptr<Thread> current_thread =
|
||||||
SharedFrom(system.CurrentScheduler().GetCurrentThread());
|
SharedFrom(kernel.CurrentScheduler().GetCurrentThread());
|
||||||
std::shared_ptr<Thread> holding_thread = handle_table.Get<Thread>(holding_thread_handle);
|
{
|
||||||
std::shared_ptr<Thread> requesting_thread = handle_table.Get<Thread>(requesting_thread_handle);
|
SchedulerLock lock(kernel);
|
||||||
|
// The mutex address must be 4-byte aligned
|
||||||
|
if ((address % sizeof(u32)) != 0) {
|
||||||
|
return ERR_INVALID_ADDRESS;
|
||||||
|
}
|
||||||
|
|
||||||
// TODO(Subv): It is currently unknown if it is possible to lock a mutex in behalf of another
|
const auto& handle_table = kernel.CurrentProcess()->GetHandleTable();
|
||||||
// thread.
|
std::shared_ptr<Thread> holding_thread = handle_table.Get<Thread>(holding_thread_handle);
|
||||||
ASSERT(requesting_thread == current_thread);
|
std::shared_ptr<Thread> requesting_thread = handle_table.Get<Thread>(requesting_thread_handle);
|
||||||
|
|
||||||
const u32 addr_value = system.Memory().Read32(address);
|
// TODO(Subv): It is currently unknown if it is possible to lock a mutex in behalf of another
|
||||||
|
// thread.
|
||||||
|
ASSERT(requesting_thread == current_thread);
|
||||||
|
|
||||||
// If the mutex isn't being held, just return success.
|
current_thread->SetSynchronizationResults(nullptr, RESULT_SUCCESS);
|
||||||
if (addr_value != (holding_thread_handle | Mutex::MutexHasWaitersFlag)) {
|
|
||||||
return RESULT_SUCCESS;
|
const u32 addr_value = system.Memory().Read32(address);
|
||||||
|
|
||||||
|
// If the mutex isn't being held, just return success.
|
||||||
|
if (addr_value != (holding_thread_handle | Mutex::MutexHasWaitersFlag)) {
|
||||||
|
return RESULT_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (holding_thread == nullptr) {
|
||||||
|
return ERR_INVALID_HANDLE;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Wait until the mutex is released
|
||||||
|
current_thread->SetMutexWaitAddress(address);
|
||||||
|
current_thread->SetWaitHandle(requesting_thread_handle);
|
||||||
|
|
||||||
|
current_thread->SetStatus(ThreadStatus::WaitMutex);
|
||||||
|
|
||||||
|
// Update the lock holder thread's priority to prevent priority inversion.
|
||||||
|
holding_thread->AddMutexWaiter(current_thread);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (holding_thread == nullptr) {
|
{
|
||||||
LOG_ERROR(Kernel, "Holding thread does not exist! thread_handle={:08X}",
|
SchedulerLock lock(kernel);
|
||||||
holding_thread_handle);
|
auto* owner = current_thread->GetLockOwner();
|
||||||
return ERR_INVALID_HANDLE;
|
if (owner != nullptr) {
|
||||||
|
owner->RemoveMutexWaiter(current_thread);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
return current_thread->GetSignalingResult();
|
||||||
// Wait until the mutex is released
|
|
||||||
current_thread->SetMutexWaitAddress(address);
|
|
||||||
current_thread->SetWaitHandle(requesting_thread_handle);
|
|
||||||
|
|
||||||
current_thread->SetStatus(ThreadStatus::WaitMutex);
|
|
||||||
current_thread->InvalidateWakeupCallback();
|
|
||||||
|
|
||||||
// Update the lock holder thread's priority to prevent priority inversion.
|
|
||||||
holding_thread->AddMutexWaiter(current_thread);
|
|
||||||
|
|
||||||
system.PrepareReschedule();
|
|
||||||
|
|
||||||
return RESULT_SUCCESS;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ResultCode Mutex::Release(VAddr address) {
|
ResultCode Mutex::Release(VAddr address) {
|
||||||
|
|
|
@ -212,6 +212,7 @@ void Process::UnregisterThread(const Thread* thread) {
|
||||||
}
|
}
|
||||||
|
|
||||||
ResultCode Process::ClearSignalState() {
|
ResultCode Process::ClearSignalState() {
|
||||||
|
SchedulerLock lock(system.Kernel());
|
||||||
if (status == ProcessStatus::Exited) {
|
if (status == ProcessStatus::Exited) {
|
||||||
LOG_ERROR(Kernel, "called on a terminated process instance.");
|
LOG_ERROR(Kernel, "called on a terminated process instance.");
|
||||||
return ERR_INVALID_STATE;
|
return ERR_INVALID_STATE;
|
||||||
|
|
|
@ -6,8 +6,10 @@
|
||||||
#include "common/assert.h"
|
#include "common/assert.h"
|
||||||
#include "common/logging/log.h"
|
#include "common/logging/log.h"
|
||||||
#include "core/hle/kernel/errors.h"
|
#include "core/hle/kernel/errors.h"
|
||||||
|
#include "core/hle/kernel/kernel.h"
|
||||||
#include "core/hle/kernel/object.h"
|
#include "core/hle/kernel/object.h"
|
||||||
#include "core/hle/kernel/readable_event.h"
|
#include "core/hle/kernel/readable_event.h"
|
||||||
|
#include "core/hle/kernel/scheduler.h"
|
||||||
#include "core/hle/kernel/thread.h"
|
#include "core/hle/kernel/thread.h"
|
||||||
|
|
||||||
namespace Kernel {
|
namespace Kernel {
|
||||||
|
@ -37,6 +39,7 @@ void ReadableEvent::Clear() {
|
||||||
}
|
}
|
||||||
|
|
||||||
ResultCode ReadableEvent::Reset() {
|
ResultCode ReadableEvent::Reset() {
|
||||||
|
SchedulerLock lock(kernel);
|
||||||
if (!is_signaled) {
|
if (!is_signaled) {
|
||||||
LOG_TRACE(Kernel, "Handle is not signaled! object_id={}, object_type={}, object_name={}",
|
LOG_TRACE(Kernel, "Handle is not signaled! object_id={}, object_type={}, object_name={}",
|
||||||
GetObjectId(), GetTypeName(), GetName());
|
GetObjectId(), GetTypeName(), GetName());
|
||||||
|
|
|
@ -448,7 +448,6 @@ static ResultCode CancelSynchronization(Core::System& system, Handle thread_hand
|
||||||
}
|
}
|
||||||
|
|
||||||
thread->CancelWait();
|
thread->CancelWait();
|
||||||
system.PrepareReschedule(thread->GetProcessorID());
|
|
||||||
return RESULT_SUCCESS;
|
return RESULT_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -10,78 +10,88 @@
|
||||||
#include "core/hle/kernel/synchronization.h"
|
#include "core/hle/kernel/synchronization.h"
|
||||||
#include "core/hle/kernel/synchronization_object.h"
|
#include "core/hle/kernel/synchronization_object.h"
|
||||||
#include "core/hle/kernel/thread.h"
|
#include "core/hle/kernel/thread.h"
|
||||||
|
#include "core/hle/kernel/time_manager.h"
|
||||||
|
|
||||||
namespace Kernel {
|
namespace Kernel {
|
||||||
|
|
||||||
/// Default thread wakeup callback for WaitSynchronization
|
|
||||||
static bool DefaultThreadWakeupCallback(ThreadWakeupReason reason, std::shared_ptr<Thread> thread,
|
|
||||||
std::shared_ptr<SynchronizationObject> object,
|
|
||||||
std::size_t index) {
|
|
||||||
ASSERT(thread->GetStatus() == ThreadStatus::WaitSynch);
|
|
||||||
|
|
||||||
if (reason == ThreadWakeupReason::Timeout) {
|
|
||||||
thread->SetWaitSynchronizationResult(RESULT_TIMEOUT);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
ASSERT(reason == ThreadWakeupReason::Signal);
|
|
||||||
thread->SetWaitSynchronizationResult(RESULT_SUCCESS);
|
|
||||||
thread->SetWaitSynchronizationOutput(static_cast<u32>(index));
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
Synchronization::Synchronization(Core::System& system) : system{system} {}
|
Synchronization::Synchronization(Core::System& system) : system{system} {}
|
||||||
|
|
||||||
void Synchronization::SignalObject(SynchronizationObject& obj) const {
|
void Synchronization::SignalObject(SynchronizationObject& obj) const {
|
||||||
|
SchedulerLock lock(system.Kernel());
|
||||||
if (obj.IsSignaled()) {
|
if (obj.IsSignaled()) {
|
||||||
obj.WakeupAllWaitingThreads();
|
for (auto thread : obj.GetWaitingThreads()) {
|
||||||
|
if (thread->GetSchedulingStatus() == ThreadSchedStatus::Paused) {
|
||||||
|
thread->SetSynchronizationResults(&obj, RESULT_SUCCESS);
|
||||||
|
thread->ResumeFromWait();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
std::pair<ResultCode, Handle> Synchronization::WaitFor(
|
std::pair<ResultCode, Handle> Synchronization::WaitFor(
|
||||||
std::vector<std::shared_ptr<SynchronizationObject>>& sync_objects, s64 nano_seconds) {
|
std::vector<std::shared_ptr<SynchronizationObject>>& sync_objects, s64 nano_seconds) {
|
||||||
|
auto& kernel = system.Kernel();
|
||||||
auto* const thread = system.CurrentScheduler().GetCurrentThread();
|
auto* const thread = system.CurrentScheduler().GetCurrentThread();
|
||||||
// Find the first object that is acquirable in the provided list of objects
|
Handle event_handle = InvalidHandle;
|
||||||
const auto itr = std::find_if(sync_objects.begin(), sync_objects.end(),
|
{
|
||||||
[thread](const std::shared_ptr<SynchronizationObject>& object) {
|
SchedulerLockAndSleep lock(kernel, event_handle, thread, nano_seconds);
|
||||||
return object->IsSignaled();
|
const auto itr =
|
||||||
});
|
std::find_if(sync_objects.begin(), sync_objects.end(),
|
||||||
|
[thread](const std::shared_ptr<SynchronizationObject>& object) {
|
||||||
|
return object->IsSignaled();
|
||||||
|
});
|
||||||
|
|
||||||
if (itr != sync_objects.end()) {
|
if (itr != sync_objects.end()) {
|
||||||
// We found a ready object, acquire it and set the result value
|
// We found a ready object, acquire it and set the result value
|
||||||
SynchronizationObject* object = itr->get();
|
SynchronizationObject* object = itr->get();
|
||||||
object->Acquire(thread);
|
object->Acquire(thread);
|
||||||
const u32 index = static_cast<s32>(std::distance(sync_objects.begin(), itr));
|
const u32 index = static_cast<s32>(std::distance(sync_objects.begin(), itr));
|
||||||
return {RESULT_SUCCESS, index};
|
lock.CancelSleep();
|
||||||
|
return {RESULT_SUCCESS, index};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (nano_seconds == 0) {
|
||||||
|
lock.CancelSleep();
|
||||||
|
return {RESULT_TIMEOUT, InvalidHandle};
|
||||||
|
}
|
||||||
|
|
||||||
|
/// TODO(Blinkhawk): Check for termination pending
|
||||||
|
|
||||||
|
if (thread->IsSyncCancelled()) {
|
||||||
|
thread->SetSyncCancelled(false);
|
||||||
|
lock.CancelSleep();
|
||||||
|
return {ERR_SYNCHRONIZATION_CANCELED, InvalidHandle};
|
||||||
|
}
|
||||||
|
|
||||||
|
for (auto& object : sync_objects) {
|
||||||
|
object->AddWaitingThread(SharedFrom(thread));
|
||||||
|
}
|
||||||
|
thread->SetSynchronizationResults(nullptr, RESULT_TIMEOUT);
|
||||||
|
thread->SetStatus(ThreadStatus::WaitSynch);
|
||||||
}
|
}
|
||||||
|
|
||||||
// No objects were ready to be acquired, prepare to suspend the thread.
|
if (event_handle != InvalidHandle) {
|
||||||
|
auto& time_manager = kernel.TimeManager();
|
||||||
// If a timeout value of 0 was provided, just return the Timeout error code instead of
|
time_manager.UnscheduleTimeEvent(event_handle);
|
||||||
// suspending the thread.
|
|
||||||
if (nano_seconds == 0) {
|
|
||||||
return {RESULT_TIMEOUT, InvalidHandle};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (thread->IsSyncCancelled()) {
|
{
|
||||||
thread->SetSyncCancelled(false);
|
SchedulerLock lock(kernel);
|
||||||
return {ERR_SYNCHRONIZATION_CANCELED, InvalidHandle};
|
ResultCode signaling_result = thread->GetSignalingResult();
|
||||||
|
SynchronizationObject* signaling_object = thread->GetSignalingObject();
|
||||||
|
if (signaling_result == RESULT_SUCCESS) {
|
||||||
|
const auto itr = std::find_if(
|
||||||
|
sync_objects.begin(), sync_objects.end(),
|
||||||
|
[signaling_object](const std::shared_ptr<SynchronizationObject>& object) {
|
||||||
|
return object.get() == signaling_object;
|
||||||
|
});
|
||||||
|
ASSERT(itr != sync_objects.end());
|
||||||
|
signaling_object->Acquire(thread);
|
||||||
|
const u32 index = static_cast<s32>(std::distance(sync_objects.begin(), itr));
|
||||||
|
return {RESULT_SUCCESS, index};
|
||||||
|
}
|
||||||
|
return {signaling_result, -1};
|
||||||
}
|
}
|
||||||
|
|
||||||
for (auto& object : sync_objects) {
|
|
||||||
object->AddWaitingThread(SharedFrom(thread));
|
|
||||||
}
|
|
||||||
|
|
||||||
thread->SetSynchronizationObjects(std::move(sync_objects));
|
|
||||||
thread->SetStatus(ThreadStatus::WaitSynch);
|
|
||||||
|
|
||||||
// Create an event to wake the thread up after the specified nanosecond delay has passed
|
|
||||||
thread->WakeAfterDelay(nano_seconds);
|
|
||||||
thread->SetWakeupCallback(DefaultThreadWakeupCallback);
|
|
||||||
|
|
||||||
system.PrepareReschedule(thread->GetProcessorID());
|
|
||||||
|
|
||||||
return {RESULT_TIMEOUT, InvalidHandle};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace Kernel
|
} // namespace Kernel
|
||||||
|
|
|
@ -12,6 +12,7 @@
|
||||||
namespace Kernel {
|
namespace Kernel {
|
||||||
|
|
||||||
class KernelCore;
|
class KernelCore;
|
||||||
|
class Synchronization;
|
||||||
class Thread;
|
class Thread;
|
||||||
|
|
||||||
/// Class that represents a Kernel object that a thread can be waiting on
|
/// Class that represents a Kernel object that a thread can be waiting on
|
||||||
|
@ -53,7 +54,7 @@ public:
|
||||||
* Wake up all threads waiting on this object that can be awoken, in priority order,
|
* Wake up all threads waiting on this object that can be awoken, in priority order,
|
||||||
* and set the synchronization result and output of the thread.
|
* and set the synchronization result and output of the thread.
|
||||||
*/
|
*/
|
||||||
void WakeupAllWaitingThreads();
|
void /* deprecated */ WakeupAllWaitingThreads();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Wakes up a single thread waiting on this object.
|
* Wakes up a single thread waiting on this object.
|
||||||
|
@ -62,7 +63,7 @@ public:
|
||||||
void WakeupWaitingThread(std::shared_ptr<Thread> thread);
|
void WakeupWaitingThread(std::shared_ptr<Thread> thread);
|
||||||
|
|
||||||
/// Obtains the highest priority thread that is ready to run from this object's waiting list.
|
/// Obtains the highest priority thread that is ready to run from this object's waiting list.
|
||||||
std::shared_ptr<Thread> GetHighestPriorityReadyThread() const;
|
std::shared_ptr<Thread> /* deprecated */ GetHighestPriorityReadyThread() const;
|
||||||
|
|
||||||
/// Get a const reference to the waiting threads list for debug use
|
/// Get a const reference to the waiting threads list for debug use
|
||||||
const std::vector<std::shared_ptr<Thread>>& GetWaitingThreads() const;
|
const std::vector<std::shared_ptr<Thread>>& GetWaitingThreads() const;
|
||||||
|
|
|
@ -139,12 +139,13 @@ ResultCode Thread::Start() {
|
||||||
}
|
}
|
||||||
|
|
||||||
void Thread::CancelWait() {
|
void Thread::CancelWait() {
|
||||||
|
SchedulerLock lock(kernel);
|
||||||
if (GetSchedulingStatus() != ThreadSchedStatus::Paused) {
|
if (GetSchedulingStatus() != ThreadSchedStatus::Paused) {
|
||||||
is_sync_cancelled = true;
|
is_sync_cancelled = true;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
is_sync_cancelled = false;
|
is_sync_cancelled = false;
|
||||||
SetWaitSynchronizationResult(ERR_SYNCHRONIZATION_CANCELED);
|
SetSynchronizationResults(nullptr, ERR_SYNCHRONIZATION_CANCELED);
|
||||||
ResumeFromWait();
|
ResumeFromWait();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -258,13 +259,16 @@ void Thread::SetPriority(u32 priority) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void Thread::SetWaitSynchronizationResult(ResultCode result) {
|
void Thread::SetWaitSynchronizationResult(ResultCode result) {
|
||||||
context_32.cpu_registers[0] = result.raw;
|
UNREACHABLE();
|
||||||
context_64.cpu_registers[0] = result.raw;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Thread::SetWaitSynchronizationOutput(s32 output) {
|
void Thread::SetWaitSynchronizationOutput(s32 output) {
|
||||||
context_32.cpu_registers[1] = output;
|
UNREACHABLE();
|
||||||
context_64.cpu_registers[1] = output;
|
}
|
||||||
|
|
||||||
|
void Thread::SetSynchronizationResults(SynchronizationObject* object, ResultCode result) {
|
||||||
|
signaling_object = object;
|
||||||
|
signaling_result = result;
|
||||||
}
|
}
|
||||||
|
|
||||||
s32 Thread::GetSynchronizationObjectIndex(std::shared_ptr<SynchronizationObject> object) const {
|
s32 Thread::GetSynchronizationObjectIndex(std::shared_ptr<SynchronizationObject> object) const {
|
||||||
|
|
|
@ -259,13 +259,23 @@ public:
|
||||||
* Sets the result after the thread awakens (from svcWaitSynchronization)
|
* 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 /*deprecated*/ SetWaitSynchronizationResult(ResultCode result);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets the output parameter value after the thread awakens (from svcWaitSynchronization)
|
* 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 /*deprecated*/ SetWaitSynchronizationOutput(s32 output);
|
||||||
|
|
||||||
|
void SetSynchronizationResults(SynchronizationObject* object, ResultCode result);
|
||||||
|
|
||||||
|
SynchronizationObject* GetSignalingObject() const {
|
||||||
|
return signaling_object;
|
||||||
|
}
|
||||||
|
|
||||||
|
ResultCode GetSignalingResult() const {
|
||||||
|
return signaling_result;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 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
|
||||||
|
@ -565,6 +575,9 @@ private:
|
||||||
/// passed to WaitSynchronization.
|
/// passed to WaitSynchronization.
|
||||||
ThreadSynchronizationObjects wait_objects;
|
ThreadSynchronizationObjects wait_objects;
|
||||||
|
|
||||||
|
SynchronizationObject* signaling_object;
|
||||||
|
ResultCode signaling_result{RESULT_SUCCESS};
|
||||||
|
|
||||||
/// 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.
|
||||||
MutexWaitingThreads wait_mutex_threads;
|
MutexWaitingThreads wait_mutex_threads;
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue