2
1
Fork 0
mirror of https://github.com/yuzu-emu/yuzu.git synced 2024-07-04 23:31:19 +01:00

SVC: Correct WaitSynchronization, WaitProcessWideKey, SignalProcessWideKey.

This commit is contained in:
Fernando Sahmkow 2020-02-26 22:26:53 -04:00
parent 5b6a67f849
commit d4ebb510a0
9 changed files with 84 additions and 33 deletions

View file

@ -187,7 +187,6 @@ void Process::RemoveConditionVariableThread(std::shared_ptr<Thread> thread) {
} }
++it; ++it;
} }
UNREACHABLE();
} }
std::vector<std::shared_ptr<Thread>> Process::GetConditionVariableThreads( std::vector<std::shared_ptr<Thread>> Process::GetConditionVariableThreads(

View file

@ -632,7 +632,7 @@ void Scheduler::SwitchContext() {
cpu_core.SaveContext(previous_thread->GetContext64()); cpu_core.SaveContext(previous_thread->GetContext64());
// Save the TPIDR_EL0 system register in case it was modified. // Save the TPIDR_EL0 system register in case it was modified.
previous_thread->SetTPIDR_EL0(cpu_core.GetTPIDR_EL0()); previous_thread->SetTPIDR_EL0(cpu_core.GetTPIDR_EL0());
cpu_core.ClearExclusiveState();
} }
if (previous_thread->GetStatus() == ThreadStatus::Running) { if (previous_thread->GetStatus() == ThreadStatus::Running) {
previous_thread->SetStatus(ThreadStatus::Ready); previous_thread->SetStatus(ThreadStatus::Ready);

View file

@ -1541,33 +1541,50 @@ static ResultCode WaitProcessWideKeyAtomic(Core::System& system, VAddr mutex_add
return ERR_INVALID_ADDRESS; return ERR_INVALID_ADDRESS;
} }
UNIMPLEMENTED();
ASSERT(condition_variable_addr == Common::AlignDown(condition_variable_addr, 4)); ASSERT(condition_variable_addr == Common::AlignDown(condition_variable_addr, 4));
auto& kernel = system.Kernel();
Handle event_handle;
Thread* current_thread = system.CurrentScheduler().GetCurrentThread();
auto* const current_process = system.Kernel().CurrentProcess(); auto* const current_process = system.Kernel().CurrentProcess();
const auto& handle_table = current_process->GetHandleTable(); {
std::shared_ptr<Thread> thread = handle_table.Get<Thread>(thread_handle); SchedulerLockAndSleep lock(kernel, event_handle, current_thread, nano_seconds);
ASSERT(thread); const auto& handle_table = current_process->GetHandleTable();
std::shared_ptr<Thread> thread = handle_table.Get<Thread>(thread_handle);
ASSERT(thread);
const auto release_result = current_process->GetMutex().Release(mutex_addr); current_thread->SetSynchronizationResults(nullptr, RESULT_TIMEOUT);
if (release_result.IsError()) {
return release_result; const auto release_result = current_process->GetMutex().Release(mutex_addr);
if (release_result.IsError()) {
lock.CancelSleep();
return release_result;
}
if (nano_seconds == 0) {
lock.CancelSleep();
return RESULT_TIMEOUT;
}
current_thread->SetCondVarWaitAddress(condition_variable_addr);
current_thread->SetMutexWaitAddress(mutex_addr);
current_thread->SetWaitHandle(thread_handle);
current_thread->SetStatus(ThreadStatus::WaitCondVar);
current_process->InsertConditionVariableThread(SharedFrom(current_thread));
} }
Thread* current_thread = system.CurrentScheduler().GetCurrentThread(); if (event_handle != InvalidHandle) {
current_thread->SetCondVarWaitAddress(condition_variable_addr); auto& time_manager = kernel.TimeManager();
current_thread->SetMutexWaitAddress(mutex_addr); time_manager.UnscheduleTimeEvent(event_handle);
current_thread->SetWaitHandle(thread_handle); }
current_thread->SetStatus(ThreadStatus::WaitCondVar);
current_thread->InvalidateWakeupCallback();
current_process->InsertConditionVariableThread(SharedFrom(current_thread));
current_thread->WakeAfterDelay(nano_seconds); {
SchedulerLock lock(kernel);
current_process->RemoveConditionVariableThread(SharedFrom(current_thread));
}
// Note: Deliberately don't attempt to inherit the lock owner's priority. // Note: Deliberately don't attempt to inherit the lock owner's priority.
return RESULT_SUCCESS; return current_thread->GetSignalingResult();
} }
/// Signal process wide key /// Signal process wide key
@ -1577,10 +1594,10 @@ static void SignalProcessWideKey(Core::System& system, VAddr condition_variable_
ASSERT(condition_variable_addr == Common::AlignDown(condition_variable_addr, 4)); ASSERT(condition_variable_addr == Common::AlignDown(condition_variable_addr, 4));
UNIMPLEMENTED();
// Retrieve a list of all threads that are waiting for this condition variable. // Retrieve a list of all threads that are waiting for this condition variable.
auto* const current_process = system.Kernel().CurrentProcess(); auto& kernel = system.Kernel();
SchedulerLock lock(kernel);
auto* const current_process = kernel.CurrentProcess();
std::vector<std::shared_ptr<Thread>> waiting_threads = std::vector<std::shared_ptr<Thread>> waiting_threads =
current_process->GetConditionVariableThreads(condition_variable_addr); current_process->GetConditionVariableThreads(condition_variable_addr);
@ -1589,10 +1606,18 @@ static void SignalProcessWideKey(Core::System& system, VAddr condition_variable_
std::size_t last = waiting_threads.size(); std::size_t last = waiting_threads.size();
if (target > 0) if (target > 0)
last = std::min(waiting_threads.size(), static_cast<std::size_t>(target)); last = std::min(waiting_threads.size(), static_cast<std::size_t>(target));
auto& time_manager = kernel.TimeManager();
for (std::size_t index = 0; index < last; ++index) { for (std::size_t index = 0; index < last; ++index) {
auto& thread = waiting_threads[index]; auto& thread = waiting_threads[index];
if (thread->GetStatus() != ThreadStatus::WaitCondVar) {
last++;
last = std::min(waiting_threads.size(), last);
continue;
}
time_manager.CancelTimeEvent(thread.get());
ASSERT(thread->GetCondVarWaitAddress() == condition_variable_addr); ASSERT(thread->GetCondVarWaitAddress() == condition_variable_addr);
// liberate Cond Var Thread. // liberate Cond Var Thread.
@ -1630,17 +1655,13 @@ static void SignalProcessWideKey(Core::System& system, VAddr condition_variable_
} }
thread->SetLockOwner(nullptr); thread->SetLockOwner(nullptr);
thread->SetMutexWaitAddress(0); thread->SetSynchronizationResults(nullptr, RESULT_SUCCESS);
thread->SetWaitHandle(0);
thread->SetWaitSynchronizationResult(RESULT_SUCCESS);
} else { } else {
// The mutex is already owned by some other thread, make this thread wait on it. // The mutex is already owned by some other thread, make this thread wait on it.
const Handle owner_handle = static_cast<Handle>(mutex_val & Mutex::MutexOwnerMask); const Handle owner_handle = static_cast<Handle>(mutex_val & Mutex::MutexOwnerMask);
const auto& handle_table = system.Kernel().CurrentProcess()->GetHandleTable(); const auto& handle_table = system.Kernel().CurrentProcess()->GetHandleTable();
auto owner = handle_table.Get<Thread>(owner_handle); auto owner = handle_table.Get<Thread>(owner_handle);
ASSERT(owner); ASSERT(owner);
ASSERT(thread->GetStatus() == ThreadStatus::WaitCondVar);
thread->InvalidateWakeupCallback();
thread->SetStatus(ThreadStatus::WaitMutex); thread->SetStatus(ThreadStatus::WaitMutex);
owner->AddMutexWaiter(thread); owner->AddMutexWaiter(thread);

View file

@ -17,12 +17,15 @@ namespace Kernel {
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()); auto& kernel = system.Kernel();
SchedulerLock lock(kernel);
auto& time_manager = kernel.TimeManager();
if (obj.IsSignaled()) { if (obj.IsSignaled()) {
for (auto thread : obj.GetWaitingThreads()) { for (auto thread : obj.GetWaitingThreads()) {
if (thread->GetSchedulingStatus() == ThreadSchedStatus::Paused) { if (thread->GetSchedulingStatus() == ThreadSchedStatus::Paused) {
thread->SetSynchronizationResults(&obj, RESULT_SUCCESS); thread->SetSynchronizationResults(&obj, RESULT_SUCCESS);
thread->ResumeFromWait(); thread->ResumeFromWait();
time_manager.CancelTimeEvent(thread.get());
} }
} }
} }
@ -79,6 +82,9 @@ std::pair<ResultCode, Handle> Synchronization::WaitFor(
SchedulerLock lock(kernel); SchedulerLock lock(kernel);
ResultCode signaling_result = thread->GetSignalingResult(); ResultCode signaling_result = thread->GetSignalingResult();
SynchronizationObject* signaling_object = thread->GetSignalingObject(); SynchronizationObject* signaling_object = thread->GetSignalingObject();
for (auto& obj : sync_objects) {
obj->RemoveWaitingThread(SharedFrom(thread));
}
if (signaling_result == RESULT_SUCCESS) { if (signaling_result == RESULT_SUCCESS) {
const auto itr = std::find_if( const auto itr = std::find_if(
sync_objects.begin(), sync_objects.end(), sync_objects.begin(), sync_objects.end(),

View file

@ -102,6 +102,10 @@ void SynchronizationObject::WakeupAllWaitingThreads() {
} }
} }
void SynchronizationObject::ClearWaitingThreads() {
waiting_threads.clear();
}
const std::vector<std::shared_ptr<Thread>>& SynchronizationObject::GetWaitingThreads() const { const std::vector<std::shared_ptr<Thread>>& SynchronizationObject::GetWaitingThreads() const {
return waiting_threads; return waiting_threads;
} }

View file

@ -68,6 +68,8 @@ public:
/// 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;
void ClearWaitingThreads();
protected: protected:
bool is_signaled{}; // Tells if this sync object is signalled; bool is_signaled{}; // Tells if this sync object is signalled;

View file

@ -49,12 +49,12 @@ Thread::~Thread() = default;
void Thread::Stop() { void Thread::Stop() {
SchedulerLock lock(kernel); SchedulerLock lock(kernel);
// Cancel any outstanding wakeup events for this thread // Cancel any outstanding wakeup events for this thread
Signal();
Core::System::GetInstance().CoreTiming().UnscheduleEvent(kernel.ThreadWakeupCallbackEventType(), Core::System::GetInstance().CoreTiming().UnscheduleEvent(kernel.ThreadWakeupCallbackEventType(),
global_handle); global_handle);
kernel.GlobalHandleTable().Close(global_handle); kernel.GlobalHandleTable().Close(global_handle);
global_handle = 0; global_handle = 0;
SetStatus(ThreadStatus::Dead); SetStatus(ThreadStatus::Dead);
Signal();
owner_process->UnregisterThread(this); owner_process->UnregisterThread(this);

View file

@ -8,15 +8,21 @@
#include "core/core_timing_util.h" #include "core/core_timing_util.h"
#include "core/hle/kernel/handle_table.h" #include "core/hle/kernel/handle_table.h"
#include "core/hle/kernel/kernel.h" #include "core/hle/kernel/kernel.h"
#include "core/hle/kernel/scheduler.h"
#include "core/hle/kernel/thread.h" #include "core/hle/kernel/thread.h"
#include "core/hle/kernel/time_manager.h" #include "core/hle/kernel/time_manager.h"
namespace Kernel { namespace Kernel {
TimeManager::TimeManager(Core::System& system) : system{system} { TimeManager::TimeManager(Core::System& system_) : system{system_} {
time_manager_event_type = Core::Timing::CreateEvent( time_manager_event_type = Core::Timing::CreateEvent(
"Kernel::TimeManagerCallback", [this](u64 thread_handle, [[maybe_unused]] s64 cycles_late) { "Kernel::TimeManagerCallback", [this](u64 thread_handle, [[maybe_unused]] s64 cycles_late) {
SchedulerLock lock(system.Kernel());
Handle proper_handle = static_cast<Handle>(thread_handle); Handle proper_handle = static_cast<Handle>(thread_handle);
if (cancelled_events[proper_handle]) {
return;
}
event_fired[proper_handle] = true;
std::shared_ptr<Thread> thread = std::shared_ptr<Thread> thread =
this->system.Kernel().RetrieveThreadFromGlobalHandleTable(proper_handle); this->system.Kernel().RetrieveThreadFromGlobalHandleTable(proper_handle);
thread->OnWakeUp(); thread->OnWakeUp();
@ -24,14 +30,16 @@ TimeManager::TimeManager(Core::System& system) : system{system} {
} }
void TimeManager::ScheduleTimeEvent(Handle& event_handle, Thread* timetask, s64 nanoseconds) { void TimeManager::ScheduleTimeEvent(Handle& event_handle, Thread* timetask, s64 nanoseconds) {
event_handle = timetask->GetGlobalHandle();
if (nanoseconds > 0) { if (nanoseconds > 0) {
ASSERT(timetask); ASSERT(timetask);
event_handle = timetask->GetGlobalHandle();
const s64 cycles = Core::Timing::nsToCycles(std::chrono::nanoseconds{nanoseconds}); const s64 cycles = Core::Timing::nsToCycles(std::chrono::nanoseconds{nanoseconds});
system.CoreTiming().ScheduleEvent(cycles, time_manager_event_type, event_handle); system.CoreTiming().ScheduleEvent(cycles, time_manager_event_type, event_handle);
} else { } else {
event_handle = InvalidHandle; event_handle = InvalidHandle;
} }
cancelled_events[event_handle] = false;
event_fired[event_handle] = false;
} }
void TimeManager::UnscheduleTimeEvent(Handle event_handle) { void TimeManager::UnscheduleTimeEvent(Handle event_handle) {
@ -39,6 +47,12 @@ void TimeManager::UnscheduleTimeEvent(Handle event_handle) {
return; return;
} }
system.CoreTiming().UnscheduleEvent(time_manager_event_type, event_handle); system.CoreTiming().UnscheduleEvent(time_manager_event_type, event_handle);
cancelled_events[event_handle] = true;
}
void TimeManager::CancelTimeEvent(Thread* time_task) {
Handle event_handle = time_task->GetGlobalHandle();
UnscheduleTimeEvent(event_handle);
} }
} // namespace Kernel } // namespace Kernel

View file

@ -5,6 +5,7 @@
#pragma once #pragma once
#include <memory> #include <memory>
#include <unordered_map>
#include "core/hle/kernel/object.h" #include "core/hle/kernel/object.h"
@ -35,9 +36,13 @@ public:
/// Unschedule an existing time event /// Unschedule an existing time event
void UnscheduleTimeEvent(Handle event_handle); void UnscheduleTimeEvent(Handle event_handle);
void CancelTimeEvent(Thread* time_task);
private: private:
Core::System& system; Core::System& system;
std::shared_ptr<Core::Timing::EventType> time_manager_event_type; std::shared_ptr<Core::Timing::EventType> time_manager_event_type;
std::unordered_map<Handle, bool> cancelled_events;
std::unordered_map<Handle, bool> event_fired;
}; };
} // namespace Kernel } // namespace Kernel