core: hle: Integrate new KConditionVariable and KAddressArbiter implementations.

This commit is contained in:
bunnei 2020-12-30 01:14:02 -08:00
parent 952d1ac487
commit 912dd50146
15 changed files with 515 additions and 1189 deletions

View File

@ -142,8 +142,6 @@ add_library(core STATIC
hardware_interrupt_manager.h hardware_interrupt_manager.h
hle/ipc.h hle/ipc.h
hle/ipc_helpers.h hle/ipc_helpers.h
hle/kernel/address_arbiter.cpp
hle/kernel/address_arbiter.h
hle/kernel/client_port.cpp hle/kernel/client_port.cpp
hle/kernel/client_port.h hle/kernel/client_port.h
hle/kernel/client_session.cpp hle/kernel/client_session.cpp
@ -189,8 +187,6 @@ add_library(core STATIC
hle/kernel/memory/slab_heap.h hle/kernel/memory/slab_heap.h
hle/kernel/memory/system_control.cpp hle/kernel/memory/system_control.cpp
hle/kernel/memory/system_control.h hle/kernel/memory/system_control.h
hle/kernel/mutex.cpp
hle/kernel/mutex.h
hle/kernel/object.cpp hle/kernel/object.cpp
hle/kernel/object.h hle/kernel/object.h
hle/kernel/physical_core.cpp hle/kernel/physical_core.cpp

View File

@ -1,317 +0,0 @@
// Copyright 2018 yuzu emulator team
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include <algorithm>
#include <vector>
#include "common/assert.h"
#include "common/common_types.h"
#include "core/arm/exclusive_monitor.h"
#include "core/core.h"
#include "core/hle/kernel/address_arbiter.h"
#include "core/hle/kernel/errors.h"
#include "core/hle/kernel/handle_table.h"
#include "core/hle/kernel/k_scheduler.h"
#include "core/hle/kernel/k_scoped_scheduler_lock_and_sleep.h"
#include "core/hle/kernel/kernel.h"
#include "core/hle/kernel/thread.h"
#include "core/hle/kernel/time_manager.h"
#include "core/hle/result.h"
#include "core/memory.h"
namespace Kernel {
// Wake up num_to_wake (or all) threads in a vector.
void AddressArbiter::WakeThreads(const std::vector<std::shared_ptr<Thread>>& waiting_threads,
s32 num_to_wake) {
// Only process up to 'target' threads, unless 'target' is <= 0, in which case process
// them all.
std::size_t last = waiting_threads.size();
if (num_to_wake > 0) {
last = std::min(last, static_cast<std::size_t>(num_to_wake));
}
// Signal the waiting threads.
for (std::size_t i = 0; i < last; i++) {
waiting_threads[i]->SetSynchronizationResults(nullptr, RESULT_SUCCESS);
RemoveThread(waiting_threads[i]);
waiting_threads[i]->WaitForArbitration(false);
waiting_threads[i]->Wakeup();
}
}
AddressArbiter::AddressArbiter(Core::System& system) : system{system} {}
AddressArbiter::~AddressArbiter() = default;
ResultCode AddressArbiter::SignalToAddress(VAddr address, SignalType type, s32 value,
s32 num_to_wake) {
switch (type) {
case SignalType::Signal:
return SignalToAddressOnly(address, num_to_wake);
case SignalType::IncrementAndSignalIfEqual:
return IncrementAndSignalToAddressIfEqual(address, value, num_to_wake);
case SignalType::ModifyByWaitingCountAndSignalIfEqual:
return ModifyByWaitingCountAndSignalToAddressIfEqual(address, value, num_to_wake);
default:
return ERR_INVALID_ENUM_VALUE;
}
}
ResultCode AddressArbiter::SignalToAddressOnly(VAddr address, s32 num_to_wake) {
KScopedSchedulerLock lock(system.Kernel());
const std::vector<std::shared_ptr<Thread>> waiting_threads =
GetThreadsWaitingOnAddress(address);
WakeThreads(waiting_threads, num_to_wake);
return RESULT_SUCCESS;
}
ResultCode AddressArbiter::IncrementAndSignalToAddressIfEqual(VAddr address, s32 value,
s32 num_to_wake) {
KScopedSchedulerLock lock(system.Kernel());
auto& memory = system.Memory();
// Ensure that we can write to the address.
if (!memory.IsValidVirtualAddress(address)) {
return ERR_INVALID_ADDRESS_STATE;
}
const std::size_t current_core = system.CurrentCoreIndex();
auto& monitor = system.Monitor();
u32 current_value;
do {
current_value = monitor.ExclusiveRead32(current_core, address);
if (current_value != static_cast<u32>(value)) {
return ERR_INVALID_STATE;
}
current_value++;
} while (!monitor.ExclusiveWrite32(current_core, address, current_value));
return SignalToAddressOnly(address, num_to_wake);
}
ResultCode AddressArbiter::ModifyByWaitingCountAndSignalToAddressIfEqual(VAddr address, s32 value,
s32 num_to_wake) {
KScopedSchedulerLock lock(system.Kernel());
auto& memory = system.Memory();
// Ensure that we can write to the address.
if (!memory.IsValidVirtualAddress(address)) {
return ERR_INVALID_ADDRESS_STATE;
}
// Get threads waiting on the address.
const std::vector<std::shared_ptr<Thread>> waiting_threads =
GetThreadsWaitingOnAddress(address);
const std::size_t current_core = system.CurrentCoreIndex();
auto& monitor = system.Monitor();
s32 updated_value;
do {
updated_value = monitor.ExclusiveRead32(current_core, address);
if (updated_value != value) {
return ERR_INVALID_STATE;
}
// Determine the modified value depending on the waiting count.
if (num_to_wake <= 0) {
if (waiting_threads.empty()) {
updated_value = value + 1;
} else {
updated_value = value - 1;
}
} else {
if (waiting_threads.empty()) {
updated_value = value + 1;
} else if (waiting_threads.size() <= static_cast<u32>(num_to_wake)) {
updated_value = value - 1;
} else {
updated_value = value;
}
}
} while (!monitor.ExclusiveWrite32(current_core, address, updated_value));
WakeThreads(waiting_threads, num_to_wake);
return RESULT_SUCCESS;
}
ResultCode AddressArbiter::WaitForAddress(VAddr address, ArbitrationType type, s32 value,
s64 timeout_ns) {
switch (type) {
case ArbitrationType::WaitIfLessThan:
return WaitForAddressIfLessThan(address, value, timeout_ns, false);
case ArbitrationType::DecrementAndWaitIfLessThan:
return WaitForAddressIfLessThan(address, value, timeout_ns, true);
case ArbitrationType::WaitIfEqual:
return WaitForAddressIfEqual(address, value, timeout_ns);
default:
return ERR_INVALID_ENUM_VALUE;
}
}
ResultCode AddressArbiter::WaitForAddressIfLessThan(VAddr address, s32 value, s64 timeout,
bool should_decrement) {
auto& memory = system.Memory();
auto& kernel = system.Kernel();
Thread* current_thread = kernel.CurrentScheduler()->GetCurrentThread();
Handle event_handle = InvalidHandle;
{
KScopedSchedulerLockAndSleep lock(kernel, event_handle, current_thread, timeout);
if (current_thread->IsTerminationRequested()) {
lock.CancelSleep();
return ERR_THREAD_TERMINATING;
}
// Ensure that we can read the address.
if (!memory.IsValidVirtualAddress(address)) {
lock.CancelSleep();
return ERR_INVALID_ADDRESS_STATE;
}
s32 current_value = static_cast<s32>(memory.Read32(address));
if (current_value >= value) {
lock.CancelSleep();
return ERR_INVALID_STATE;
}
current_thread->SetSynchronizationResults(nullptr, RESULT_TIMEOUT);
s32 decrement_value;
const std::size_t current_core = system.CurrentCoreIndex();
auto& monitor = system.Monitor();
do {
current_value = static_cast<s32>(monitor.ExclusiveRead32(current_core, address));
if (should_decrement) {
decrement_value = current_value - 1;
} else {
decrement_value = current_value;
}
} while (
!monitor.ExclusiveWrite32(current_core, address, static_cast<u32>(decrement_value)));
// Short-circuit without rescheduling, if timeout is zero.
if (timeout == 0) {
lock.CancelSleep();
return RESULT_TIMEOUT;
}
current_thread->SetArbiterWaitAddress(address);
InsertThread(SharedFrom(current_thread));
current_thread->SetState(ThreadState::Waiting);
current_thread->WaitForArbitration(true);
}
if (event_handle != InvalidHandle) {
auto& time_manager = kernel.TimeManager();
time_manager.UnscheduleTimeEvent(event_handle);
}
{
KScopedSchedulerLock lock(kernel);
if (current_thread->IsWaitingForArbitration()) {
RemoveThread(SharedFrom(current_thread));
current_thread->WaitForArbitration(false);
}
}
return current_thread->GetSignalingResult();
}
ResultCode AddressArbiter::WaitForAddressIfEqual(VAddr address, s32 value, s64 timeout) {
auto& memory = system.Memory();
auto& kernel = system.Kernel();
Thread* current_thread = kernel.CurrentScheduler()->GetCurrentThread();
Handle event_handle = InvalidHandle;
{
KScopedSchedulerLockAndSleep lock(kernel, event_handle, current_thread, timeout);
if (current_thread->IsTerminationRequested()) {
lock.CancelSleep();
return ERR_THREAD_TERMINATING;
}
// Ensure that we can read the address.
if (!memory.IsValidVirtualAddress(address)) {
lock.CancelSleep();
return ERR_INVALID_ADDRESS_STATE;
}
s32 current_value = static_cast<s32>(memory.Read32(address));
if (current_value != value) {
lock.CancelSleep();
return ERR_INVALID_STATE;
}
// Short-circuit without rescheduling, if timeout is zero.
if (timeout == 0) {
lock.CancelSleep();
return RESULT_TIMEOUT;
}
current_thread->SetSynchronizationResults(nullptr, RESULT_TIMEOUT);
current_thread->SetArbiterWaitAddress(address);
InsertThread(SharedFrom(current_thread));
current_thread->SetState(ThreadState::Waiting);
current_thread->WaitForArbitration(true);
}
if (event_handle != InvalidHandle) {
auto& time_manager = kernel.TimeManager();
time_manager.UnscheduleTimeEvent(event_handle);
}
{
KScopedSchedulerLock lock(kernel);
if (current_thread->IsWaitingForArbitration()) {
RemoveThread(SharedFrom(current_thread));
current_thread->WaitForArbitration(false);
}
}
return current_thread->GetSignalingResult();
}
void AddressArbiter::InsertThread(std::shared_ptr<Thread> thread) {
const VAddr arb_addr = thread->GetArbiterWaitAddress();
std::list<std::shared_ptr<Thread>>& thread_list = arb_threads[arb_addr];
const auto iter =
std::find_if(thread_list.cbegin(), thread_list.cend(), [&thread](const auto& entry) {
return entry->GetPriority() >= thread->GetPriority();
});
if (iter == thread_list.cend()) {
thread_list.push_back(std::move(thread));
} else {
thread_list.insert(iter, std::move(thread));
}
}
void AddressArbiter::RemoveThread(std::shared_ptr<Thread> thread) {
const VAddr arb_addr = thread->GetArbiterWaitAddress();
std::list<std::shared_ptr<Thread>>& thread_list = arb_threads[arb_addr];
const auto iter = std::find_if(thread_list.cbegin(), thread_list.cend(),
[&thread](const auto& entry) { return thread == entry; });
if (iter != thread_list.cend()) {
thread_list.erase(iter);
}
}
std::vector<std::shared_ptr<Thread>> AddressArbiter::GetThreadsWaitingOnAddress(
VAddr address) const {
const auto iter = arb_threads.find(address);
if (iter == arb_threads.cend()) {
return {};
}
const std::list<std::shared_ptr<Thread>>& thread_list = iter->second;
return {thread_list.cbegin(), thread_list.cend()};
}
} // namespace Kernel

View File

@ -1,91 +0,0 @@
// Copyright 2018 yuzu emulator team
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include <list>
#include <memory>
#include <unordered_map>
#include <vector>
#include "common/common_types.h"
union ResultCode;
namespace Core {
class System;
}
namespace Kernel {
class Thread;
class AddressArbiter {
public:
enum class ArbitrationType {
WaitIfLessThan = 0,
DecrementAndWaitIfLessThan = 1,
WaitIfEqual = 2,
};
enum class SignalType {
Signal = 0,
IncrementAndSignalIfEqual = 1,
ModifyByWaitingCountAndSignalIfEqual = 2,
};
explicit AddressArbiter(Core::System& system);
~AddressArbiter();
AddressArbiter(const AddressArbiter&) = delete;
AddressArbiter& operator=(const AddressArbiter&) = delete;
AddressArbiter(AddressArbiter&&) = default;
AddressArbiter& operator=(AddressArbiter&&) = delete;
/// Signals an address being waited on with a particular signaling type.
ResultCode SignalToAddress(VAddr address, SignalType type, s32 value, s32 num_to_wake);
/// Waits on an address with a particular arbitration type.
ResultCode WaitForAddress(VAddr address, ArbitrationType type, s32 value, s64 timeout_ns);
private:
/// Signals an address being waited on.
ResultCode SignalToAddressOnly(VAddr address, s32 num_to_wake);
/// Signals an address being waited on and increments its value if equal to the value argument.
ResultCode IncrementAndSignalToAddressIfEqual(VAddr address, s32 value, s32 num_to_wake);
/// Signals an address being waited on and modifies its value based on waiting thread count if
/// equal to the value argument.
ResultCode ModifyByWaitingCountAndSignalToAddressIfEqual(VAddr address, s32 value,
s32 num_to_wake);
/// Waits on an address if the value passed is less than the argument value,
/// optionally decrementing.
ResultCode WaitForAddressIfLessThan(VAddr address, s32 value, s64 timeout,
bool should_decrement);
/// Waits on an address if the value passed is equal to the argument value.
ResultCode WaitForAddressIfEqual(VAddr address, s32 value, s64 timeout);
/// Wake up num_to_wake (or all) threads in a vector.
void WakeThreads(const std::vector<std::shared_ptr<Thread>>& waiting_threads, s32 num_to_wake);
/// Insert a thread into the address arbiter container
void InsertThread(std::shared_ptr<Thread> thread);
/// Removes a thread from the address arbiter container
void RemoveThread(std::shared_ptr<Thread> thread);
// Gets the threads waiting on an address.
std::vector<std::shared_ptr<Thread>> GetThreadsWaitingOnAddress(VAddr address) const;
/// List of threads waiting for a address arbiter
std::unordered_map<VAddr, std::list<std::shared_ptr<Thread>>> arb_threads;
Core::System& system;
};
} // namespace Kernel

View File

@ -72,7 +72,7 @@ ResultCode KSynchronizationObject::Wait(KernelCore& kernel, s32* out_index,
} }
// For debugging only // For debugging only
thread->SetWaitObjectsForDebugging(objects, num_objects); thread->SetWaitObjectsForDebugging({objects, static_cast<std::size_t>(num_objects)});
// Mark the thread as waiting. // Mark the thread as waiting.
thread->SetCancellable(); thread->SetCancellable();
@ -86,7 +86,7 @@ ResultCode KSynchronizationObject::Wait(KernelCore& kernel, s32* out_index,
thread->ClearCancellable(); thread->ClearCancellable();
// For debugging only // For debugging only
thread->SetWaitObjectsForDebugging(nullptr, 0); thread->SetWaitObjectsForDebugging({});
// Cancel the timer as needed. // Cancel the timer as needed.
if (timer != InvalidHandle) { if (timer != InvalidHandle) {

View File

@ -1,170 +0,0 @@
// Copyright 2014 Citra Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include <memory>
#include <utility>
#include <vector>
#include "common/assert.h"
#include "common/logging/log.h"
#include "core/core.h"
#include "core/hle/kernel/errors.h"
#include "core/hle/kernel/handle_table.h"
#include "core/hle/kernel/k_scheduler.h"
#include "core/hle/kernel/kernel.h"
#include "core/hle/kernel/mutex.h"
#include "core/hle/kernel/object.h"
#include "core/hle/kernel/process.h"
#include "core/hle/kernel/thread.h"
#include "core/hle/result.h"
#include "core/memory.h"
namespace Kernel {
/// Returns the number of threads that are waiting for a mutex, and the highest priority one among
/// those.
static std::pair<std::shared_ptr<Thread>, u32> GetHighestPriorityMutexWaitingThread(
const std::shared_ptr<Thread>& current_thread, VAddr mutex_addr) {
std::shared_ptr<Thread> highest_priority_thread;
u32 num_waiters = 0;
for (const auto& thread : current_thread->GetMutexWaitingThreads()) {
if (thread->GetMutexWaitAddress() != mutex_addr)
continue;
++num_waiters;
if (highest_priority_thread == nullptr ||
thread->GetPriority() < highest_priority_thread->GetPriority()) {
highest_priority_thread = thread;
}
}
return {highest_priority_thread, num_waiters};
}
/// Update the mutex owner field of all threads waiting on the mutex to point to the new owner.
static void TransferMutexOwnership(VAddr mutex_addr, std::shared_ptr<Thread> current_thread,
std::shared_ptr<Thread> new_owner) {
current_thread->RemoveMutexWaiter(new_owner);
const auto threads = current_thread->GetMutexWaitingThreads();
for (const auto& thread : threads) {
if (thread->GetMutexWaitAddress() != mutex_addr)
continue;
ASSERT(thread->GetLockOwner() == current_thread.get());
current_thread->RemoveMutexWaiter(thread);
if (new_owner != thread)
new_owner->AddMutexWaiter(thread);
}
}
Mutex::Mutex(Core::System& system) : system{system} {}
Mutex::~Mutex() = default;
ResultCode Mutex::TryAcquire(VAddr address, Handle holding_thread_handle,
Handle requesting_thread_handle) {
// The mutex address must be 4-byte aligned
if ((address % sizeof(u32)) != 0) {
LOG_ERROR(Kernel, "Address is not 4-byte aligned! address={:016X}", address);
return ERR_INVALID_ADDRESS;
}
auto& kernel = system.Kernel();
std::shared_ptr<Thread> current_thread =
SharedFrom(kernel.CurrentScheduler()->GetCurrentThread());
{
KScopedSchedulerLock lock(kernel);
// The mutex address must be 4-byte aligned
if ((address % sizeof(u32)) != 0) {
return ERR_INVALID_ADDRESS;
}
const auto& handle_table = kernel.CurrentProcess()->GetHandleTable();
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);
// TODO(Subv): It is currently unknown if it is possible to lock a mutex in behalf of
// another thread.
ASSERT(requesting_thread == current_thread);
current_thread->SetSynchronizationResults(nullptr, 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->SetState(ThreadState::Waiting);
// Update the lock holder thread's priority to prevent priority inversion.
holding_thread->AddMutexWaiter(current_thread);
}
{
KScopedSchedulerLock lock(kernel);
auto* owner = current_thread->GetLockOwner();
if (owner != nullptr) {
owner->RemoveMutexWaiter(current_thread);
}
}
return current_thread->GetSignalingResult();
}
std::pair<ResultCode, std::shared_ptr<Thread>> Mutex::Unlock(std::shared_ptr<Thread> owner,
VAddr address) {
// The mutex address must be 4-byte aligned
if ((address % sizeof(u32)) != 0) {
LOG_ERROR(Kernel, "Address is not 4-byte aligned! address={:016X}", address);
return {ERR_INVALID_ADDRESS, nullptr};
}
auto [new_owner, num_waiters] = GetHighestPriorityMutexWaitingThread(owner, address);
if (new_owner == nullptr) {
system.Memory().Write32(address, 0);
return {RESULT_SUCCESS, nullptr};
}
// Transfer the ownership of the mutex from the previous owner to the new one.
TransferMutexOwnership(address, owner, new_owner);
u32 mutex_value = new_owner->GetWaitHandle();
if (num_waiters >= 2) {
// Notify the guest that there are still some threads waiting for the mutex
mutex_value |= Mutex::MutexHasWaitersFlag;
}
new_owner->SetSynchronizationResults(nullptr, RESULT_SUCCESS);
new_owner->SetLockOwner(nullptr);
new_owner->Wakeup();
system.Memory().Write32(address, mutex_value);
return {RESULT_SUCCESS, new_owner};
}
ResultCode Mutex::Release(VAddr address) {
auto& kernel = system.Kernel();
KScopedSchedulerLock lock(kernel);
std::shared_ptr<Thread> current_thread =
SharedFrom(kernel.CurrentScheduler()->GetCurrentThread());
auto [result, new_owner] = Unlock(current_thread, address);
if (result != RESULT_SUCCESS && new_owner != nullptr) {
new_owner->SetSynchronizationResults(nullptr, result);
}
return result;
}
} // namespace Kernel

View File

@ -1,42 +0,0 @@
// Copyright 2014 Citra Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include "common/common_types.h"
union ResultCode;
namespace Core {
class System;
}
namespace Kernel {
class Mutex final {
public:
explicit Mutex(Core::System& system);
~Mutex();
/// Flag that indicates that a mutex still has threads waiting for it.
static constexpr u32 MutexHasWaitersFlag = 0x40000000;
/// Mask of the bits in a mutex address value that contain the mutex owner.
static constexpr u32 MutexOwnerMask = 0xBFFFFFFF;
/// Attempts to acquire a mutex at the specified address.
ResultCode TryAcquire(VAddr address, Handle holding_thread_handle,
Handle requesting_thread_handle);
/// Unlocks a mutex for owner at address
std::pair<ResultCode, std::shared_ptr<Thread>> Unlock(std::shared_ptr<Thread> owner,
VAddr address);
/// Releases the mutex at the specified address.
ResultCode Release(VAddr address);
private:
Core::System& system;
};
} // namespace Kernel

View File

@ -162,48 +162,6 @@ u64 Process::GetTotalPhysicalMemoryUsedWithoutSystemResource() const {
return GetTotalPhysicalMemoryUsed() - GetSystemResourceUsage(); return GetTotalPhysicalMemoryUsed() - GetSystemResourceUsage();
} }
void Process::InsertConditionVariableThread(std::shared_ptr<Thread> thread) {
VAddr cond_var_addr = thread->GetCondVarWaitAddress();
std::list<std::shared_ptr<Thread>>& thread_list = cond_var_threads[cond_var_addr];
auto it = thread_list.begin();
while (it != thread_list.end()) {
const std::shared_ptr<Thread> current_thread = *it;
if (current_thread->GetPriority() > thread->GetPriority()) {
thread_list.insert(it, thread);
return;
}
++it;
}
thread_list.push_back(thread);
}
void Process::RemoveConditionVariableThread(std::shared_ptr<Thread> thread) {
VAddr cond_var_addr = thread->GetCondVarWaitAddress();
std::list<std::shared_ptr<Thread>>& thread_list = cond_var_threads[cond_var_addr];
auto it = thread_list.begin();
while (it != thread_list.end()) {
const std::shared_ptr<Thread> current_thread = *it;
if (current_thread.get() == thread.get()) {
thread_list.erase(it);
return;
}
++it;
}
}
std::vector<std::shared_ptr<Thread>> Process::GetConditionVariableThreads(
const VAddr cond_var_addr) {
std::vector<std::shared_ptr<Thread>> result{};
std::list<std::shared_ptr<Thread>>& thread_list = cond_var_threads[cond_var_addr];
auto it = thread_list.begin();
while (it != thread_list.end()) {
std::shared_ptr<Thread> current_thread = *it;
result.push_back(current_thread);
++it;
}
return result;
}
void Process::RegisterThread(const Thread* thread) { void Process::RegisterThread(const Thread* thread) {
thread_list.push_back(thread); thread_list.push_back(thread);
} }
@ -412,9 +370,9 @@ bool Process::IsSignaled() const {
} }
Process::Process(Core::System& system) Process::Process(Core::System& system)
: KSynchronizationObject{system.Kernel()}, page_table{std::make_unique<Memory::PageTable>( : KSynchronizationObject{system.Kernel()},
system)}, page_table{std::make_unique<Memory::PageTable>(system)}, handle_table{system.Kernel()},
handle_table{system.Kernel()}, address_arbiter{system}, mutex{system}, system{system} {} address_arbiter{system}, condition_var{system}, system{system} {}
Process::~Process() = default; Process::~Process() = default;

View File

@ -11,10 +11,10 @@
#include <unordered_map> #include <unordered_map>
#include <vector> #include <vector>
#include "common/common_types.h" #include "common/common_types.h"
#include "core/hle/kernel/address_arbiter.h"
#include "core/hle/kernel/handle_table.h" #include "core/hle/kernel/handle_table.h"
#include "core/hle/kernel/k_address_arbiter.h"
#include "core/hle/kernel/k_condition_variable.h"
#include "core/hle/kernel/k_synchronization_object.h" #include "core/hle/kernel/k_synchronization_object.h"
#include "core/hle/kernel/mutex.h"
#include "core/hle/kernel/process_capability.h" #include "core/hle/kernel/process_capability.h"
#include "core/hle/result.h" #include "core/hle/result.h"
@ -123,24 +123,30 @@ public:
return handle_table; return handle_table;
} }
/// Gets a reference to the process' address arbiter. ResultCode SignalToAddress(VAddr address) {
AddressArbiter& GetAddressArbiter() { return condition_var.SignalToAddress(address);
return address_arbiter;
} }
/// Gets a const reference to the process' address arbiter. ResultCode WaitForAddress(Handle handle, VAddr address, u32 tag) {
const AddressArbiter& GetAddressArbiter() const { return condition_var.WaitForAddress(handle, address, tag);
return address_arbiter;
} }
/// Gets a reference to the process' mutex lock. void SignalConditionVariable(u64 cv_key, int32_t count) {
Mutex& GetMutex() { return condition_var.Signal(cv_key, count);
return mutex;
} }
/// Gets a const reference to the process' mutex lock ResultCode WaitConditionVariable(VAddr address, u64 cv_key, u32 tag, s64 ns) {
const Mutex& GetMutex() const { return condition_var.Wait(address, cv_key, tag, ns);
return mutex; }
ResultCode SignalAddressArbiter(VAddr address, Svc::SignalType signal_type, s32 value,
s32 count) {
return address_arbiter.SignalToAddress(address, signal_type, value, count);
}
ResultCode WaitAddressArbiter(VAddr address, Svc::ArbitrationType arb_type, s32 value,
s64 timeout) {
return address_arbiter.WaitForAddress(address, arb_type, value, timeout);
} }
/// Gets the address to the process' dedicated TLS region. /// Gets the address to the process' dedicated TLS region.
@ -250,15 +256,6 @@ public:
return thread_list; return thread_list;
} }
/// Insert a thread into the condition variable wait container
void InsertConditionVariableThread(std::shared_ptr<Thread> thread);
/// Remove a thread from the condition variable wait container
void RemoveConditionVariableThread(std::shared_ptr<Thread> thread);
/// Obtain all condition variable threads waiting for some address
std::vector<std::shared_ptr<Thread>> GetConditionVariableThreads(VAddr cond_var_addr);
/// Registers a thread as being created under this process, /// Registers a thread as being created under this process,
/// adding it to this process' thread list. /// adding it to this process' thread list.
void RegisterThread(const Thread* thread); void RegisterThread(const Thread* thread);
@ -369,12 +366,12 @@ private:
HandleTable handle_table; HandleTable handle_table;
/// Per-process address arbiter. /// Per-process address arbiter.
AddressArbiter address_arbiter; KAddressArbiter address_arbiter;
/// The per-process mutex lock instance used for handling various /// The per-process mutex lock instance used for handling various
/// forms of services, such as lock arbitration, and condition /// forms of services, such as lock arbitration, and condition
/// variable related facilities. /// variable related facilities.
Mutex mutex; KConditionVariable condition_var;
/// Address indicating the location of the process' dedicated TLS region. /// Address indicating the location of the process' dedicated TLS region.
VAddr tls_region_address = 0; VAddr tls_region_address = 0;
@ -385,9 +382,6 @@ private:
/// List of threads that are running with this process as their owner. /// List of threads that are running with this process as their owner.
std::list<const Thread*> thread_list; std::list<const Thread*> thread_list;
/// List of threads waiting for a condition variable
std::unordered_map<VAddr, std::list<std::shared_ptr<Thread>>> cond_var_threads;
/// Address of the top of the main thread's stack /// Address of the top of the main thread's stack
VAddr main_thread_stack_top{}; VAddr main_thread_stack_top{};

View File

@ -10,6 +10,7 @@
#include "common/alignment.h" #include "common/alignment.h"
#include "common/assert.h" #include "common/assert.h"
#include "common/common_funcs.h"
#include "common/fiber.h" #include "common/fiber.h"
#include "common/logging/log.h" #include "common/logging/log.h"
#include "common/microprofile.h" #include "common/microprofile.h"
@ -19,24 +20,26 @@
#include "core/core_timing.h" #include "core/core_timing.h"
#include "core/core_timing_util.h" #include "core/core_timing_util.h"
#include "core/cpu_manager.h" #include "core/cpu_manager.h"
#include "core/hle/kernel/address_arbiter.h"
#include "core/hle/kernel/client_port.h" #include "core/hle/kernel/client_port.h"
#include "core/hle/kernel/client_session.h" #include "core/hle/kernel/client_session.h"
#include "core/hle/kernel/errors.h" #include "core/hle/kernel/errors.h"
#include "core/hle/kernel/handle_table.h" #include "core/hle/kernel/handle_table.h"
#include "core/hle/kernel/k_address_arbiter.h"
#include "core/hle/kernel/k_condition_variable.h"
#include "core/hle/kernel/k_scheduler.h" #include "core/hle/kernel/k_scheduler.h"
#include "core/hle/kernel/k_scoped_scheduler_lock_and_sleep.h" #include "core/hle/kernel/k_scoped_scheduler_lock_and_sleep.h"
#include "core/hle/kernel/k_synchronization_object.h" #include "core/hle/kernel/k_synchronization_object.h"
#include "core/hle/kernel/kernel.h" #include "core/hle/kernel/kernel.h"
#include "core/hle/kernel/memory/memory_block.h" #include "core/hle/kernel/memory/memory_block.h"
#include "core/hle/kernel/memory/memory_layout.h"
#include "core/hle/kernel/memory/page_table.h" #include "core/hle/kernel/memory/page_table.h"
#include "core/hle/kernel/mutex.h"
#include "core/hle/kernel/physical_core.h" #include "core/hle/kernel/physical_core.h"
#include "core/hle/kernel/process.h" #include "core/hle/kernel/process.h"
#include "core/hle/kernel/readable_event.h" #include "core/hle/kernel/readable_event.h"
#include "core/hle/kernel/resource_limit.h" #include "core/hle/kernel/resource_limit.h"
#include "core/hle/kernel/shared_memory.h" #include "core/hle/kernel/shared_memory.h"
#include "core/hle/kernel/svc.h" #include "core/hle/kernel/svc.h"
#include "core/hle/kernel/svc_results.h"
#include "core/hle/kernel/svc_types.h" #include "core/hle/kernel/svc_types.h"
#include "core/hle/kernel/svc_wrap.h" #include "core/hle/kernel/svc_wrap.h"
#include "core/hle/kernel/thread.h" #include "core/hle/kernel/thread.h"
@ -347,12 +350,6 @@ static ResultCode SendSyncRequest(Core::System& system, Handle handle) {
session->SendSyncRequest(SharedFrom(thread), system.Memory(), system.CoreTiming()); session->SendSyncRequest(SharedFrom(thread), system.Memory(), system.CoreTiming());
} }
Handle event_handle = thread->GetHLETimeEvent();
if (event_handle != InvalidHandle) {
auto& time_manager = kernel.TimeManager();
time_manager.UnscheduleTimeEvent(event_handle);
}
return thread->GetSignalingResult(); return thread->GetSignalingResult();
} }
@ -491,56 +488,37 @@ static ResultCode CancelSynchronization32(Core::System& system, Handle thread_ha
return CancelSynchronization(system, thread_handle); return CancelSynchronization(system, thread_handle);
} }
/// Attempts to locks a mutex, creating it if it does not already exist /// Attempts to locks a mutex
static ResultCode ArbitrateLock(Core::System& system, Handle holding_thread_handle, static ResultCode ArbitrateLock(Core::System& system, Handle thread_handle, VAddr address,
VAddr mutex_addr, Handle requesting_thread_handle) { u32 tag) {
LOG_TRACE(Kernel_SVC, LOG_TRACE(Kernel_SVC, "called thread_handle=0x{:08X}, address=0x{:X}, tag=0x{:08X}",
"called holding_thread_handle=0x{:08X}, mutex_addr=0x{:X}, " thread_handle, address, tag);
"requesting_current_thread_handle=0x{:08X}",
holding_thread_handle, mutex_addr, requesting_thread_handle);
if (Core::Memory::IsKernelVirtualAddress(mutex_addr)) { // Validate the input address.
LOG_ERROR(Kernel_SVC, "Mutex Address is a kernel virtual address, mutex_addr={:016X}", R_UNLESS(!Memory::IsKernelAddress(address), Svc::ResultInvalidCurrentMemory);
mutex_addr); R_UNLESS(Common::IsAligned(address, sizeof(u32)), Svc::ResultInvalidAddress);
return ERR_INVALID_ADDRESS_STATE;
}
if (!Common::IsWordAligned(mutex_addr)) { return system.Kernel().CurrentProcess()->WaitForAddress(thread_handle, address, tag);
LOG_ERROR(Kernel_SVC, "Mutex Address is not word aligned, mutex_addr={:016X}", mutex_addr);
return ERR_INVALID_ADDRESS;
}
auto* const current_process = system.Kernel().CurrentProcess();
return current_process->GetMutex().TryAcquire(mutex_addr, holding_thread_handle,
requesting_thread_handle);
} }
static ResultCode ArbitrateLock32(Core::System& system, Handle holding_thread_handle, static ResultCode ArbitrateLock32(Core::System& system, Handle thread_handle, u32 address,
u32 mutex_addr, Handle requesting_thread_handle) { u32 tag) {
return ArbitrateLock(system, holding_thread_handle, mutex_addr, requesting_thread_handle); return ArbitrateLock(system, thread_handle, address, tag);
} }
/// Unlock a mutex /// Unlock a mutex
static ResultCode ArbitrateUnlock(Core::System& system, VAddr mutex_addr) { static ResultCode ArbitrateUnlock(Core::System& system, VAddr address) {
LOG_TRACE(Kernel_SVC, "called mutex_addr=0x{:X}", mutex_addr); LOG_TRACE(Kernel_SVC, "called address=0x{:X}", address);
if (Core::Memory::IsKernelVirtualAddress(mutex_addr)) { // Validate the input address.
LOG_ERROR(Kernel_SVC, "Mutex Address is a kernel virtual address, mutex_addr={:016X}", R_UNLESS(!Memory::IsKernelAddress(address), Svc::ResultInvalidCurrentMemory);
mutex_addr); R_UNLESS(Common::IsAligned(address, sizeof(u32)), Svc::ResultInvalidAddress);
return ERR_INVALID_ADDRESS_STATE;
}
if (!Common::IsWordAligned(mutex_addr)) { return system.Kernel().CurrentProcess()->SignalToAddress(address);
LOG_ERROR(Kernel_SVC, "Mutex Address is not word aligned, mutex_addr={:016X}", mutex_addr);
return ERR_INVALID_ADDRESS;
}
auto* const current_process = system.Kernel().CurrentProcess();
return current_process->GetMutex().Release(mutex_addr);
} }
static ResultCode ArbitrateUnlock32(Core::System& system, u32 mutex_addr) { static ResultCode ArbitrateUnlock32(Core::System& system, u32 address) {
return ArbitrateUnlock(system, mutex_addr); return ArbitrateUnlock(system, address);
} }
enum class BreakType : u32 { enum class BreakType : u32 {
@ -1167,7 +1145,7 @@ static ResultCode SetThreadPriority(Core::System& system, Handle handle, u32 pri
return ERR_INVALID_HANDLE; return ERR_INVALID_HANDLE;
} }
thread->SetPriority(priority); thread->SetBasePriority(priority);
return RESULT_SUCCESS; return RESULT_SUCCESS;
} }
@ -1607,223 +1585,135 @@ static void SleepThread32(Core::System& system, u32 nanoseconds_low, u32 nanosec
} }
/// Wait process wide key atomic /// Wait process wide key atomic
static ResultCode WaitProcessWideKeyAtomic(Core::System& system, VAddr mutex_addr, static ResultCode WaitProcessWideKeyAtomic(Core::System& system, VAddr address, VAddr cv_key,
VAddr condition_variable_addr, Handle thread_handle, u32 tag, s64 timeout_ns) {
s64 nano_seconds) { LOG_TRACE(Kernel_SVC, "called address={:X}, cv_key={:X}, tag=0x{:08X}, timeout_ns={}", address,
LOG_TRACE( cv_key, tag, timeout_ns);
Kernel_SVC,
"called mutex_addr={:X}, condition_variable_addr={:X}, thread_handle=0x{:08X}, timeout={}",
mutex_addr, condition_variable_addr, thread_handle, nano_seconds);
if (Core::Memory::IsKernelVirtualAddress(mutex_addr)) { // Validate input.
LOG_ERROR( R_UNLESS(!Memory::IsKernelAddress(address), Svc::ResultInvalidCurrentMemory);
Kernel_SVC, R_UNLESS(Common::IsAligned(address, sizeof(int32_t)), Svc::ResultInvalidAddress);
"Given mutex address must not be within the kernel address space. address=0x{:016X}",
mutex_addr); // Convert timeout from nanoseconds to ticks.
return ERR_INVALID_ADDRESS_STATE; s64 timeout{};
if (timeout_ns > 0) {
const s64 offset_tick(timeout_ns);
if (offset_tick > 0) {
timeout = offset_tick + 2;
if (timeout <= 0) {
timeout = std::numeric_limits<s64>::max();
}
} else {
timeout = std::numeric_limits<s64>::max();
}
} else {
timeout = timeout_ns;
} }
if (!Common::IsWordAligned(mutex_addr)) { // Wait on the condition variable.
LOG_ERROR(Kernel_SVC, "Given mutex address must be word-aligned. address=0x{:016X}", return system.Kernel().CurrentProcess()->WaitConditionVariable(
mutex_addr); address, Common::AlignDown(cv_key, sizeof(u32)), tag, timeout);
return ERR_INVALID_ADDRESS;
}
ASSERT(condition_variable_addr == Common::AlignDown(condition_variable_addr, 4));
auto& kernel = system.Kernel();
Handle event_handle;
Thread* current_thread = kernel.CurrentScheduler()->GetCurrentThread();
auto* const current_process = kernel.CurrentProcess();
{
KScopedSchedulerLockAndSleep lock(kernel, event_handle, current_thread, nano_seconds);
const auto& handle_table = current_process->GetHandleTable();
std::shared_ptr<Thread> thread = handle_table.Get<Thread>(thread_handle);
ASSERT(thread);
current_thread->SetSynchronizationResults(nullptr, RESULT_TIMEOUT);
if (thread->IsTerminationRequested()) {
lock.CancelSleep();
return ERR_THREAD_TERMINATING;
}
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->SetState(ThreadState::Waiting);
current_thread->SetWaitingCondVar(true);
current_process->InsertConditionVariableThread(SharedFrom(current_thread));
}
if (event_handle != InvalidHandle) {
auto& time_manager = kernel.TimeManager();
time_manager.UnscheduleTimeEvent(event_handle);
}
{
KScopedSchedulerLock lock(kernel);
auto* owner = current_thread->GetLockOwner();
if (owner != nullptr) {
owner->RemoveMutexWaiter(SharedFrom(current_thread));
}
current_process->RemoveConditionVariableThread(SharedFrom(current_thread));
}
// Note: Deliberately don't attempt to inherit the lock owner's priority.
return current_thread->GetSignalingResult();
} }
static ResultCode WaitProcessWideKeyAtomic32(Core::System& system, u32 mutex_addr, static ResultCode WaitProcessWideKeyAtomic32(Core::System& system, u32 address, u32 cv_key, u32 tag,
u32 condition_variable_addr, Handle thread_handle, u32 timeout_ns_low, u32 timeout_ns_high) {
u32 nanoseconds_low, u32 nanoseconds_high) { const auto timeout_ns = static_cast<s64>(timeout_ns_low | (u64{timeout_ns_high} << 32));
const auto nanoseconds = static_cast<s64>(nanoseconds_low | (u64{nanoseconds_high} << 32)); return WaitProcessWideKeyAtomic(system, address, cv_key, tag, timeout_ns);
return WaitProcessWideKeyAtomic(system, mutex_addr, condition_variable_addr, thread_handle,
nanoseconds);
} }
/// Signal process wide key /// Signal process wide key
static void SignalProcessWideKey(Core::System& system, VAddr condition_variable_addr, s32 target) { static void SignalProcessWideKey(Core::System& system, VAddr cv_key, s32 count) {
LOG_TRACE(Kernel_SVC, "called, condition_variable_addr=0x{:X}, target=0x{:08X}", LOG_TRACE(Kernel_SVC, "called, cv_key=0x{:X}, count=0x{:08X}", cv_key, count);
condition_variable_addr, target);
ASSERT(condition_variable_addr == Common::AlignDown(condition_variable_addr, 4)); // Signal the condition variable.
return system.Kernel().CurrentProcess()->SignalConditionVariable(
Common::AlignDown(cv_key, sizeof(u32)), count);
}
// Retrieve a list of all threads that are waiting for this condition variable. static void SignalProcessWideKey32(Core::System& system, u32 cv_key, s32 count) {
auto& kernel = system.Kernel(); SignalProcessWideKey(system, cv_key, count);
KScopedSchedulerLock lock(kernel); }
auto* const current_process = kernel.CurrentProcess();
std::vector<std::shared_ptr<Thread>> waiting_threads =
current_process->GetConditionVariableThreads(condition_variable_addr);
// Only process up to 'target' threads, unless 'target' is less equal 0, in which case process namespace {
// them all.
std::size_t last = waiting_threads.size();
if (target > 0) {
last = std::min(waiting_threads.size(), static_cast<std::size_t>(target));
}
for (std::size_t index = 0; index < last; ++index) {
auto& thread = waiting_threads[index];
ASSERT(thread->GetCondVarWaitAddress() == condition_variable_addr); constexpr bool IsValidSignalType(Svc::SignalType type) {
switch (type) {
// liberate Cond Var Thread. case Svc::SignalType::Signal:
current_process->RemoveConditionVariableThread(thread); case Svc::SignalType::SignalAndIncrementIfEqual:
case Svc::SignalType::SignalAndModifyByWaitingCountIfEqual:
const std::size_t current_core = system.CurrentCoreIndex(); return true;
auto& monitor = system.Monitor(); default:
return false;
// Atomically read the value of the mutex.
u32 mutex_val = 0;
u32 update_val = 0;
const VAddr mutex_address = thread->GetMutexWaitAddress();
do {
// If the mutex is not yet acquired, acquire it.
mutex_val = monitor.ExclusiveRead32(current_core, mutex_address);
if (mutex_val != 0) {
update_val = mutex_val | Mutex::MutexHasWaitersFlag;
} else {
update_val = thread->GetWaitHandle();
}
} while (!monitor.ExclusiveWrite32(current_core, mutex_address, update_val));
monitor.ClearExclusive();
if (mutex_val == 0) {
// We were able to acquire the mutex, resume this thread.
auto* const lock_owner = thread->GetLockOwner();
if (lock_owner != nullptr) {
lock_owner->RemoveMutexWaiter(thread);
}
thread->SetLockOwner(nullptr);
thread->SetSynchronizationResults(nullptr, RESULT_SUCCESS);
thread->Wakeup();
} else {
// 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 auto& handle_table = system.Kernel().CurrentProcess()->GetHandleTable();
auto owner = handle_table.Get<Thread>(owner_handle);
ASSERT(owner);
thread->SetWaitingCondVar(false);
owner->AddMutexWaiter(thread);
}
} }
} }
static void SignalProcessWideKey32(Core::System& system, u32 condition_variable_addr, s32 target) { constexpr bool IsValidArbitrationType(Svc::ArbitrationType type) {
SignalProcessWideKey(system, condition_variable_addr, target); switch (type) {
case Svc::ArbitrationType::WaitIfLessThan:
case Svc::ArbitrationType::DecrementAndWaitIfLessThan:
case Svc::ArbitrationType::WaitIfEqual:
return true;
default:
return false;
}
} }
} // namespace
// Wait for an address (via Address Arbiter) // Wait for an address (via Address Arbiter)
static ResultCode WaitForAddress(Core::System& system, VAddr address, u32 type, s32 value, static ResultCode WaitForAddress(Core::System& system, VAddr address, Svc::ArbitrationType arb_type,
s64 timeout) { s32 value, s64 timeout_ns) {
LOG_TRACE(Kernel_SVC, "called, address=0x{:X}, type=0x{:X}, value=0x{:X}, timeout={}", address, LOG_TRACE(Kernel_SVC, "called, address=0x{:X}, arb_type=0x{:X}, value=0x{:X}, timeout_ns={}",
type, value, timeout); address, arb_type, value, timeout_ns);
// If the passed address is a kernel virtual address, return invalid memory state. // Validate input.
if (Core::Memory::IsKernelVirtualAddress(address)) { R_UNLESS(!Memory::IsKernelAddress(address), Svc::ResultInvalidCurrentMemory);
LOG_ERROR(Kernel_SVC, "Address is a kernel virtual address, address={:016X}", address); R_UNLESS(Common::IsAligned(address, sizeof(int32_t)), Svc::ResultInvalidAddress);
return ERR_INVALID_ADDRESS_STATE; R_UNLESS(IsValidArbitrationType(arb_type), Svc::ResultInvalidEnumValue);
// Convert timeout from nanoseconds to ticks.
s64 timeout{};
if (timeout_ns > 0) {
const s64 offset_tick(timeout_ns);
if (offset_tick > 0) {
timeout = offset_tick + 2;
if (timeout <= 0) {
timeout = std::numeric_limits<s64>::max();
}
} else {
timeout = std::numeric_limits<s64>::max();
}
} else {
timeout = timeout_ns;
} }
// If the address is not properly aligned to 4 bytes, return invalid address. return system.Kernel().CurrentProcess()->WaitAddressArbiter(address, arb_type, value, timeout);
if (!Common::IsWordAligned(address)) {
LOG_ERROR(Kernel_SVC, "Address is not word aligned, address={:016X}", address);
return ERR_INVALID_ADDRESS;
}
const auto arbitration_type = static_cast<AddressArbiter::ArbitrationType>(type);
auto& address_arbiter = system.Kernel().CurrentProcess()->GetAddressArbiter();
const ResultCode result =
address_arbiter.WaitForAddress(address, arbitration_type, value, timeout);
return result;
} }
static ResultCode WaitForAddress32(Core::System& system, u32 address, u32 type, s32 value, static ResultCode WaitForAddress32(Core::System& system, u32 address, Svc::ArbitrationType arb_type,
u32 timeout_low, u32 timeout_high) { s32 value, u32 timeout_ns_low, u32 timeout_ns_high) {
const auto timeout = static_cast<s64>(timeout_low | (u64{timeout_high} << 32)); const auto timeout = static_cast<s64>(timeout_ns_low | (u64{timeout_ns_high} << 32));
return WaitForAddress(system, address, type, value, timeout); return WaitForAddress(system, address, arb_type, value, timeout);
} }
// Signals to an address (via Address Arbiter) // Signals to an address (via Address Arbiter)
static ResultCode SignalToAddress(Core::System& system, VAddr address, u32 type, s32 value, static ResultCode SignalToAddress(Core::System& system, VAddr address, Svc::SignalType signal_type,
s32 num_to_wake) { s32 value, s32 count) {
LOG_TRACE(Kernel_SVC, "called, address=0x{:X}, type=0x{:X}, value=0x{:X}, num_to_wake=0x{:X}", LOG_TRACE(Kernel_SVC, "called, address=0x{:X}, signal_type=0x{:X}, value=0x{:X}, count=0x{:X}",
address, type, value, num_to_wake); address, signal_type, value, count);
// If the passed address is a kernel virtual address, return invalid memory state. // Validate input.
if (Core::Memory::IsKernelVirtualAddress(address)) { R_UNLESS(!Memory::IsKernelAddress(address), Svc::ResultInvalidCurrentMemory);
LOG_ERROR(Kernel_SVC, "Address is a kernel virtual address, address={:016X}", address); R_UNLESS(Common::IsAligned(address, sizeof(s32)), Svc::ResultInvalidAddress);
return ERR_INVALID_ADDRESS_STATE; R_UNLESS(IsValidSignalType(signal_type), Svc::ResultInvalidEnumValue);
}
// If the address is not properly aligned to 4 bytes, return invalid address. return system.Kernel().CurrentProcess()->SignalAddressArbiter(address, signal_type, value,
if (!Common::IsWordAligned(address)) { count);
LOG_ERROR(Kernel_SVC, "Address is not word aligned, address={:016X}", address);
return ERR_INVALID_ADDRESS;
}
const auto signal_type = static_cast<AddressArbiter::SignalType>(type);
auto& address_arbiter = system.Kernel().CurrentProcess()->GetAddressArbiter();
return address_arbiter.SignalToAddress(address, signal_type, value, num_to_wake);
} }
static ResultCode SignalToAddress32(Core::System& system, u32 address, u32 type, s32 value, static ResultCode SignalToAddress32(Core::System& system, u32 address, Svc::SignalType signal_type,
s32 num_to_wake) { s32 value, s32 count) {
return SignalToAddress(system, address, type, value, num_to_wake); return SignalToAddress(system, address, signal_type, value, count);
} }
static void KernelDebug([[maybe_unused]] Core::System& system, static void KernelDebug([[maybe_unused]] Core::System& system,

View File

@ -8,6 +8,7 @@
namespace Kernel::Svc { namespace Kernel::Svc {
constexpr s32 ArgumentHandleCountMax = 0x40;
constexpr u32 HandleWaitMask{1u << 30}; constexpr u32 HandleWaitMask{1u << 30};
} // namespace Kernel::Svc } // namespace Kernel::Svc

View File

@ -7,6 +7,7 @@
#include "common/common_types.h" #include "common/common_types.h"
#include "core/arm/arm_interface.h" #include "core/arm/arm_interface.h"
#include "core/core.h" #include "core/core.h"
#include "core/hle/kernel/svc_types.h"
#include "core/hle/result.h" #include "core/hle/result.h"
namespace Kernel { namespace Kernel {
@ -277,16 +278,20 @@ void SvcWrap64(Core::System& system) {
FuncReturn(system, retval); FuncReturn(system, retval);
} }
template <ResultCode func(Core::System&, u64, u32, s32, s64)> // Used by WaitForAddress
template <ResultCode func(Core::System&, u64, Svc::ArbitrationType, s32, s64)>
void SvcWrap64(Core::System& system) { void SvcWrap64(Core::System& system) {
FuncReturn(system, func(system, Param(system, 0), static_cast<u32>(Param(system, 1)), FuncReturn(system,
func(system, Param(system, 0), static_cast<Svc::ArbitrationType>(Param(system, 1)),
static_cast<s32>(Param(system, 2)), static_cast<s64>(Param(system, 3))) static_cast<s32>(Param(system, 2)), static_cast<s64>(Param(system, 3)))
.raw); .raw);
} }
template <ResultCode func(Core::System&, u64, u32, s32, s32)> // Used by SignalToAddress
template <ResultCode func(Core::System&, u64, Svc::SignalType, s32, s32)>
void SvcWrap64(Core::System& system) { void SvcWrap64(Core::System& system) {
FuncReturn(system, func(system, Param(system, 0), static_cast<u32>(Param(system, 1)), FuncReturn(system,
func(system, Param(system, 0), static_cast<Svc::SignalType>(Param(system, 1)),
static_cast<s32>(Param(system, 2)), static_cast<s32>(Param(system, 3))) static_cast<s32>(Param(system, 2)), static_cast<s32>(Param(system, 3)))
.raw); .raw);
} }
@ -504,20 +509,21 @@ void SvcWrap32(Core::System& system) {
} }
// Used by WaitForAddress32 // Used by WaitForAddress32
template <ResultCode func(Core::System&, u32, u32, s32, u32, u32)> template <ResultCode func(Core::System&, u32, Svc::ArbitrationType, s32, u32, u32)>
void SvcWrap32(Core::System& system) { void SvcWrap32(Core::System& system) {
const u32 retval = func(system, static_cast<u32>(Param(system, 0)), const u32 retval = func(system, static_cast<u32>(Param(system, 0)),
static_cast<u32>(Param(system, 1)), static_cast<s32>(Param(system, 2)), static_cast<Svc::ArbitrationType>(Param(system, 1)),
static_cast<u32>(Param(system, 3)), static_cast<u32>(Param(system, 4))) static_cast<s32>(Param(system, 2)), static_cast<u32>(Param(system, 3)),
static_cast<u32>(Param(system, 4)))
.raw; .raw;
FuncReturn(system, retval); FuncReturn(system, retval);
} }
// Used by SignalToAddress32 // Used by SignalToAddress32
template <ResultCode func(Core::System&, u32, u32, s32, s32)> template <ResultCode func(Core::System&, u32, Svc::SignalType, s32, s32)>
void SvcWrap32(Core::System& system) { void SvcWrap32(Core::System& system) {
const u32 retval = const u32 retval = func(system, static_cast<u32>(Param(system, 0)),
func(system, static_cast<u32>(Param(system, 0)), static_cast<u32>(Param(system, 1)), static_cast<Svc::SignalType>(Param(system, 1)),
static_cast<s32>(Param(system, 2)), static_cast<s32>(Param(system, 3))) static_cast<s32>(Param(system, 2)), static_cast<s32>(Param(system, 3)))
.raw; .raw;
FuncReturn(system, retval); FuncReturn(system, retval);

View File

@ -17,9 +17,11 @@
#include "core/hardware_properties.h" #include "core/hardware_properties.h"
#include "core/hle/kernel/errors.h" #include "core/hle/kernel/errors.h"
#include "core/hle/kernel/handle_table.h" #include "core/hle/kernel/handle_table.h"
#include "core/hle/kernel/k_condition_variable.h"
#include "core/hle/kernel/k_scheduler.h" #include "core/hle/kernel/k_scheduler.h"
#include "core/hle/kernel/k_scoped_scheduler_lock_and_sleep.h" #include "core/hle/kernel/k_scoped_scheduler_lock_and_sleep.h"
#include "core/hle/kernel/kernel.h" #include "core/hle/kernel/kernel.h"
#include "core/hle/kernel/memory/memory_layout.h"
#include "core/hle/kernel/object.h" #include "core/hle/kernel/object.h"
#include "core/hle/kernel/process.h" #include "core/hle/kernel/process.h"
#include "core/hle/kernel/thread.h" #include "core/hle/kernel/thread.h"
@ -61,24 +63,6 @@ void Thread::Stop() {
} }
void Thread::Wakeup() { void Thread::Wakeup() {
KScopedSchedulerLock lock(kernel);
switch (thread_state) {
case ThreadState::Runnable:
// If the thread is waiting on multiple wait objects, it might be awoken more than once
// before actually resuming. We can ignore subsequent wakeups if the thread status has
// already been set to ThreadStatus::Ready.
return;
case ThreadState::Terminated:
// This should never happen, as threads must complete before being stopped.
DEBUG_ASSERT_MSG(false, "Thread with object id {} cannot be resumed because it's DEAD.",
GetObjectId());
return;
}
SetState(ThreadState::Runnable);
}
void Thread::OnWakeUp() {
KScopedSchedulerLock lock(kernel); KScopedSchedulerLock lock(kernel);
SetState(ThreadState::Runnable); SetState(ThreadState::Runnable);
} }
@ -167,15 +151,14 @@ ResultVal<std::shared_ptr<Thread>> Thread::Create(Core::System& system, ThreadTy
thread->stack_top = stack_top; thread->stack_top = stack_top;
thread->disable_count = 1; thread->disable_count = 1;
thread->tpidr_el0 = 0; thread->tpidr_el0 = 0;
thread->nominal_priority = thread->current_priority = priority; thread->current_priority = priority;
thread->base_priority = priority;
thread->lock_owner = nullptr;
thread->schedule_count = -1; thread->schedule_count = -1;
thread->last_scheduled_tick = 0; thread->last_scheduled_tick = 0;
thread->processor_id = processor_id; thread->processor_id = processor_id;
thread->ideal_core = processor_id; thread->ideal_core = processor_id;
thread->affinity_mask.SetAffinity(processor_id, true); thread->affinity_mask.SetAffinity(processor_id, true);
thread->mutex_wait_address = 0;
thread->condvar_wait_address = 0;
thread->wait_handle = 0;
thread->name = std::move(name); thread->name = std::move(name);
thread->global_handle = kernel.GlobalHandleTable().Create(thread).Unwrap(); thread->global_handle = kernel.GlobalHandleTable().Create(thread).Unwrap();
thread->owner_process = owner_process; thread->owner_process = owner_process;
@ -205,12 +188,17 @@ ResultVal<std::shared_ptr<Thread>> Thread::Create(Core::System& system, ThreadTy
return MakeResult<std::shared_ptr<Thread>>(std::move(thread)); return MakeResult<std::shared_ptr<Thread>>(std::move(thread));
} }
void Thread::SetPriority(u32 priority) { void Thread::SetBasePriority(u32 priority) {
KScopedSchedulerLock lock(kernel);
ASSERT_MSG(priority <= THREADPRIO_LOWEST && priority >= THREADPRIO_HIGHEST, ASSERT_MSG(priority <= THREADPRIO_LOWEST && priority >= THREADPRIO_HIGHEST,
"Invalid priority value."); "Invalid priority value.");
nominal_priority = priority;
UpdatePriority(); KScopedSchedulerLock lock(kernel);
// Change our base priority.
base_priority = priority;
// Perform a priority restoration.
RestorePriority(kernel, this);
} }
void Thread::SetSynchronizationResults(KSynchronizationObject* object, ResultCode result) { void Thread::SetSynchronizationResults(KSynchronizationObject* object, ResultCode result) {
@ -224,95 +212,146 @@ VAddr Thread::GetCommandBufferAddress() const {
return GetTLSAddress() + command_header_offset; return GetTLSAddress() + command_header_offset;
} }
void Thread::SetState(ThreadState new_status) { void Thread::SetState(ThreadState state) {
if (new_status == thread_state) { KScopedSchedulerLock sl(kernel);
return;
SetMutexWaitAddressForDebugging(0);
const ThreadState old_state = thread_state;
thread_state =
static_cast<ThreadState>((old_state & ~ThreadState::Mask) | (state & ThreadState::Mask));
if (thread_state != old_state) {
KScheduler::OnThreadStateChanged(kernel, this, old_state);
} }
if (new_status != ThreadState::Waiting) {
SetWaitingCondVar(false);
}
SetSchedulingStatus(new_status);
thread_state = new_status;
} }
void Thread::AddMutexWaiter(std::shared_ptr<Thread> thread) { void Thread::AddWaiterImpl(Thread* thread) {
if (thread->lock_owner.get() == this) { ASSERT(kernel.GlobalSchedulerContext().IsLocked());
// If the thread is already waiting for this thread to release the mutex, ensure that the
// waiters list is consistent and return without doing anything. // Find the right spot to insert the waiter.
const auto iter = std::find(wait_mutex_threads.begin(), wait_mutex_threads.end(), thread); auto it = waiter_list.begin();
ASSERT(iter != wait_mutex_threads.end()); while (it != waiter_list.end()) {
return; if (it->GetPriority() > thread->GetPriority()) {
break;
}
it++;
} }
// A thread can't wait on two different mutexes at the same time. // Keep track of how many kernel waiters we have.
ASSERT(thread->lock_owner == nullptr); if (Memory::IsKernelAddressKey(thread->GetAddressKey())) {
ASSERT((num_kernel_waiters++) >= 0);
}
// Ensure that the thread is not already in the list of mutex waiters // Insert the waiter.
const auto iter = std::find(wait_mutex_threads.begin(), wait_mutex_threads.end(), thread); waiter_list.insert(it, *thread);
ASSERT(iter == wait_mutex_threads.end()); thread->SetLockOwner(this);
// Keep the list in an ordered fashion
const auto insertion_point = std::find_if(
wait_mutex_threads.begin(), wait_mutex_threads.end(),
[&thread](const auto& entry) { return entry->GetPriority() > thread->GetPriority(); });
wait_mutex_threads.insert(insertion_point, thread);
thread->lock_owner = SharedFrom(this);
UpdatePriority();
} }
void Thread::RemoveMutexWaiter(std::shared_ptr<Thread> thread) { void Thread::RemoveWaiterImpl(Thread* thread) {
ASSERT(thread->lock_owner.get() == this); ASSERT(kernel.GlobalSchedulerContext().IsLocked());
// Ensure that the thread is in the list of mutex waiters // Keep track of how many kernel waiters we have.
const auto iter = std::find(wait_mutex_threads.begin(), wait_mutex_threads.end(), thread); if (Memory::IsKernelAddressKey(thread->GetAddressKey())) {
ASSERT(iter != wait_mutex_threads.end()); ASSERT((num_kernel_waiters--) > 0);
}
wait_mutex_threads.erase(iter); // Remove the waiter.
waiter_list.erase(waiter_list.iterator_to(*thread));
thread->lock_owner = nullptr; thread->SetLockOwner(nullptr);
UpdatePriority();
} }
void Thread::UpdatePriority() { void Thread::RestorePriority(KernelCore& kernel, Thread* thread) {
// If any of the threads waiting on the mutex have a higher priority ASSERT(kernel.GlobalSchedulerContext().IsLocked());
// (taking into account priority inheritance), then this thread inherits
// that thread's priority. while (true) {
u32 new_priority = nominal_priority; // We want to inherit priority where possible.
if (!wait_mutex_threads.empty()) { s32 new_priority = thread->GetBasePriority();
if (wait_mutex_threads.front()->current_priority < new_priority) { if (thread->HasWaiters()) {
new_priority = wait_mutex_threads.front()->current_priority; new_priority = std::min(new_priority, thread->waiter_list.front().GetPriority());
}
} }
if (new_priority == current_priority) { // If the priority we would inherit is not different from ours, don't do anything.
if (new_priority == thread->GetPriority()) {
return; return;
} }
if (GetState() == ThreadState::Waiting && is_waiting_on_condvar) { // Ensure we don't violate condition variable red black tree invariants.
owner_process->RemoveConditionVariableThread(SharedFrom(this)); if (auto* cv_tree = thread->GetConditionVariableTree(); cv_tree != nullptr) {
BeforeUpdatePriority(kernel, cv_tree, thread);
} }
SetCurrentPriority(new_priority); // Change the priority.
const s32 old_priority = thread->GetPriority();
thread->SetPriority(new_priority);
if (GetState() == ThreadState::Waiting && is_waiting_on_condvar) { // Restore the condition variable, if relevant.
owner_process->InsertConditionVariableThread(SharedFrom(this)); if (auto* cv_tree = thread->GetConditionVariableTree(); cv_tree != nullptr) {
AfterUpdatePriority(kernel, cv_tree, thread);
} }
if (!lock_owner) { // Update the scheduler.
KScheduler::OnThreadPriorityChanged(kernel, thread, old_priority);
// Keep the lock owner up to date.
Thread* lock_owner = thread->GetLockOwner();
if (lock_owner == nullptr) {
return; return;
} }
// Ensure that the thread is within the correct location in the waiting list. // Update the thread in the lock owner's sorted list, and continue inheriting.
auto old_owner = lock_owner; lock_owner->RemoveWaiterImpl(thread);
lock_owner->RemoveMutexWaiter(SharedFrom(this)); lock_owner->AddWaiterImpl(thread);
old_owner->AddMutexWaiter(SharedFrom(this)); thread = lock_owner;
}
}
// Recursively update the priority of the thread that depends on the priority of this one. void Thread::AddWaiter(Thread* thread) {
lock_owner->UpdatePriority(); AddWaiterImpl(thread);
RestorePriority(kernel, this);
}
void Thread::RemoveWaiter(Thread* thread) {
RemoveWaiterImpl(thread);
RestorePriority(kernel, this);
}
Thread* Thread::RemoveWaiterByKey(s32* out_num_waiters, VAddr key) {
ASSERT(kernel.GlobalSchedulerContext().IsLocked());
s32 num_waiters{};
Thread* next_lock_owner{};
auto it = waiter_list.begin();
while (it != waiter_list.end()) {
if (it->GetAddressKey() == key) {
Thread* thread = std::addressof(*it);
// Keep track of how many kernel waiters we have.
if (Memory::IsKernelAddressKey(thread->GetAddressKey())) {
ASSERT((num_kernel_waiters--) > 0);
}
it = waiter_list.erase(it);
// Update the next lock owner.
if (next_lock_owner == nullptr) {
next_lock_owner = thread;
next_lock_owner->SetLockOwner(nullptr);
} else {
next_lock_owner->AddWaiterImpl(thread);
}
num_waiters++;
} else {
it++;
}
}
// Do priority updates, if we have a next owner.
if (next_lock_owner) {
RestorePriority(kernel, this);
RestorePriority(kernel, next_lock_owner);
}
// Return output.
*out_num_waiters = num_waiters;
return next_lock_owner;
} }
ResultCode Thread::SetActivity(ThreadActivity value) { ResultCode Thread::SetActivity(ThreadActivity value) {
@ -372,18 +411,6 @@ void Thread::RemoveSchedulingFlag(ThreadSchedFlags flag) {
KScheduler::OnThreadStateChanged(kernel, this, old_state); KScheduler::OnThreadStateChanged(kernel, this, old_state);
} }
void Thread::SetSchedulingStatus(ThreadState new_status) {
const auto old_state = GetRawState();
thread_state = (thread_state & ThreadState::HighMask) | new_status;
KScheduler::OnThreadStateChanged(kernel, this, old_state);
}
void Thread::SetCurrentPriority(u32 new_priority) {
const u32 old_priority = std::exchange(current_priority, new_priority);
KScheduler::OnThreadPriorityChanged(kernel, this, kernel.CurrentScheduler()->GetCurrentThread(),
old_priority);
}
ResultCode Thread::SetCoreAndAffinityMask(s32 new_core, u64 new_affinity_mask) { ResultCode Thread::SetCoreAndAffinityMask(s32 new_core, u64 new_affinity_mask) {
KScopedSchedulerLock lock(kernel); KScopedSchedulerLock lock(kernel);
const auto HighestSetCore = [](u64 mask, u32 max_cores) { const auto HighestSetCore = [](u64 mask, u32 max_cores) {

View File

@ -6,16 +6,21 @@
#include <array> #include <array>
#include <functional> #include <functional>
#include <span>
#include <string> #include <string>
#include <utility> #include <utility>
#include <vector> #include <vector>
#include <boost/intrusive/list.hpp>
#include "common/common_types.h" #include "common/common_types.h"
#include "common/intrusive_red_black_tree.h"
#include "common/spin_lock.h" #include "common/spin_lock.h"
#include "core/arm/arm_interface.h" #include "core/arm/arm_interface.h"
#include "core/hle/kernel/k_affinity_mask.h" #include "core/hle/kernel/k_affinity_mask.h"
#include "core/hle/kernel/k_synchronization_object.h" #include "core/hle/kernel/k_synchronization_object.h"
#include "core/hle/kernel/object.h" #include "core/hle/kernel/object.h"
#include "core/hle/kernel/svc_common.h"
#include "core/hle/result.h" #include "core/hle/result.h"
namespace Common { namespace Common {
@ -89,8 +94,6 @@ enum class ThreadState : u16 {
InitSuspended = (1 << (4 + SuspendShift)), InitSuspended = (1 << (4 + SuspendShift)),
SuspendFlagMask = ((1 << 5) - 1) << SuspendShift, SuspendFlagMask = ((1 << 5) - 1) << SuspendShift,
HighMask = 0xfff0,
}; };
DECLARE_ENUM_FLAG_OPERATORS(ThreadState); DECLARE_ENUM_FLAG_OPERATORS(ThreadState);
@ -111,7 +114,10 @@ enum class ThreadSchedFlags : u32 {
KernelInitPauseFlag = 1 << 8, KernelInitPauseFlag = 1 << 8,
}; };
class Thread final : public KSynchronizationObject { class Thread final : public KSynchronizationObject, public boost::intrusive::list_base_hook<> {
friend class KScheduler;
friend class Process;
public: public:
explicit Thread(KernelCore& kernel); explicit Thread(KernelCore& kernel);
~Thread() override; ~Thread() override;
@ -180,49 +186,46 @@ public:
* Gets the thread's current priority * Gets the thread's current priority
* @return The current thread's priority * @return The current thread's priority
*/ */
u32 GetPriority() const { [[nodiscard]] s32 GetPriority() const {
return current_priority; return current_priority;
} }
/**
* Sets the thread's current priority.
* @param priority The new priority.
*/
void SetPriority(s32 priority) {
current_priority = priority;
}
/** /**
* Gets the thread's nominal priority. * Gets the thread's nominal priority.
* @return The current thread's nominal priority. * @return The current thread's nominal priority.
*/ */
u32 GetNominalPriority() const { [[nodiscard]] s32 GetBasePriority() const {
return nominal_priority; return base_priority;
} }
/** /**
* Sets the thread's current priority * Sets the thread's nominal priority.
* @param priority The new priority * @param priority The new priority.
*/ */
void SetPriority(u32 priority); void SetBasePriority(u32 priority);
/// Adds a thread to the list of threads that are waiting for a lock held by this thread.
void AddMutexWaiter(std::shared_ptr<Thread> thread);
/// Removes a thread from the list of threads that are waiting for a lock held by this thread.
void RemoveMutexWaiter(std::shared_ptr<Thread> thread);
/// Recalculates the current priority taking into account priority inheritance.
void UpdatePriority();
/// Changes the core that the thread is running or scheduled to run on. /// Changes the core that the thread is running or scheduled to run on.
ResultCode SetCoreAndAffinityMask(s32 new_core, u64 new_affinity_mask); [[nodiscard]] ResultCode SetCoreAndAffinityMask(s32 new_core, u64 new_affinity_mask);
/** /**
* Gets the thread's thread ID * Gets the thread's thread ID
* @return The thread's ID * @return The thread's ID
*/ */
u64 GetThreadID() const { [[nodiscard]] u64 GetThreadID() const {
return thread_id; return thread_id;
} }
/// Resumes a thread from waiting /// Resumes a thread from waiting
void Wakeup(); void Wakeup();
void OnWakeUp();
ResultCode Start(); ResultCode Start();
virtual bool IsSignaled() const override; virtual bool IsSignaled() const override;
@ -242,7 +245,7 @@ public:
} }
ResultCode GetWaitResult(KSynchronizationObject** out) const { ResultCode GetWaitResult(KSynchronizationObject** out) const {
*out = this->signaling_object; *out = signaling_object;
return signaling_result; return signaling_result;
} }
@ -328,18 +331,14 @@ public:
return thread_state; return thread_state;
} }
void SetState(ThreadState new_state); void SetState(ThreadState state);
void SetWaitingCondVar(bool value) {
is_waiting_on_condvar = value;
}
s64 GetLastScheduledTick() const { s64 GetLastScheduledTick() const {
return this->last_scheduled_tick; return last_scheduled_tick;
} }
void SetLastScheduledTick(s64 tick) { void SetLastScheduledTick(s64 tick) {
this->last_scheduled_tick = tick; last_scheduled_tick = tick;
} }
u64 GetTotalCPUTimeTicks() const { u64 GetTotalCPUTimeTicks() const {
@ -379,55 +378,13 @@ public:
} }
Thread* GetLockOwner() const { Thread* GetLockOwner() const {
return lock_owner.get(); return lock_owner;
} }
void SetLockOwner(std::shared_ptr<Thread> owner) { void SetLockOwner(Thread* owner) {
lock_owner = std::move(owner); lock_owner = owner;
} }
VAddr GetCondVarWaitAddress() const {
return condvar_wait_address;
}
void SetCondVarWaitAddress(VAddr address) {
condvar_wait_address = address;
}
VAddr GetMutexWaitAddress() const {
return mutex_wait_address;
}
void SetMutexWaitAddress(VAddr address) {
mutex_wait_address = address;
}
Handle GetWaitHandle() const {
return wait_handle;
}
void SetWaitHandle(Handle handle) {
wait_handle = handle;
}
VAddr GetArbiterWaitAddress() const {
return arb_wait_address;
}
void SetArbiterWaitAddress(VAddr address) {
arb_wait_address = address;
}
void SetHLETimeEvent(Handle time_event) {
hle_time_event = time_event;
}
Handle GetHLETimeEvent() const {
return hle_time_event;
}
bool InvokeHLECallback(std::shared_ptr<Thread> thread);
u32 GetIdealCore() const { u32 GetIdealCore() const {
return ideal_core; return ideal_core;
} }
@ -442,11 +399,11 @@ public:
ResultCode Sleep(s64 nanoseconds); ResultCode Sleep(s64 nanoseconds);
s64 GetYieldScheduleCount() const { s64 GetYieldScheduleCount() const {
return this->schedule_count; return schedule_count;
} }
void SetYieldScheduleCount(s64 count) { void SetYieldScheduleCount(s64 count) {
this->schedule_count = count; schedule_count = count;
} }
bool IsRunning() const { bool IsRunning() const {
@ -469,14 +426,6 @@ public:
return global_handle; return global_handle;
} }
bool IsWaitingForArbitration() const {
return waiting_for_arbitration;
}
void WaitForArbitration(bool set) {
waiting_for_arbitration = set;
}
bool IsCancellable() const { bool IsCancellable() const {
return is_cancellable; return is_cancellable;
} }
@ -490,7 +439,7 @@ public:
} }
bool IsTerminationRequested() const { bool IsTerminationRequested() const {
return will_be_terminated || GetState() == ThreadState::Terminated; return will_be_terminated || GetRawState() == ThreadState::Terminated;
} }
bool IsPaused() const { bool IsPaused() const {
@ -522,21 +471,21 @@ public:
constexpr QueueEntry() = default; constexpr QueueEntry() = default;
constexpr void Initialize() { constexpr void Initialize() {
this->prev = nullptr; prev = nullptr;
this->next = nullptr; next = nullptr;
} }
constexpr Thread* GetPrev() const { constexpr Thread* GetPrev() const {
return this->prev; return prev;
} }
constexpr Thread* GetNext() const { constexpr Thread* GetNext() const {
return this->next; return next;
} }
constexpr void SetPrev(Thread* thread) { constexpr void SetPrev(Thread* thread) {
this->prev = thread; prev = thread;
} }
constexpr void SetNext(Thread* thread) { constexpr void SetNext(Thread* thread) {
this->next = thread; next = thread;
} }
private: private:
@ -545,11 +494,11 @@ public:
}; };
QueueEntry& GetPriorityQueueEntry(s32 core) { QueueEntry& GetPriorityQueueEntry(s32 core) {
return this->per_core_priority_queue_entry[core]; return per_core_priority_queue_entry[core];
} }
const QueueEntry& GetPriorityQueueEntry(s32 core) const { const QueueEntry& GetPriorityQueueEntry(s32 core) const {
return this->per_core_priority_queue_entry[core]; return per_core_priority_queue_entry[core];
} }
s32 GetDisableDispatchCount() const { s32 GetDisableDispatchCount() const {
@ -566,27 +515,155 @@ public:
disable_count--; disable_count--;
} }
void SetWaitObjectsForDebugging(KSynchronizationObject** objects, s32 num_objects) { void SetWaitObjectsForDebugging(const std::span<KSynchronizationObject*>& objects) {
wait_objects_for_debugging.clear(); wait_objects_for_debugging.clear();
wait_objects_for_debugging.reserve(num_objects); wait_objects_for_debugging.reserve(objects.size());
for (auto i = 0; i < num_objects; ++i) { for (const auto& object : objects) {
wait_objects_for_debugging.emplace_back(objects[i]); wait_objects_for_debugging.emplace_back(object);
} }
} }
const std::vector<KSynchronizationObject*>& GetWaitObjectsForDebugging() const { [[nodiscard]] const std::vector<KSynchronizationObject*>& GetWaitObjectsForDebugging() const {
return wait_objects_for_debugging; return wait_objects_for_debugging;
} }
private: void SetMutexWaitAddressForDebugging(VAddr address) {
friend class GlobalSchedulerContext; mutex_wait_address_for_debugging = address;
friend class KScheduler; }
friend class Process;
void SetSchedulingStatus(ThreadState new_status); [[nodiscard]] VAddr GetMutexWaitAddressForDebugging() const {
return mutex_wait_address_for_debugging;
}
void AddWaiter(Thread* thread);
void RemoveWaiter(Thread* thread);
[[nodiscard]] Thread* RemoveWaiterByKey(s32* out_num_waiters, VAddr key);
[[nodiscard]] VAddr GetAddressKey() const {
return address_key;
}
[[nodiscard]] u32 GetAddressKeyValue() const {
return address_key_value;
}
void SetAddressKey(VAddr key) {
address_key = key;
}
void SetAddressKey(VAddr key, u32 val) {
address_key = key;
address_key_value = val;
}
private:
static constexpr size_t PriorityInheritanceCountMax = 10;
union SyncObjectBuffer {
std::array<KSynchronizationObject*, Svc::ArgumentHandleCountMax> sync_objects{};
std::array<Handle,
Svc::ArgumentHandleCountMax*(sizeof(KSynchronizationObject*) / sizeof(Handle))>
handles;
constexpr SyncObjectBuffer() {}
};
static_assert(sizeof(SyncObjectBuffer::sync_objects) == sizeof(SyncObjectBuffer::handles));
struct ConditionVariableComparator {
struct LightCompareType {
u64 cv_key{};
s32 priority{};
[[nodiscard]] constexpr u64 GetConditionVariableKey() const {
return cv_key;
}
[[nodiscard]] constexpr s32 GetPriority() const {
return priority;
}
};
template <typename T>
requires(
std::same_as<T, Thread> ||
std::same_as<T, LightCompareType>) static constexpr int Compare(const T& lhs,
const Thread& rhs) {
const uintptr_t l_key = lhs.GetConditionVariableKey();
const uintptr_t r_key = rhs.GetConditionVariableKey();
if (l_key < r_key) {
// Sort first by key
return -1;
} else if (l_key == r_key && lhs.GetPriority() < rhs.GetPriority()) {
// And then by priority.
return -1;
} else {
return 1;
}
}
};
Common::IntrusiveRedBlackTreeNode condvar_arbiter_tree_node{};
using ConditionVariableThreadTreeTraits =
Common::IntrusiveRedBlackTreeMemberTraitsDeferredAssert<&Thread::condvar_arbiter_tree_node>;
using ConditionVariableThreadTree =
ConditionVariableThreadTreeTraits::TreeType<ConditionVariableComparator>;
public:
using ConditionVariableThreadTreeType = ConditionVariableThreadTree;
[[nodiscard]] uintptr_t GetConditionVariableKey() const {
return condvar_key;
}
[[nodiscard]] uintptr_t GetAddressArbiterKey() const {
return condvar_key;
}
void SetConditionVariable(ConditionVariableThreadTree* tree, VAddr address, uintptr_t cv_key,
u32 value) {
condvar_tree = tree;
condvar_key = cv_key;
address_key = address;
address_key_value = value;
}
void ClearConditionVariable() {
condvar_tree = nullptr;
}
[[nodiscard]] bool IsWaitingForConditionVariable() const {
return condvar_tree != nullptr;
}
void SetAddressArbiter(ConditionVariableThreadTree* tree, uintptr_t address) {
condvar_tree = tree;
condvar_key = address;
}
void ClearAddressArbiter() {
condvar_tree = nullptr;
}
[[nodiscard]] bool IsWaitingForAddressArbiter() const {
return condvar_tree != nullptr;
}
[[nodiscard]] ConditionVariableThreadTree* GetConditionVariableTree() const {
return condvar_tree;
}
[[nodiscard]] bool HasWaiters() const {
return !waiter_list.empty();
}
private:
void AddSchedulingFlag(ThreadSchedFlags flag); void AddSchedulingFlag(ThreadSchedFlags flag);
void RemoveSchedulingFlag(ThreadSchedFlags flag); void RemoveSchedulingFlag(ThreadSchedFlags flag);
void SetCurrentPriority(u32 new_priority); void AddWaiterImpl(Thread* thread);
void RemoveWaiterImpl(Thread* thread);
static void RestorePriority(KernelCore& kernel, Thread* thread);
Common::SpinLock context_guard{}; Common::SpinLock context_guard{};
ThreadContext32 context_32{}; ThreadContext32 context_32{};
@ -606,11 +683,11 @@ private:
/// Nominal thread priority, as set by the emulated application. /// Nominal thread priority, as set by the emulated application.
/// The nominal priority is the thread priority without priority /// The nominal priority is the thread priority without priority
/// inheritance taken into account. /// inheritance taken into account.
u32 nominal_priority = 0; s32 base_priority{};
/// Current thread priority. This may change over the course of the /// Current thread priority. This may change over the course of the
/// thread's lifetime in order to facilitate priority inheritance. /// thread's lifetime in order to facilitate priority inheritance.
u32 current_priority = 0; s32 current_priority{};
u64 total_cpu_time_ticks = 0; ///< Total CPU running ticks. u64 total_cpu_time_ticks = 0; ///< Total CPU running ticks.
s64 schedule_count{}; s64 schedule_count{};
@ -628,6 +705,9 @@ private:
/// passed to WaitSynchronization. This is used for debugging only. /// passed to WaitSynchronization. This is used for debugging only.
std::vector<KSynchronizationObject*> wait_objects_for_debugging; std::vector<KSynchronizationObject*> wait_objects_for_debugging;
/// The current mutex wait address. This is used for debugging only.
VAddr mutex_wait_address_for_debugging{};
KSynchronizationObject* signaling_object; KSynchronizationObject* signaling_object;
ResultCode signaling_result{RESULT_SUCCESS}; ResultCode signaling_result{RESULT_SUCCESS};
@ -635,25 +715,11 @@ private:
MutexWaitingThreads wait_mutex_threads; MutexWaitingThreads wait_mutex_threads;
/// Thread that owns the lock that this thread is waiting for. /// Thread that owns the lock that this thread is waiting for.
std::shared_ptr<Thread> lock_owner; Thread* lock_owner{};
/// If waiting on a ConditionVariable, this is the ConditionVariable address
VAddr condvar_wait_address = 0;
bool is_waiting_on_condvar{};
/// If waiting on a Mutex, this is the mutex address
VAddr mutex_wait_address = 0;
/// The handle used to wait for the mutex.
Handle wait_handle = 0;
/// If waiting for an AddressArbiter, this is the address being waited on.
VAddr arb_wait_address{0};
bool waiting_for_arbitration{};
/// Handle used as userdata to reference this object when inserting into the CoreTiming queue. /// Handle used as userdata to reference this object when inserting into the CoreTiming queue.
Handle global_handle = 0; Handle global_handle = 0;
Handle hle_time_event;
KScheduler* scheduler = nullptr; KScheduler* scheduler = nullptr;
std::array<QueueEntry, Core::Hardware::NUM_CPU_CORES> per_core_priority_queue_entry{}; std::array<QueueEntry, Core::Hardware::NUM_CPU_CORES> per_core_priority_queue_entry{};
@ -679,6 +745,16 @@ private:
bool signaled{}; bool signaled{};
ConditionVariableThreadTree* condvar_tree{};
uintptr_t condvar_key{};
VAddr address_key{};
u32 address_key_value{};
s32 num_kernel_waiters{};
using WaiterList = boost::intrusive::list<Thread>;
WaiterList waiter_list{};
WaiterList pinned_waiter_list{};
std::string name; std::string name;
}; };

View File

@ -18,12 +18,10 @@ TimeManager::TimeManager(Core::System& system_) : system{system_} {
time_manager_event_type = Core::Timing::CreateEvent( time_manager_event_type = Core::Timing::CreateEvent(
"Kernel::TimeManagerCallback", "Kernel::TimeManagerCallback",
[this](std::uintptr_t thread_handle, std::chrono::nanoseconds) { [this](std::uintptr_t thread_handle, std::chrono::nanoseconds) {
const KScopedSchedulerLock lock(system.Kernel());
const auto proper_handle = static_cast<Handle>(thread_handle);
std::shared_ptr<Thread> thread; std::shared_ptr<Thread> thread;
{ {
std::lock_guard lock{mutex}; std::lock_guard lock{mutex};
const auto proper_handle = static_cast<Handle>(thread_handle);
if (cancelled_events[proper_handle]) { if (cancelled_events[proper_handle]) {
return; return;
} }
@ -32,7 +30,7 @@ TimeManager::TimeManager(Core::System& system_) : system{system_} {
if (thread) { if (thread) {
// Thread can be null if process has exited // Thread can be null if process has exited
thread->OnWakeUp(); thread->Wakeup();
} }
}); });
} }

View File

@ -15,9 +15,9 @@
#include "core/hle/kernel/handle_table.h" #include "core/hle/kernel/handle_table.h"
#include "core/hle/kernel/k_scheduler.h" #include "core/hle/kernel/k_scheduler.h"
#include "core/hle/kernel/k_synchronization_object.h" #include "core/hle/kernel/k_synchronization_object.h"
#include "core/hle/kernel/mutex.h"
#include "core/hle/kernel/process.h" #include "core/hle/kernel/process.h"
#include "core/hle/kernel/readable_event.h" #include "core/hle/kernel/readable_event.h"
#include "core/hle/kernel/svc_common.h"
#include "core/hle/kernel/thread.h" #include "core/hle/kernel/thread.h"
#include "core/memory.h" #include "core/memory.h"
@ -116,7 +116,7 @@ QString WaitTreeText::GetText() const {
WaitTreeMutexInfo::WaitTreeMutexInfo(VAddr mutex_address, const Kernel::HandleTable& handle_table) WaitTreeMutexInfo::WaitTreeMutexInfo(VAddr mutex_address, const Kernel::HandleTable& handle_table)
: mutex_address(mutex_address) { : mutex_address(mutex_address) {
mutex_value = Core::System::GetInstance().Memory().Read32(mutex_address); mutex_value = Core::System::GetInstance().Memory().Read32(mutex_address);
owner_handle = static_cast<Kernel::Handle>(mutex_value & Kernel::Mutex::MutexOwnerMask); owner_handle = static_cast<Kernel::Handle>(mutex_value & Kernel::Svc::HandleWaitMask);
owner = handle_table.Get<Kernel::Thread>(owner_handle); owner = handle_table.Get<Kernel::Thread>(owner_handle);
} }
@ -127,7 +127,7 @@ QString WaitTreeMutexInfo::GetText() const {
} }
std::vector<std::unique_ptr<WaitTreeItem>> WaitTreeMutexInfo::GetChildren() const { std::vector<std::unique_ptr<WaitTreeItem>> WaitTreeMutexInfo::GetChildren() const {
const bool has_waiters = (mutex_value & Kernel::Mutex::MutexHasWaitersFlag) != 0; const bool has_waiters = (mutex_value & Kernel::Svc::HandleWaitMask) != 0;
std::vector<std::unique_ptr<WaitTreeItem>> list; std::vector<std::unique_ptr<WaitTreeItem>> list;
list.push_back(std::make_unique<WaitTreeText>(tr("has waiters: %1").arg(has_waiters))); list.push_back(std::make_unique<WaitTreeText>(tr("has waiters: %1").arg(has_waiters)));
@ -324,11 +324,11 @@ std::vector<std::unique_ptr<WaitTreeItem>> WaitTreeThread::GetChildren() const {
list.push_back(std::make_unique<WaitTreeText>(tr("thread id = %1").arg(thread.GetThreadID()))); list.push_back(std::make_unique<WaitTreeText>(tr("thread id = %1").arg(thread.GetThreadID())));
list.push_back(std::make_unique<WaitTreeText>(tr("priority = %1(current) / %2(normal)") list.push_back(std::make_unique<WaitTreeText>(tr("priority = %1(current) / %2(normal)")
.arg(thread.GetPriority()) .arg(thread.GetPriority())
.arg(thread.GetNominalPriority()))); .arg(thread.GetBasePriority())));
list.push_back(std::make_unique<WaitTreeText>( list.push_back(std::make_unique<WaitTreeText>(
tr("last running ticks = %1").arg(thread.GetLastScheduledTick()))); tr("last running ticks = %1").arg(thread.GetLastScheduledTick())));
const VAddr mutex_wait_address = thread.GetMutexWaitAddress(); const VAddr mutex_wait_address = thread.GetMutexWaitAddressForDebugging();
if (mutex_wait_address != 0) { if (mutex_wait_address != 0) {
const auto& handle_table = thread.GetOwnerProcess()->GetHandleTable(); const auto& handle_table = thread.GetOwnerProcess()->GetHandleTable();
list.push_back(std::make_unique<WaitTreeMutexInfo>(mutex_wait_address, handle_table)); list.push_back(std::make_unique<WaitTreeMutexInfo>(mutex_wait_address, handle_table));