SVC: Correct WaitSynchronization, WaitProcessWideKey, SignalProcessWideKey.
This commit is contained in:
parent
5b6a67f849
commit
d4ebb510a0
9 changed files with 84 additions and 33 deletions
|
@ -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(
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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(),
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
|
|
||||||
|
|
|
@ -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);
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
Loading…
Reference in a new issue