core: hle: Integrate new KConditionVariable and KAddressArbiter implementations.
This commit is contained in:
parent
952d1ac487
commit
912dd50146
|
@ -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
|
||||||
|
|
|
@ -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
|
|
|
@ -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
|
|
|
@ -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) {
|
||||||
|
|
|
@ -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
|
|
|
@ -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
|
|
|
@ -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;
|
||||||
|
|
||||||
|
|
|
@ -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{};
|
||||||
|
|
||||||
|
|
|
@ -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);
|
|
||||||
return ERR_INVALID_ADDRESS_STATE;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!Common::IsWordAligned(mutex_addr)) {
|
// Convert timeout from nanoseconds to ticks.
|
||||||
LOG_ERROR(Kernel_SVC, "Given mutex address must be word-aligned. address=0x{:016X}",
|
s64 timeout{};
|
||||||
mutex_addr);
|
if (timeout_ns > 0) {
|
||||||
return ERR_INVALID_ADDRESS;
|
const s64 offset_tick(timeout_ns);
|
||||||
}
|
if (offset_tick > 0) {
|
||||||
|
timeout = offset_tick + 2;
|
||||||
ASSERT(condition_variable_addr == Common::AlignDown(condition_variable_addr, 4));
|
if (timeout <= 0) {
|
||||||
auto& kernel = system.Kernel();
|
timeout = std::numeric_limits<s64>::max();
|
||||||
Handle event_handle;
|
}
|
||||||
Thread* current_thread = kernel.CurrentScheduler()->GetCurrentThread();
|
} else {
|
||||||
auto* const current_process = kernel.CurrentProcess();
|
timeout = std::numeric_limits<s64>::max();
|
||||||
{
|
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
const auto release_result = current_process->GetMutex().Release(mutex_addr);
|
timeout = timeout_ns;
|
||||||
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) {
|
// Wait on the condition variable.
|
||||||
auto& time_manager = kernel.TimeManager();
|
return system.Kernel().CurrentProcess()->WaitConditionVariable(
|
||||||
time_manager.UnscheduleTimeEvent(event_handle);
|
address, Common::AlignDown(cv_key, sizeof(u32)), tag, timeout);
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
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,
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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,18 +278,22 @@ 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,
|
||||||
static_cast<s32>(Param(system, 2)), static_cast<s64>(Param(system, 3)))
|
func(system, Param(system, 0), static_cast<Svc::ArbitrationType>(Param(system, 1)),
|
||||||
.raw);
|
static_cast<s32>(Param(system, 2)), static_cast<s64>(Param(system, 3)))
|
||||||
|
.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,
|
||||||
static_cast<s32>(Param(system, 2)), static_cast<s32>(Param(system, 3)))
|
func(system, Param(system, 0), static_cast<Svc::SignalType>(Param(system, 1)),
|
||||||
.raw);
|
static_cast<s32>(Param(system, 2)), static_cast<s32>(Param(system, 3)))
|
||||||
|
.raw);
|
||||||
}
|
}
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
@ -504,22 +509,23 @@ 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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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 the priority we would inherit is not different from ours, don't do anything.
|
||||||
|
if (new_priority == thread->GetPriority()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ensure we don't violate condition variable red black tree invariants.
|
||||||
|
if (auto* cv_tree = thread->GetConditionVariableTree(); cv_tree != nullptr) {
|
||||||
|
BeforeUpdatePriority(kernel, cv_tree, thread);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Change the priority.
|
||||||
|
const s32 old_priority = thread->GetPriority();
|
||||||
|
thread->SetPriority(new_priority);
|
||||||
|
|
||||||
|
// Restore the condition variable, if relevant.
|
||||||
|
if (auto* cv_tree = thread->GetConditionVariableTree(); cv_tree != nullptr) {
|
||||||
|
AfterUpdatePriority(kernel, cv_tree, thread);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update the thread in the lock owner's sorted list, and continue inheriting.
|
||||||
|
lock_owner->RemoveWaiterImpl(thread);
|
||||||
|
lock_owner->AddWaiterImpl(thread);
|
||||||
|
thread = lock_owner;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Thread::AddWaiter(Thread* thread) {
|
||||||
|
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++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (new_priority == current_priority) {
|
// Do priority updates, if we have a next owner.
|
||||||
return;
|
if (next_lock_owner) {
|
||||||
|
RestorePriority(kernel, this);
|
||||||
|
RestorePriority(kernel, next_lock_owner);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (GetState() == ThreadState::Waiting && is_waiting_on_condvar) {
|
// Return output.
|
||||||
owner_process->RemoveConditionVariableThread(SharedFrom(this));
|
*out_num_waiters = num_waiters;
|
||||||
}
|
return next_lock_owner;
|
||||||
|
|
||||||
SetCurrentPriority(new_priority);
|
|
||||||
|
|
||||||
if (GetState() == ThreadState::Waiting && is_waiting_on_condvar) {
|
|
||||||
owner_process->InsertConditionVariableThread(SharedFrom(this));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!lock_owner) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Ensure that the thread is within the correct location in the waiting list.
|
|
||||||
auto old_owner = lock_owner;
|
|
||||||
lock_owner->RemoveMutexWaiter(SharedFrom(this));
|
|
||||||
old_owner->AddMutexWaiter(SharedFrom(this));
|
|
||||||
|
|
||||||
// Recursively update the priority of the thread that depends on the priority of this one.
|
|
||||||
lock_owner->UpdatePriority();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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) {
|
||||||
|
|
|
@ -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;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -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();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -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));
|
||||||
|
|
Reference in New Issue