diff options
Diffstat (limited to '')
46 files changed, 1844 insertions, 1829 deletions
diff --git a/src/core/hle/kernel/address_arbiter.cpp b/src/core/hle/kernel/address_arbiter.cpp deleted file mode 100644 index 20ffa7d47..000000000 --- a/src/core/hle/kernel/address_arbiter.cpp +++ /dev/null @@ -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]->ResumeFromWait(); - } -} - -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->IsPendingTermination()) { - 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->SetStatus(ThreadStatus::WaitArb); - 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->IsPendingTermination()) { - 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->SetStatus(ThreadStatus::WaitArb); - 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 diff --git a/src/core/hle/kernel/address_arbiter.h b/src/core/hle/kernel/address_arbiter.h deleted file mode 100644 index b91edc67d..000000000 --- a/src/core/hle/kernel/address_arbiter.h +++ /dev/null @@ -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 diff --git a/src/core/hle/kernel/client_port.cpp b/src/core/hle/kernel/client_port.cpp index 8aff2227a..f8f005f15 100644 --- a/src/core/hle/kernel/client_port.cpp +++ b/src/core/hle/kernel/client_port.cpp @@ -33,9 +33,6 @@ ResultVal<std::shared_ptr<ClientSession>> ClientPort::Connect() { server_port->AppendPendingSession(std::move(server)); } - // Wake the threads waiting on the ServerPort - server_port->Signal(); - return MakeResult(std::move(client)); } diff --git a/src/core/hle/kernel/client_session.cpp b/src/core/hle/kernel/client_session.cpp index be9eba519..e8e52900d 100644 --- a/src/core/hle/kernel/client_session.cpp +++ b/src/core/hle/kernel/client_session.cpp @@ -12,7 +12,7 @@ namespace Kernel { -ClientSession::ClientSession(KernelCore& kernel) : SynchronizationObject{kernel} {} +ClientSession::ClientSession(KernelCore& kernel) : KSynchronizationObject{kernel} {} ClientSession::~ClientSession() { // This destructor will be called automatically when the last ClientSession handle is closed by @@ -22,15 +22,6 @@ ClientSession::~ClientSession() { } } -bool ClientSession::ShouldWait(const Thread* thread) const { - UNIMPLEMENTED(); - return {}; -} - -void ClientSession::Acquire(Thread* thread) { - UNIMPLEMENTED(); -} - bool ClientSession::IsSignaled() const { UNIMPLEMENTED(); return true; diff --git a/src/core/hle/kernel/client_session.h b/src/core/hle/kernel/client_session.h index e5e0690c2..d5c9ebee8 100644 --- a/src/core/hle/kernel/client_session.h +++ b/src/core/hle/kernel/client_session.h @@ -7,7 +7,7 @@ #include <memory> #include <string> -#include "core/hle/kernel/synchronization_object.h" +#include "core/hle/kernel/k_synchronization_object.h" #include "core/hle/result.h" union ResultCode; @@ -26,7 +26,7 @@ class KernelCore; class Session; class Thread; -class ClientSession final : public SynchronizationObject { +class ClientSession final : public KSynchronizationObject { public: explicit ClientSession(KernelCore& kernel); ~ClientSession() override; @@ -49,10 +49,6 @@ public: ResultCode SendSyncRequest(std::shared_ptr<Thread> thread, Core::Memory::Memory& memory, Core::Timing::CoreTiming& core_timing); - bool ShouldWait(const Thread* thread) const override; - - void Acquire(Thread* thread) override; - bool IsSignaled() const override; private: diff --git a/src/core/hle/kernel/errors.h b/src/core/hle/kernel/errors.h index d4e5d88cf..7d32a39f0 100644 --- a/src/core/hle/kernel/errors.h +++ b/src/core/hle/kernel/errors.h @@ -13,12 +13,14 @@ namespace Kernel { constexpr ResultCode ERR_MAX_CONNECTIONS_REACHED{ErrorModule::Kernel, 7}; constexpr ResultCode ERR_INVALID_CAPABILITY_DESCRIPTOR{ErrorModule::Kernel, 14}; constexpr ResultCode ERR_THREAD_TERMINATING{ErrorModule::Kernel, 59}; +constexpr ResultCode ERR_TERMINATION_REQUESTED{ErrorModule::Kernel, 59}; constexpr ResultCode ERR_INVALID_SIZE{ErrorModule::Kernel, 101}; constexpr ResultCode ERR_INVALID_ADDRESS{ErrorModule::Kernel, 102}; constexpr ResultCode ERR_OUT_OF_RESOURCES{ErrorModule::Kernel, 103}; constexpr ResultCode ERR_OUT_OF_MEMORY{ErrorModule::Kernel, 104}; constexpr ResultCode ERR_HANDLE_TABLE_FULL{ErrorModule::Kernel, 105}; constexpr ResultCode ERR_INVALID_ADDRESS_STATE{ErrorModule::Kernel, 106}; +constexpr ResultCode ERR_INVALID_CURRENT_MEMORY{ErrorModule::Kernel, 106}; constexpr ResultCode ERR_INVALID_MEMORY_PERMISSIONS{ErrorModule::Kernel, 108}; constexpr ResultCode ERR_INVALID_MEMORY_RANGE{ErrorModule::Kernel, 110}; constexpr ResultCode ERR_INVALID_PROCESSOR_ID{ErrorModule::Kernel, 113}; @@ -28,6 +30,7 @@ constexpr ResultCode ERR_INVALID_POINTER{ErrorModule::Kernel, 115}; constexpr ResultCode ERR_INVALID_COMBINATION{ErrorModule::Kernel, 116}; constexpr ResultCode RESULT_TIMEOUT{ErrorModule::Kernel, 117}; constexpr ResultCode ERR_SYNCHRONIZATION_CANCELED{ErrorModule::Kernel, 118}; +constexpr ResultCode ERR_CANCELLED{ErrorModule::Kernel, 118}; constexpr ResultCode ERR_OUT_OF_RANGE{ErrorModule::Kernel, 119}; constexpr ResultCode ERR_INVALID_ENUM_VALUE{ErrorModule::Kernel, 120}; constexpr ResultCode ERR_NOT_FOUND{ErrorModule::Kernel, 121}; diff --git a/src/core/hle/kernel/k_address_arbiter.cpp b/src/core/hle/kernel/k_address_arbiter.cpp new file mode 100644 index 000000000..d9e702f13 --- /dev/null +++ b/src/core/hle/kernel/k_address_arbiter.cpp @@ -0,0 +1,367 @@ +// Copyright 2021 yuzu Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include "core/arm/exclusive_monitor.h" +#include "core/core.h" +#include "core/hle/kernel/k_address_arbiter.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/svc_results.h" +#include "core/hle/kernel/thread.h" +#include "core/hle/kernel/time_manager.h" +#include "core/memory.h" + +namespace Kernel { + +KAddressArbiter::KAddressArbiter(Core::System& system_) + : system{system_}, kernel{system.Kernel()} {} +KAddressArbiter::~KAddressArbiter() = default; + +namespace { + +bool ReadFromUser(Core::System& system, s32* out, VAddr address) { + *out = system.Memory().Read32(address); + return true; +} + +bool DecrementIfLessThan(Core::System& system, s32* out, VAddr address, s32 value) { + auto& monitor = system.Monitor(); + const auto current_core = system.CurrentCoreIndex(); + + // TODO(bunnei): We should disable interrupts here via KScopedInterruptDisable. + // TODO(bunnei): We should call CanAccessAtomic(..) here. + + // Load the value from the address. + const s32 current_value = static_cast<s32>(monitor.ExclusiveRead32(current_core, address)); + + // Compare it to the desired one. + if (current_value < value) { + // If less than, we want to try to decrement. + const s32 decrement_value = current_value - 1; + + // Decrement and try to store. + if (!monitor.ExclusiveWrite32(current_core, address, static_cast<u32>(decrement_value))) { + // If we failed to store, try again. + DecrementIfLessThan(system, out, address, value); + } + } else { + // Otherwise, clear our exclusive hold and finish + monitor.ClearExclusive(); + } + + // We're done. + *out = current_value; + return true; +} + +bool UpdateIfEqual(Core::System& system, s32* out, VAddr address, s32 value, s32 new_value) { + auto& monitor = system.Monitor(); + const auto current_core = system.CurrentCoreIndex(); + + // TODO(bunnei): We should disable interrupts here via KScopedInterruptDisable. + // TODO(bunnei): We should call CanAccessAtomic(..) here. + + // Load the value from the address. + const s32 current_value = static_cast<s32>(monitor.ExclusiveRead32(current_core, address)); + + // Compare it to the desired one. + if (current_value == value) { + // If equal, we want to try to write the new value. + + // Try to store. + if (!monitor.ExclusiveWrite32(current_core, address, static_cast<u32>(new_value))) { + // If we failed to store, try again. + UpdateIfEqual(system, out, address, value, new_value); + } + } else { + // Otherwise, clear our exclusive hold and finish. + monitor.ClearExclusive(); + } + + // We're done. + *out = current_value; + return true; +} + +} // namespace + +ResultCode KAddressArbiter::Signal(VAddr addr, s32 count) { + // Perform signaling. + s32 num_waiters{}; + { + KScopedSchedulerLock sl(kernel); + + auto it = thread_tree.nfind_light({addr, -1}); + while ((it != thread_tree.end()) && (count <= 0 || num_waiters < count) && + (it->GetAddressArbiterKey() == addr)) { + Thread* target_thread = std::addressof(*it); + target_thread->SetSyncedObject(nullptr, RESULT_SUCCESS); + + ASSERT(target_thread->IsWaitingForAddressArbiter()); + target_thread->Wakeup(); + + it = thread_tree.erase(it); + target_thread->ClearAddressArbiter(); + ++num_waiters; + } + } + return RESULT_SUCCESS; +} + +ResultCode KAddressArbiter::SignalAndIncrementIfEqual(VAddr addr, s32 value, s32 count) { + // Perform signaling. + s32 num_waiters{}; + { + KScopedSchedulerLock sl(kernel); + + // Check the userspace value. + s32 user_value{}; + R_UNLESS(UpdateIfEqual(system, std::addressof(user_value), addr, value, value + 1), + Svc::ResultInvalidCurrentMemory); + R_UNLESS(user_value == value, Svc::ResultInvalidState); + + auto it = thread_tree.nfind_light({addr, -1}); + while ((it != thread_tree.end()) && (count <= 0 || num_waiters < count) && + (it->GetAddressArbiterKey() == addr)) { + Thread* target_thread = std::addressof(*it); + target_thread->SetSyncedObject(nullptr, RESULT_SUCCESS); + + ASSERT(target_thread->IsWaitingForAddressArbiter()); + target_thread->Wakeup(); + + it = thread_tree.erase(it); + target_thread->ClearAddressArbiter(); + ++num_waiters; + } + } + return RESULT_SUCCESS; +} + +ResultCode KAddressArbiter::SignalAndModifyByWaitingCountIfEqual(VAddr addr, s32 value, s32 count) { + // Perform signaling. + s32 num_waiters{}; + { + KScopedSchedulerLock sl(kernel); + + auto it = thread_tree.nfind_light({addr, -1}); + // Determine the updated value. + s32 new_value{}; + if (/*GetTargetFirmware() >= TargetFirmware_7_0_0*/ true) { + if (count <= 0) { + if ((it != thread_tree.end()) && (it->GetAddressArbiterKey() == addr)) { + new_value = value - 2; + } else { + new_value = value + 1; + } + } else { + if ((it != thread_tree.end()) && (it->GetAddressArbiterKey() == addr)) { + auto tmp_it = it; + s32 tmp_num_waiters{}; + while ((++tmp_it != thread_tree.end()) && + (tmp_it->GetAddressArbiterKey() == addr)) { + if ((tmp_num_waiters++) >= count) { + break; + } + } + + if (tmp_num_waiters < count) { + new_value = value - 1; + } else { + new_value = value; + } + } else { + new_value = value + 1; + } + } + } else { + if (count <= 0) { + if ((it != thread_tree.end()) && (it->GetAddressArbiterKey() == addr)) { + new_value = value - 1; + } else { + new_value = value + 1; + } + } else { + auto tmp_it = it; + s32 tmp_num_waiters{}; + while ((tmp_it != thread_tree.end()) && (tmp_it->GetAddressArbiterKey() == addr) && + (tmp_num_waiters < count + 1)) { + ++tmp_num_waiters; + ++tmp_it; + } + + if (tmp_num_waiters == 0) { + new_value = value + 1; + } else if (tmp_num_waiters <= count) { + new_value = value - 1; + } else { + new_value = value; + } + } + } + + // Check the userspace value. + s32 user_value{}; + bool succeeded{}; + if (value != new_value) { + succeeded = UpdateIfEqual(system, std::addressof(user_value), addr, value, new_value); + } else { + succeeded = ReadFromUser(system, std::addressof(user_value), addr); + } + + R_UNLESS(succeeded, Svc::ResultInvalidCurrentMemory); + R_UNLESS(user_value == value, Svc::ResultInvalidState); + + while ((it != thread_tree.end()) && (count <= 0 || num_waiters < count) && + (it->GetAddressArbiterKey() == addr)) { + Thread* target_thread = std::addressof(*it); + target_thread->SetSyncedObject(nullptr, RESULT_SUCCESS); + + ASSERT(target_thread->IsWaitingForAddressArbiter()); + target_thread->Wakeup(); + + it = thread_tree.erase(it); + target_thread->ClearAddressArbiter(); + ++num_waiters; + } + } + return RESULT_SUCCESS; +} + +ResultCode KAddressArbiter::WaitIfLessThan(VAddr addr, s32 value, bool decrement, s64 timeout) { + // Prepare to wait. + Thread* cur_thread = kernel.CurrentScheduler()->GetCurrentThread(); + Handle timer = InvalidHandle; + + { + KScopedSchedulerLockAndSleep slp(kernel, timer, cur_thread, timeout); + + // Check that the thread isn't terminating. + if (cur_thread->IsTerminationRequested()) { + slp.CancelSleep(); + return Svc::ResultTerminationRequested; + } + + // Set the synced object. + cur_thread->SetSyncedObject(nullptr, Svc::ResultTimedOut); + + // Read the value from userspace. + s32 user_value{}; + bool succeeded{}; + if (decrement) { + succeeded = DecrementIfLessThan(system, std::addressof(user_value), addr, value); + } else { + succeeded = ReadFromUser(system, std::addressof(user_value), addr); + } + + if (!succeeded) { + slp.CancelSleep(); + return Svc::ResultInvalidCurrentMemory; + } + + // Check that the value is less than the specified one. + if (user_value >= value) { + slp.CancelSleep(); + return Svc::ResultInvalidState; + } + + // Check that the timeout is non-zero. + if (timeout == 0) { + slp.CancelSleep(); + return Svc::ResultTimedOut; + } + + // Set the arbiter. + cur_thread->SetAddressArbiter(std::addressof(thread_tree), addr); + thread_tree.insert(*cur_thread); + cur_thread->SetState(ThreadState::Waiting); + cur_thread->SetWaitReasonForDebugging(ThreadWaitReasonForDebugging::Arbitration); + } + + // Cancel the timer wait. + if (timer != InvalidHandle) { + auto& time_manager = kernel.TimeManager(); + time_manager.UnscheduleTimeEvent(timer); + } + + // Remove from the address arbiter. + { + KScopedSchedulerLock sl(kernel); + + if (cur_thread->IsWaitingForAddressArbiter()) { + thread_tree.erase(thread_tree.iterator_to(*cur_thread)); + cur_thread->ClearAddressArbiter(); + } + } + + // Get the result. + KSynchronizationObject* dummy{}; + return cur_thread->GetWaitResult(std::addressof(dummy)); +} + +ResultCode KAddressArbiter::WaitIfEqual(VAddr addr, s32 value, s64 timeout) { + // Prepare to wait. + Thread* cur_thread = kernel.CurrentScheduler()->GetCurrentThread(); + Handle timer = InvalidHandle; + + { + KScopedSchedulerLockAndSleep slp(kernel, timer, cur_thread, timeout); + + // Check that the thread isn't terminating. + if (cur_thread->IsTerminationRequested()) { + slp.CancelSleep(); + return Svc::ResultTerminationRequested; + } + + // Set the synced object. + cur_thread->SetSyncedObject(nullptr, Svc::ResultTimedOut); + + // Read the value from userspace. + s32 user_value{}; + if (!ReadFromUser(system, std::addressof(user_value), addr)) { + slp.CancelSleep(); + return Svc::ResultInvalidCurrentMemory; + } + + // Check that the value is equal. + if (value != user_value) { + slp.CancelSleep(); + return Svc::ResultInvalidState; + } + + // Check that the timeout is non-zero. + if (timeout == 0) { + slp.CancelSleep(); + return Svc::ResultTimedOut; + } + + // Set the arbiter. + cur_thread->SetAddressArbiter(std::addressof(thread_tree), addr); + thread_tree.insert(*cur_thread); + cur_thread->SetState(ThreadState::Waiting); + cur_thread->SetWaitReasonForDebugging(ThreadWaitReasonForDebugging::Arbitration); + } + + // Cancel the timer wait. + if (timer != InvalidHandle) { + auto& time_manager = kernel.TimeManager(); + time_manager.UnscheduleTimeEvent(timer); + } + + // Remove from the address arbiter. + { + KScopedSchedulerLock sl(kernel); + + if (cur_thread->IsWaitingForAddressArbiter()) { + thread_tree.erase(thread_tree.iterator_to(*cur_thread)); + cur_thread->ClearAddressArbiter(); + } + } + + // Get the result. + KSynchronizationObject* dummy{}; + return cur_thread->GetWaitResult(std::addressof(dummy)); +} + +} // namespace Kernel diff --git a/src/core/hle/kernel/k_address_arbiter.h b/src/core/hle/kernel/k_address_arbiter.h new file mode 100644 index 000000000..8d379b524 --- /dev/null +++ b/src/core/hle/kernel/k_address_arbiter.h @@ -0,0 +1,70 @@ +// Copyright 2021 yuzu Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include "common/assert.h" +#include "common/common_types.h" +#include "core/hle/kernel/k_condition_variable.h" +#include "core/hle/kernel/svc_types.h" + +union ResultCode; + +namespace Core { +class System; +} + +namespace Kernel { + +class KernelCore; + +class KAddressArbiter { +public: + using ThreadTree = KConditionVariable::ThreadTree; + + explicit KAddressArbiter(Core::System& system_); + ~KAddressArbiter(); + + [[nodiscard]] ResultCode SignalToAddress(VAddr addr, Svc::SignalType type, s32 value, + s32 count) { + switch (type) { + case Svc::SignalType::Signal: + return Signal(addr, count); + case Svc::SignalType::SignalAndIncrementIfEqual: + return SignalAndIncrementIfEqual(addr, value, count); + case Svc::SignalType::SignalAndModifyByWaitingCountIfEqual: + return SignalAndModifyByWaitingCountIfEqual(addr, value, count); + } + UNREACHABLE(); + return RESULT_UNKNOWN; + } + + [[nodiscard]] ResultCode WaitForAddress(VAddr addr, Svc::ArbitrationType type, s32 value, + s64 timeout) { + switch (type) { + case Svc::ArbitrationType::WaitIfLessThan: + return WaitIfLessThan(addr, value, false, timeout); + case Svc::ArbitrationType::DecrementAndWaitIfLessThan: + return WaitIfLessThan(addr, value, true, timeout); + case Svc::ArbitrationType::WaitIfEqual: + return WaitIfEqual(addr, value, timeout); + } + UNREACHABLE(); + return RESULT_UNKNOWN; + } + +private: + [[nodiscard]] ResultCode Signal(VAddr addr, s32 count); + [[nodiscard]] ResultCode SignalAndIncrementIfEqual(VAddr addr, s32 value, s32 count); + [[nodiscard]] ResultCode SignalAndModifyByWaitingCountIfEqual(VAddr addr, s32 value, s32 count); + [[nodiscard]] ResultCode WaitIfLessThan(VAddr addr, s32 value, bool decrement, s64 timeout); + [[nodiscard]] ResultCode WaitIfEqual(VAddr addr, s32 value, s64 timeout); + + ThreadTree thread_tree; + + Core::System& system; + KernelCore& kernel; +}; + +} // namespace Kernel diff --git a/src/core/hle/kernel/k_condition_variable.cpp b/src/core/hle/kernel/k_condition_variable.cpp new file mode 100644 index 000000000..49a068310 --- /dev/null +++ b/src/core/hle/kernel/k_condition_variable.cpp @@ -0,0 +1,349 @@ +// Copyright 2021 yuzu Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include <vector> + +#include "core/arm/exclusive_monitor.h" +#include "core/core.h" +#include "core/hle/kernel/k_condition_variable.h" +#include "core/hle/kernel/k_scheduler.h" +#include "core/hle/kernel/k_scoped_scheduler_lock_and_sleep.h" +#include "core/hle/kernel/k_synchronization_object.h" +#include "core/hle/kernel/kernel.h" +#include "core/hle/kernel/process.h" +#include "core/hle/kernel/svc_common.h" +#include "core/hle/kernel/svc_results.h" +#include "core/hle/kernel/thread.h" +#include "core/memory.h" + +namespace Kernel { + +namespace { + +bool ReadFromUser(Core::System& system, u32* out, VAddr address) { + *out = system.Memory().Read32(address); + return true; +} + +bool WriteToUser(Core::System& system, VAddr address, const u32* p) { + system.Memory().Write32(address, *p); + return true; +} + +bool UpdateLockAtomic(Core::System& system, u32* out, VAddr address, u32 if_zero, + u32 new_orr_mask) { + auto& monitor = system.Monitor(); + const auto current_core = system.CurrentCoreIndex(); + + // Load the value from the address. + const auto expected = monitor.ExclusiveRead32(current_core, address); + + // Orr in the new mask. + u32 value = expected | new_orr_mask; + + // If the value is zero, use the if_zero value, otherwise use the newly orr'd value. + if (!expected) { + value = if_zero; + } + + // Try to store. + if (!monitor.ExclusiveWrite32(current_core, address, value)) { + // If we failed to store, try again. + return UpdateLockAtomic(system, out, address, if_zero, new_orr_mask); + } + + // We're done. + *out = expected; + return true; +} + +} // namespace + +KConditionVariable::KConditionVariable(Core::System& system_) + : system{system_}, kernel{system.Kernel()} {} + +KConditionVariable::~KConditionVariable() = default; + +ResultCode KConditionVariable::SignalToAddress(VAddr addr) { + Thread* owner_thread = kernel.CurrentScheduler()->GetCurrentThread(); + + // Signal the address. + { + KScopedSchedulerLock sl(kernel); + + // Remove waiter thread. + s32 num_waiters{}; + Thread* next_owner_thread = + owner_thread->RemoveWaiterByKey(std::addressof(num_waiters), addr); + + // Determine the next tag. + u32 next_value{}; + if (next_owner_thread) { + next_value = next_owner_thread->GetAddressKeyValue(); + if (num_waiters > 1) { + next_value |= Svc::HandleWaitMask; + } + + next_owner_thread->SetSyncedObject(nullptr, RESULT_SUCCESS); + next_owner_thread->Wakeup(); + } + + // Write the value to userspace. + if (!WriteToUser(system, addr, std::addressof(next_value))) { + if (next_owner_thread) { + next_owner_thread->SetSyncedObject(nullptr, Svc::ResultInvalidCurrentMemory); + } + + return Svc::ResultInvalidCurrentMemory; + } + } + + return RESULT_SUCCESS; +} + +ResultCode KConditionVariable::WaitForAddress(Handle handle, VAddr addr, u32 value) { + Thread* cur_thread = kernel.CurrentScheduler()->GetCurrentThread(); + + // Wait for the address. + { + std::shared_ptr<Thread> owner_thread; + ASSERT(!owner_thread); + { + KScopedSchedulerLock sl(kernel); + cur_thread->SetSyncedObject(nullptr, RESULT_SUCCESS); + + // Check if the thread should terminate. + R_UNLESS(!cur_thread->IsTerminationRequested(), Svc::ResultTerminationRequested); + + { + // Read the tag from userspace. + u32 test_tag{}; + R_UNLESS(ReadFromUser(system, std::addressof(test_tag), addr), + Svc::ResultInvalidCurrentMemory); + + // If the tag isn't the handle (with wait mask), we're done. + R_UNLESS(test_tag == (handle | Svc::HandleWaitMask), RESULT_SUCCESS); + + // Get the lock owner thread. + owner_thread = kernel.CurrentProcess()->GetHandleTable().Get<Thread>(handle); + R_UNLESS(owner_thread, Svc::ResultInvalidHandle); + + // Update the lock. + cur_thread->SetAddressKey(addr, value); + owner_thread->AddWaiter(cur_thread); + cur_thread->SetState(ThreadState::Waiting); + cur_thread->SetWaitReasonForDebugging(ThreadWaitReasonForDebugging::ConditionVar); + cur_thread->SetMutexWaitAddressForDebugging(addr); + } + } + ASSERT(owner_thread); + } + + // Remove the thread as a waiter from the lock owner. + { + KScopedSchedulerLock sl(kernel); + Thread* owner_thread = cur_thread->GetLockOwner(); + if (owner_thread != nullptr) { + owner_thread->RemoveWaiter(cur_thread); + } + } + + // Get the wait result. + KSynchronizationObject* dummy{}; + return cur_thread->GetWaitResult(std::addressof(dummy)); +} + +Thread* KConditionVariable::SignalImpl(Thread* thread) { + // Check pre-conditions. + ASSERT(kernel.GlobalSchedulerContext().IsLocked()); + + // Update the tag. + VAddr address = thread->GetAddressKey(); + u32 own_tag = thread->GetAddressKeyValue(); + + u32 prev_tag{}; + bool can_access{}; + { + // TODO(bunnei): We should disable interrupts here via KScopedInterruptDisable. + // TODO(bunnei): We should call CanAccessAtomic(..) here. + can_access = true; + if (can_access) { + UpdateLockAtomic(system, std::addressof(prev_tag), address, own_tag, + Svc::HandleWaitMask); + } + } + + Thread* thread_to_close = nullptr; + if (can_access) { + if (prev_tag == InvalidHandle) { + // If nobody held the lock previously, we're all good. + thread->SetSyncedObject(nullptr, RESULT_SUCCESS); + thread->Wakeup(); + } else { + // Get the previous owner. + auto owner_thread = kernel.CurrentProcess()->GetHandleTable().Get<Thread>( + prev_tag & ~Svc::HandleWaitMask); + + if (owner_thread) { + // Add the thread as a waiter on the owner. + owner_thread->AddWaiter(thread); + thread_to_close = owner_thread.get(); + } else { + // The lock was tagged with a thread that doesn't exist. + thread->SetSyncedObject(nullptr, Svc::ResultInvalidState); + thread->Wakeup(); + } + } + } else { + // If the address wasn't accessible, note so. + thread->SetSyncedObject(nullptr, Svc::ResultInvalidCurrentMemory); + thread->Wakeup(); + } + + return thread_to_close; +} + +void KConditionVariable::Signal(u64 cv_key, s32 count) { + // Prepare for signaling. + constexpr int MaxThreads = 16; + + // TODO(bunnei): This should just be Thread once we implement KAutoObject instead of using + // std::shared_ptr. + std::vector<std::shared_ptr<Thread>> thread_list; + std::array<Thread*, MaxThreads> thread_array; + s32 num_to_close{}; + + // Perform signaling. + s32 num_waiters{}; + { + KScopedSchedulerLock sl(kernel); + + auto it = thread_tree.nfind_light({cv_key, -1}); + while ((it != thread_tree.end()) && (count <= 0 || num_waiters < count) && + (it->GetConditionVariableKey() == cv_key)) { + Thread* target_thread = std::addressof(*it); + + if (Thread* thread = SignalImpl(target_thread); thread != nullptr) { + if (num_to_close < MaxThreads) { + thread_array[num_to_close++] = thread; + } else { + thread_list.push_back(SharedFrom(thread)); + } + } + + it = thread_tree.erase(it); + target_thread->ClearConditionVariable(); + ++num_waiters; + } + + // If we have no waiters, clear the has waiter flag. + if (it == thread_tree.end() || it->GetConditionVariableKey() != cv_key) { + const u32 has_waiter_flag{}; + WriteToUser(system, cv_key, std::addressof(has_waiter_flag)); + } + } + + // Close threads in the array. + for (auto i = 0; i < num_to_close; ++i) { + thread_array[i]->Close(); + } + + // Close threads in the list. + for (auto it = thread_list.begin(); it != thread_list.end(); it = thread_list.erase(it)) { + (*it)->Close(); + } +} + +ResultCode KConditionVariable::Wait(VAddr addr, u64 key, u32 value, s64 timeout) { + // Prepare to wait. + Thread* cur_thread = kernel.CurrentScheduler()->GetCurrentThread(); + Handle timer = InvalidHandle; + + { + KScopedSchedulerLockAndSleep slp(kernel, timer, cur_thread, timeout); + + // Set the synced object. + cur_thread->SetSyncedObject(nullptr, Svc::ResultTimedOut); + + // Check that the thread isn't terminating. + if (cur_thread->IsTerminationRequested()) { + slp.CancelSleep(); + return Svc::ResultTerminationRequested; + } + + // Update the value and process for the next owner. + { + // Remove waiter thread. + s32 num_waiters{}; + Thread* next_owner_thread = + cur_thread->RemoveWaiterByKey(std::addressof(num_waiters), addr); + + // Update for the next owner thread. + u32 next_value{}; + if (next_owner_thread != nullptr) { + // Get the next tag value. + next_value = next_owner_thread->GetAddressKeyValue(); + if (num_waiters > 1) { + next_value |= Svc::HandleWaitMask; + } + + // Wake up the next owner. + next_owner_thread->SetSyncedObject(nullptr, RESULT_SUCCESS); + next_owner_thread->Wakeup(); + } + + // Write to the cv key. + { + const u32 has_waiter_flag = 1; + WriteToUser(system, key, std::addressof(has_waiter_flag)); + // TODO(bunnei): We should call DataMemoryBarrier(..) here. + } + + // Write the value to userspace. + if (!WriteToUser(system, addr, std::addressof(next_value))) { + slp.CancelSleep(); + return Svc::ResultInvalidCurrentMemory; + } + } + + // Update condition variable tracking. + { + cur_thread->SetConditionVariable(std::addressof(thread_tree), addr, key, value); + thread_tree.insert(*cur_thread); + } + + // If the timeout is non-zero, set the thread as waiting. + if (timeout != 0) { + cur_thread->SetState(ThreadState::Waiting); + cur_thread->SetWaitReasonForDebugging(ThreadWaitReasonForDebugging::ConditionVar); + cur_thread->SetMutexWaitAddressForDebugging(addr); + } + } + + // Cancel the timer wait. + if (timer != InvalidHandle) { + auto& time_manager = kernel.TimeManager(); + time_manager.UnscheduleTimeEvent(timer); + } + + // Remove from the condition variable. + { + KScopedSchedulerLock sl(kernel); + + if (Thread* owner = cur_thread->GetLockOwner(); owner != nullptr) { + owner->RemoveWaiter(cur_thread); + } + + if (cur_thread->IsWaitingForConditionVariable()) { + thread_tree.erase(thread_tree.iterator_to(*cur_thread)); + cur_thread->ClearConditionVariable(); + } + } + + // Get the result. + KSynchronizationObject* dummy{}; + return cur_thread->GetWaitResult(std::addressof(dummy)); +} + +} // namespace Kernel diff --git a/src/core/hle/kernel/k_condition_variable.h b/src/core/hle/kernel/k_condition_variable.h new file mode 100644 index 000000000..98ed5b323 --- /dev/null +++ b/src/core/hle/kernel/k_condition_variable.h @@ -0,0 +1,59 @@ +// Copyright 2021 yuzu Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include "common/assert.h" +#include "common/common_types.h" + +#include "core/hle/kernel/k_scheduler.h" +#include "core/hle/kernel/kernel.h" +#include "core/hle/kernel/thread.h" +#include "core/hle/result.h" + +namespace Core { +class System; +} + +namespace Kernel { + +class KConditionVariable { +public: + using ThreadTree = typename Thread::ConditionVariableThreadTreeType; + + explicit KConditionVariable(Core::System& system_); + ~KConditionVariable(); + + // Arbitration + [[nodiscard]] ResultCode SignalToAddress(VAddr addr); + [[nodiscard]] ResultCode WaitForAddress(Handle handle, VAddr addr, u32 value); + + // Condition variable + void Signal(u64 cv_key, s32 count); + [[nodiscard]] ResultCode Wait(VAddr addr, u64 key, u32 value, s64 timeout); + +private: + [[nodiscard]] Thread* SignalImpl(Thread* thread); + + ThreadTree thread_tree; + + Core::System& system; + KernelCore& kernel; +}; + +inline void BeforeUpdatePriority(const KernelCore& kernel, KConditionVariable::ThreadTree* tree, + Thread* thread) { + ASSERT(kernel.GlobalSchedulerContext().IsLocked()); + + tree->erase(tree->iterator_to(*thread)); +} + +inline void AfterUpdatePriority(const KernelCore& kernel, KConditionVariable::ThreadTree* tree, + Thread* thread) { + ASSERT(kernel.GlobalSchedulerContext().IsLocked()); + + tree->insert(*thread); +} + +} // namespace Kernel diff --git a/src/core/hle/kernel/k_scheduler.cpp b/src/core/hle/kernel/k_scheduler.cpp index c5fd82a6b..42f0ea483 100644 --- a/src/core/hle/kernel/k_scheduler.cpp +++ b/src/core/hle/kernel/k_scheduler.cpp @@ -180,22 +180,22 @@ u64 KScheduler::UpdateHighestPriorityThreadsImpl(KernelCore& kernel) { return cores_needing_scheduling; } -void KScheduler::OnThreadStateChanged(KernelCore& kernel, Thread* thread, u32 old_state) { +void KScheduler::OnThreadStateChanged(KernelCore& kernel, Thread* thread, ThreadState old_state) { ASSERT(kernel.GlobalSchedulerContext().IsLocked()); // Check if the state has changed, because if it hasn't there's nothing to do. - const auto cur_state = thread->scheduling_state; + const auto cur_state = thread->GetRawState(); if (cur_state == old_state) { return; } // Update the priority queues. - if (old_state == static_cast<u32>(ThreadSchedStatus::Runnable)) { + if (old_state == ThreadState::Runnable) { // If we were previously runnable, then we're not runnable now, and we should remove. GetPriorityQueue(kernel).Remove(thread); IncrementScheduledCount(thread); SetSchedulerUpdateNeeded(kernel); - } else if (cur_state == static_cast<u32>(ThreadSchedStatus::Runnable)) { + } else if (cur_state == ThreadState::Runnable) { // If we're now runnable, then we weren't previously, and we should add. GetPriorityQueue(kernel).PushBack(thread); IncrementScheduledCount(thread); @@ -203,13 +203,11 @@ void KScheduler::OnThreadStateChanged(KernelCore& kernel, Thread* thread, u32 ol } } -void KScheduler::OnThreadPriorityChanged(KernelCore& kernel, Thread* thread, Thread* current_thread, - u32 old_priority) { - +void KScheduler::OnThreadPriorityChanged(KernelCore& kernel, Thread* thread, s32 old_priority) { ASSERT(kernel.GlobalSchedulerContext().IsLocked()); // If the thread is runnable, we want to change its priority in the queue. - if (thread->scheduling_state == static_cast<u32>(ThreadSchedStatus::Runnable)) { + if (thread->GetRawState() == ThreadState::Runnable) { GetPriorityQueue(kernel).ChangePriority( old_priority, thread == kernel.CurrentScheduler()->GetCurrentThread(), thread); IncrementScheduledCount(thread); @@ -222,7 +220,7 @@ void KScheduler::OnThreadAffinityMaskChanged(KernelCore& kernel, Thread* thread, ASSERT(kernel.GlobalSchedulerContext().IsLocked()); // If the thread is runnable, we want to change its affinity in the queue. - if (thread->scheduling_state == static_cast<u32>(ThreadSchedStatus::Runnable)) { + if (thread->GetRawState() == ThreadState::Runnable) { GetPriorityQueue(kernel).ChangeAffinityMask(old_core, old_affinity, thread); IncrementScheduledCount(thread); SetSchedulerUpdateNeeded(kernel); @@ -292,7 +290,7 @@ void KScheduler::RotateScheduledQueue(s32 core_id, s32 priority) { // If the best thread we can choose has a priority the same or worse than ours, try to // migrate a higher priority thread. - if (best_thread != nullptr && best_thread->GetPriority() >= static_cast<u32>(priority)) { + if (best_thread != nullptr && best_thread->GetPriority() >= priority) { Thread* suggested = priority_queue.GetSuggestedFront(core_id); while (suggested != nullptr) { // If the suggestion's priority is the same as ours, don't bother. @@ -395,8 +393,8 @@ void KScheduler::YieldWithoutCoreMigration() { { KScopedSchedulerLock lock(kernel); - const auto cur_state = cur_thread.scheduling_state; - if (cur_state == static_cast<u32>(ThreadSchedStatus::Runnable)) { + const auto cur_state = cur_thread.GetRawState(); + if (cur_state == ThreadState::Runnable) { // Put the current thread at the back of the queue. Thread* next_thread = priority_queue.MoveToScheduledBack(std::addressof(cur_thread)); IncrementScheduledCount(std::addressof(cur_thread)); @@ -436,8 +434,8 @@ void KScheduler::YieldWithCoreMigration() { { KScopedSchedulerLock lock(kernel); - const auto cur_state = cur_thread.scheduling_state; - if (cur_state == static_cast<u32>(ThreadSchedStatus::Runnable)) { + const auto cur_state = cur_thread.GetRawState(); + if (cur_state == ThreadState::Runnable) { // Get the current active core. const s32 core_id = cur_thread.GetActiveCore(); @@ -526,8 +524,8 @@ void KScheduler::YieldToAnyThread() { { KScopedSchedulerLock lock(kernel); - const auto cur_state = cur_thread.scheduling_state; - if (cur_state == static_cast<u32>(ThreadSchedStatus::Runnable)) { + const auto cur_state = cur_thread.GetRawState(); + if (cur_state == ThreadState::Runnable) { // Get the current active core. const s32 core_id = cur_thread.GetActiveCore(); @@ -645,8 +643,7 @@ void KScheduler::Unload(Thread* thread) { void KScheduler::Reload(Thread* thread) { if (thread) { - ASSERT_MSG(thread->GetSchedulingStatus() == ThreadSchedStatus::Runnable, - "Thread must be runnable."); + ASSERT_MSG(thread->GetState() == ThreadState::Runnable, "Thread must be runnable."); // Cancel any outstanding wakeup events for this thread thread->SetIsRunning(true); @@ -725,7 +722,7 @@ void KScheduler::SwitchToCurrent() { do { if (current_thread != nullptr && !current_thread->IsHLEThread()) { current_thread->context_guard.lock(); - if (!current_thread->IsRunnable()) { + if (current_thread->GetRawState() != ThreadState::Runnable) { current_thread->context_guard.unlock(); break; } @@ -772,7 +769,7 @@ void KScheduler::Initialize() { { KScopedSchedulerLock lock{system.Kernel()}; - idle_thread->SetStatus(ThreadStatus::Ready); + idle_thread->SetState(ThreadState::Runnable); } } diff --git a/src/core/hle/kernel/k_scheduler.h b/src/core/hle/kernel/k_scheduler.h index e84abc84c..783665123 100644 --- a/src/core/hle/kernel/k_scheduler.h +++ b/src/core/hle/kernel/k_scheduler.h @@ -100,11 +100,10 @@ public: void YieldToAnyThread(); /// Notify the scheduler a thread's status has changed. - static void OnThreadStateChanged(KernelCore& kernel, Thread* thread, u32 old_state); + static void OnThreadStateChanged(KernelCore& kernel, Thread* thread, ThreadState old_state); /// Notify the scheduler a thread's priority has changed. - static void OnThreadPriorityChanged(KernelCore& kernel, Thread* thread, Thread* current_thread, - u32 old_priority); + static void OnThreadPriorityChanged(KernelCore& kernel, Thread* thread, s32 old_priority); /// Notify the scheduler a thread's core and/or affinity mask has changed. static void OnThreadAffinityMaskChanged(KernelCore& kernel, Thread* thread, diff --git a/src/core/hle/kernel/k_scheduler_lock.h b/src/core/hle/kernel/k_scheduler_lock.h index 2f1c1f691..9b40bd22c 100644 --- a/src/core/hle/kernel/k_scheduler_lock.h +++ b/src/core/hle/kernel/k_scheduler_lock.h @@ -19,7 +19,7 @@ class KernelCore; template <typename SchedulerType> class KAbstractSchedulerLock { public: - explicit KAbstractSchedulerLock(KernelCore& kernel) : kernel{kernel} {} + explicit KAbstractSchedulerLock(KernelCore& kernel_) : kernel{kernel_} {} bool IsLockedByCurrentThread() const { return this->owner_thread == kernel.GetCurrentEmuThreadID(); diff --git a/src/core/hle/kernel/k_synchronization_object.cpp b/src/core/hle/kernel/k_synchronization_object.cpp new file mode 100644 index 000000000..1c508cb55 --- /dev/null +++ b/src/core/hle/kernel/k_synchronization_object.cpp @@ -0,0 +1,172 @@ +// Copyright 2021 yuzu Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include "common/assert.h" +#include "common/common_types.h" +#include "core/hle/kernel/k_scheduler.h" +#include "core/hle/kernel/k_scoped_scheduler_lock_and_sleep.h" +#include "core/hle/kernel/k_synchronization_object.h" +#include "core/hle/kernel/kernel.h" +#include "core/hle/kernel/svc_results.h" +#include "core/hle/kernel/thread.h" + +namespace Kernel { + +ResultCode KSynchronizationObject::Wait(KernelCore& kernel, s32* out_index, + KSynchronizationObject** objects, const s32 num_objects, + s64 timeout) { + // Allocate space on stack for thread nodes. + std::vector<ThreadListNode> thread_nodes(num_objects); + + // Prepare for wait. + Thread* thread = kernel.CurrentScheduler()->GetCurrentThread(); + Handle timer = InvalidHandle; + + { + // Setup the scheduling lock and sleep. + KScopedSchedulerLockAndSleep slp(kernel, timer, thread, timeout); + + // Check if any of the objects are already signaled. + for (auto i = 0; i < num_objects; ++i) { + ASSERT(objects[i] != nullptr); + + if (objects[i]->IsSignaled()) { + *out_index = i; + slp.CancelSleep(); + return RESULT_SUCCESS; + } + } + + // Check if the timeout is zero. + if (timeout == 0) { + slp.CancelSleep(); + return Svc::ResultTimedOut; + } + + // Check if the thread should terminate. + if (thread->IsTerminationRequested()) { + slp.CancelSleep(); + return Svc::ResultTerminationRequested; + } + + // Check if waiting was canceled. + if (thread->IsWaitCancelled()) { + slp.CancelSleep(); + thread->ClearWaitCancelled(); + return Svc::ResultCancelled; + } + + // Add the waiters. + for (auto i = 0; i < num_objects; ++i) { + thread_nodes[i].thread = thread; + thread_nodes[i].next = nullptr; + + if (objects[i]->thread_list_tail == nullptr) { + objects[i]->thread_list_head = std::addressof(thread_nodes[i]); + } else { + objects[i]->thread_list_tail->next = std::addressof(thread_nodes[i]); + } + + objects[i]->thread_list_tail = std::addressof(thread_nodes[i]); + } + + // For debugging only + thread->SetWaitObjectsForDebugging({objects, static_cast<std::size_t>(num_objects)}); + + // Mark the thread as waiting. + thread->SetCancellable(); + thread->SetSyncedObject(nullptr, Svc::ResultTimedOut); + thread->SetState(ThreadState::Waiting); + thread->SetWaitReasonForDebugging(ThreadWaitReasonForDebugging::Synchronization); + } + + // The lock/sleep is done, so we should be able to get our result. + + // Thread is no longer cancellable. + thread->ClearCancellable(); + + // For debugging only + thread->SetWaitObjectsForDebugging({}); + + // Cancel the timer as needed. + if (timer != InvalidHandle) { + auto& time_manager = kernel.TimeManager(); + time_manager.UnscheduleTimeEvent(timer); + } + + // Get the wait result. + ResultCode wait_result{RESULT_SUCCESS}; + s32 sync_index = -1; + { + KScopedSchedulerLock lock(kernel); + KSynchronizationObject* synced_obj; + wait_result = thread->GetWaitResult(std::addressof(synced_obj)); + + for (auto i = 0; i < num_objects; ++i) { + // Unlink the object from the list. + ThreadListNode* prev_ptr = + reinterpret_cast<ThreadListNode*>(std::addressof(objects[i]->thread_list_head)); + ThreadListNode* prev_val = nullptr; + ThreadListNode *prev, *tail_prev; + + do { + prev = prev_ptr; + prev_ptr = prev_ptr->next; + tail_prev = prev_val; + prev_val = prev_ptr; + } while (prev_ptr != std::addressof(thread_nodes[i])); + + if (objects[i]->thread_list_tail == std::addressof(thread_nodes[i])) { + objects[i]->thread_list_tail = tail_prev; + } + + prev->next = thread_nodes[i].next; + + if (objects[i] == synced_obj) { + sync_index = i; + } + } + } + + // Set output. + *out_index = sync_index; + return wait_result; +} + +KSynchronizationObject::KSynchronizationObject(KernelCore& kernel) : Object{kernel} {} + +KSynchronizationObject ::~KSynchronizationObject() = default; + +void KSynchronizationObject::NotifyAvailable(ResultCode result) { + KScopedSchedulerLock lock(kernel); + + // If we're not signaled, we've nothing to notify. + if (!this->IsSignaled()) { + return; + } + + // Iterate over each thread. + for (auto* cur_node = thread_list_head; cur_node != nullptr; cur_node = cur_node->next) { + Thread* thread = cur_node->thread; + if (thread->GetState() == ThreadState::Waiting) { + thread->SetSyncedObject(this, result); + thread->SetState(ThreadState::Runnable); + } + } +} + +std::vector<Thread*> KSynchronizationObject::GetWaitingThreadsForDebugging() const { + std::vector<Thread*> threads; + + // If debugging, dump the list of waiters. + { + KScopedSchedulerLock lock(kernel); + for (auto* cur_node = thread_list_head; cur_node != nullptr; cur_node = cur_node->next) { + threads.emplace_back(cur_node->thread); + } + } + + return threads; +} +} // namespace Kernel diff --git a/src/core/hle/kernel/k_synchronization_object.h b/src/core/hle/kernel/k_synchronization_object.h new file mode 100644 index 000000000..14d80ebf1 --- /dev/null +++ b/src/core/hle/kernel/k_synchronization_object.h @@ -0,0 +1,58 @@ +// Copyright 2021 yuzu Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include <vector> + +#include "core/hle/kernel/object.h" +#include "core/hle/result.h" + +namespace Kernel { + +class KernelCore; +class Synchronization; +class Thread; + +/// Class that represents a Kernel object that a thread can be waiting on +class KSynchronizationObject : public Object { +public: + struct ThreadListNode { + ThreadListNode* next{}; + Thread* thread{}; + }; + + [[nodiscard]] static ResultCode Wait(KernelCore& kernel, s32* out_index, + KSynchronizationObject** objects, const s32 num_objects, + s64 timeout); + + [[nodiscard]] virtual bool IsSignaled() const = 0; + + [[nodiscard]] std::vector<Thread*> GetWaitingThreadsForDebugging() const; + +protected: + explicit KSynchronizationObject(KernelCore& kernel); + virtual ~KSynchronizationObject(); + + void NotifyAvailable(ResultCode result); + void NotifyAvailable() { + return this->NotifyAvailable(RESULT_SUCCESS); + } + +private: + ThreadListNode* thread_list_head{}; + ThreadListNode* thread_list_tail{}; +}; + +// Specialization of DynamicObjectCast for KSynchronizationObjects +template <> +inline std::shared_ptr<KSynchronizationObject> DynamicObjectCast<KSynchronizationObject>( + std::shared_ptr<Object> object) { + if (object != nullptr && object->IsWaitable()) { + return std::static_pointer_cast<KSynchronizationObject>(object); + } + return nullptr; +} + +} // namespace Kernel diff --git a/src/core/hle/kernel/kernel.cpp b/src/core/hle/kernel/kernel.cpp index e8ece8164..c0ff287a6 100644 --- a/src/core/hle/kernel/kernel.cpp +++ b/src/core/hle/kernel/kernel.cpp @@ -38,7 +38,6 @@ #include "core/hle/kernel/resource_limit.h" #include "core/hle/kernel/service_thread.h" #include "core/hle/kernel/shared_memory.h" -#include "core/hle/kernel/synchronization.h" #include "core/hle/kernel/thread.h" #include "core/hle/kernel/time_manager.h" #include "core/hle/lock.h" @@ -51,8 +50,7 @@ namespace Kernel { struct KernelCore::Impl { explicit Impl(Core::System& system, KernelCore& kernel) - : synchronization{system}, time_manager{system}, global_handle_table{kernel}, system{ - system} {} + : time_manager{system}, global_handle_table{kernel}, system{system} {} void SetMulticore(bool is_multicore) { this->is_multicore = is_multicore; @@ -307,7 +305,6 @@ struct KernelCore::Impl { std::vector<std::shared_ptr<Process>> process_list; Process* current_process = nullptr; std::unique_ptr<Kernel::GlobalSchedulerContext> global_scheduler_context; - Kernel::Synchronization synchronization; Kernel::TimeManager time_manager; std::shared_ptr<ResourceLimit> system_resource_limit; @@ -461,14 +458,6 @@ const std::array<Core::CPUInterruptHandler, Core::Hardware::NUM_CPU_CORES>& Kern return impl->interrupts; } -Kernel::Synchronization& KernelCore::Synchronization() { - return impl->synchronization; -} - -const Kernel::Synchronization& KernelCore::Synchronization() const { - return impl->synchronization; -} - Kernel::TimeManager& KernelCore::TimeManager() { return impl->time_manager; } @@ -613,9 +602,11 @@ void KernelCore::Suspend(bool in_suspention) { const bool should_suspend = exception_exited || in_suspention; { KScopedSchedulerLock lock(*this); - ThreadStatus status = should_suspend ? ThreadStatus::Ready : ThreadStatus::WaitSleep; + const auto state = should_suspend ? ThreadState::Runnable : ThreadState::Waiting; for (std::size_t i = 0; i < Core::Hardware::NUM_CPU_CORES; i++) { - impl->suspend_threads[i]->SetStatus(status); + impl->suspend_threads[i]->SetState(state); + impl->suspend_threads[i]->SetWaitReasonForDebugging( + ThreadWaitReasonForDebugging::Suspended); } } } diff --git a/src/core/hle/kernel/kernel.h b/src/core/hle/kernel/kernel.h index e3169f5a7..933d9a7d6 100644 --- a/src/core/hle/kernel/kernel.h +++ b/src/core/hle/kernel/kernel.h @@ -33,7 +33,6 @@ template <typename T> class SlabHeap; } // namespace Memory -class AddressArbiter; class ClientPort; class GlobalSchedulerContext; class HandleTable; @@ -129,12 +128,6 @@ public: /// Gets the an instance of the current physical CPU core. const Kernel::PhysicalCore& CurrentPhysicalCore() const; - /// Gets the an instance of the Synchronization Interface. - Kernel::Synchronization& Synchronization(); - - /// Gets the an instance of the Synchronization Interface. - const Kernel::Synchronization& Synchronization() const; - /// Gets the an instance of the TimeManager Interface. Kernel::TimeManager& TimeManager(); diff --git a/src/core/hle/kernel/memory/memory_layout.h b/src/core/hle/kernel/memory/memory_layout.h index 9b3d6267a..c7c0b2f49 100644 --- a/src/core/hle/kernel/memory/memory_layout.h +++ b/src/core/hle/kernel/memory/memory_layout.h @@ -5,9 +5,28 @@ #pragma once #include "common/common_types.h" +#include "core/device_memory.h" namespace Kernel::Memory { +constexpr std::size_t KernelAslrAlignment = 2 * 1024 * 1024; +constexpr std::size_t KernelVirtualAddressSpaceWidth = 1ULL << 39; +constexpr std::size_t KernelPhysicalAddressSpaceWidth = 1ULL << 48; +constexpr std::size_t KernelVirtualAddressSpaceBase = 0ULL - KernelVirtualAddressSpaceWidth; +constexpr std::size_t KernelVirtualAddressSpaceEnd = + KernelVirtualAddressSpaceBase + (KernelVirtualAddressSpaceWidth - KernelAslrAlignment); +constexpr std::size_t KernelVirtualAddressSpaceLast = KernelVirtualAddressSpaceEnd - 1; +constexpr std::size_t KernelVirtualAddressSpaceSize = + KernelVirtualAddressSpaceEnd - KernelVirtualAddressSpaceBase; + +constexpr bool IsKernelAddressKey(VAddr key) { + return KernelVirtualAddressSpaceBase <= key && key <= KernelVirtualAddressSpaceLast; +} + +constexpr bool IsKernelAddress(VAddr address) { + return KernelVirtualAddressSpaceBase <= address && address < KernelVirtualAddressSpaceEnd; +} + class MemoryRegion final { friend class MemoryLayout; diff --git a/src/core/hle/kernel/mutex.cpp b/src/core/hle/kernel/mutex.cpp deleted file mode 100644 index 4f8075e0e..000000000 --- a/src/core/hle/kernel/mutex.cpp +++ /dev/null @@ -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->SetStatus(ThreadStatus::WaitMutex); - - // 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->ResumeFromWait(); - - 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 diff --git a/src/core/hle/kernel/mutex.h b/src/core/hle/kernel/mutex.h deleted file mode 100644 index 3b81dc3df..000000000 --- a/src/core/hle/kernel/mutex.h +++ /dev/null @@ -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 diff --git a/src/core/hle/kernel/object.h b/src/core/hle/kernel/object.h index e3391e2af..27124ef67 100644 --- a/src/core/hle/kernel/object.h +++ b/src/core/hle/kernel/object.h @@ -50,6 +50,11 @@ public: } virtual HandleType GetHandleType() const = 0; + void Close() { + // TODO(bunnei): This is a placeholder to decrement the reference count, which we will use + // when we implement KAutoObject instead of using shared_ptr. + } + /** * Check if a thread can wait on the object * @return True if a thread can wait on the object, otherwise false diff --git a/src/core/hle/kernel/process.cpp b/src/core/hle/kernel/process.cpp index b905b486a..37b77fa6e 100644 --- a/src/core/hle/kernel/process.cpp +++ b/src/core/hle/kernel/process.cpp @@ -55,7 +55,7 @@ void SetupMainThread(Core::System& system, Process& owner_process, u32 priority, // Threads by default are dormant, wake up the main thread so it runs when the scheduler fires { KScopedSchedulerLock lock{kernel}; - thread->SetStatus(ThreadStatus::Ready); + thread->SetState(ThreadState::Runnable); } } } // Anonymous namespace @@ -162,48 +162,6 @@ u64 Process::GetTotalPhysicalMemoryUsedWithoutSystemResource() const { 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) { thread_list.push_back(thread); } @@ -318,7 +276,7 @@ void Process::PrepareForTermination() { continue; // TODO(Subv): When are the other running/ready threads terminated? - ASSERT_MSG(thread->GetStatus() == ThreadStatus::WaitSynch, + ASSERT_MSG(thread->GetState() == ThreadState::Waiting, "Exiting processes with non-waiting threads is currently unimplemented"); thread->Stop(); @@ -406,21 +364,18 @@ void Process::LoadModule(CodeSet code_set, VAddr base_addr) { ReprotectSegment(code_set.DataSegment(), Memory::MemoryPermission::ReadAndWrite); } +bool Process::IsSignaled() const { + ASSERT(kernel.GlobalSchedulerContext().IsLocked()); + return is_signaled; +} + Process::Process(Core::System& system) - : SynchronizationObject{system.Kernel()}, page_table{std::make_unique<Memory::PageTable>( - system)}, - handle_table{system.Kernel()}, address_arbiter{system}, mutex{system}, system{system} {} + : KSynchronizationObject{system.Kernel()}, + page_table{std::make_unique<Memory::PageTable>(system)}, handle_table{system.Kernel()}, + address_arbiter{system}, condition_var{system}, system{system} {} Process::~Process() = default; -void Process::Acquire(Thread* thread) { - ASSERT_MSG(!ShouldWait(thread), "Object unavailable!"); -} - -bool Process::ShouldWait(const Thread* thread) const { - return !is_signaled; -} - void Process::ChangeStatus(ProcessStatus new_status) { if (status == new_status) { return; @@ -428,7 +383,7 @@ void Process::ChangeStatus(ProcessStatus new_status) { status = new_status; is_signaled = true; - Signal(); + NotifyAvailable(); } ResultCode Process::AllocateMainThreadStack(std::size_t stack_size) { diff --git a/src/core/hle/kernel/process.h b/src/core/hle/kernel/process.h index e412e58aa..564e1f27d 100644 --- a/src/core/hle/kernel/process.h +++ b/src/core/hle/kernel/process.h @@ -11,11 +11,11 @@ #include <unordered_map> #include <vector> #include "common/common_types.h" -#include "core/hle/kernel/address_arbiter.h" #include "core/hle/kernel/handle_table.h" -#include "core/hle/kernel/mutex.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/process_capability.h" -#include "core/hle/kernel/synchronization_object.h" #include "core/hle/result.h" namespace Core { @@ -63,7 +63,7 @@ enum class ProcessStatus { DebugBreak, }; -class Process final : public SynchronizationObject { +class Process final : public KSynchronizationObject { public: explicit Process(Core::System& system); ~Process() override; @@ -123,24 +123,30 @@ public: return handle_table; } - /// Gets a reference to the process' address arbiter. - AddressArbiter& GetAddressArbiter() { - return address_arbiter; + ResultCode SignalToAddress(VAddr address) { + return condition_var.SignalToAddress(address); } - /// Gets a const reference to the process' address arbiter. - const AddressArbiter& GetAddressArbiter() const { - return address_arbiter; + ResultCode WaitForAddress(Handle handle, VAddr address, u32 tag) { + return condition_var.WaitForAddress(handle, address, tag); } - /// Gets a reference to the process' mutex lock. - Mutex& GetMutex() { - return mutex; + void SignalConditionVariable(u64 cv_key, int32_t count) { + return condition_var.Signal(cv_key, count); } - /// Gets a const reference to the process' mutex lock - const Mutex& GetMutex() const { - return mutex; + ResultCode WaitConditionVariable(VAddr address, u64 cv_key, u32 tag, s64 ns) { + return condition_var.Wait(address, cv_key, tag, ns); + } + + 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. @@ -250,15 +256,6 @@ public: 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, /// adding it to this process' thread list. void RegisterThread(const Thread* thread); @@ -304,6 +301,8 @@ public: void LoadModule(CodeSet code_set, VAddr base_addr); + bool IsSignaled() const override; + /////////////////////////////////////////////////////////////////////////////////////////////// // Thread-local storage management @@ -314,12 +313,6 @@ public: void FreeTLSRegion(VAddr tls_address); private: - /// Checks if the specified thread should wait until this process is available. - bool ShouldWait(const Thread* thread) const override; - - /// Acquires/locks this process for the specified thread if it's available. - void Acquire(Thread* thread) override; - /// Changes the process status. If the status is different /// from the current process status, then this will trigger /// a process signal. @@ -373,12 +366,12 @@ private: HandleTable handle_table; /// Per-process address arbiter. - AddressArbiter address_arbiter; + KAddressArbiter address_arbiter; /// The per-process mutex lock instance used for handling various /// forms of services, such as lock arbitration, and condition /// variable related facilities. - Mutex mutex; + KConditionVariable condition_var; /// Address indicating the location of the process' dedicated TLS region. VAddr tls_region_address = 0; @@ -389,9 +382,6 @@ private: /// List of threads that are running with this process as their owner. 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 VAddr main_thread_stack_top{}; @@ -410,6 +400,8 @@ private: /// Schedule count of this process s64 schedule_count{}; + bool is_signaled{}; + /// System context Core::System& system; }; diff --git a/src/core/hle/kernel/readable_event.cpp b/src/core/hle/kernel/readable_event.cpp index cea262ce0..99ed0857e 100644 --- a/src/core/hle/kernel/readable_event.cpp +++ b/src/core/hle/kernel/readable_event.cpp @@ -14,24 +14,22 @@ namespace Kernel { -ReadableEvent::ReadableEvent(KernelCore& kernel) : SynchronizationObject{kernel} {} +ReadableEvent::ReadableEvent(KernelCore& kernel) : KSynchronizationObject{kernel} {} ReadableEvent::~ReadableEvent() = default; -bool ReadableEvent::ShouldWait(const Thread* thread) const { - return !is_signaled; -} - -void ReadableEvent::Acquire(Thread* thread) { - ASSERT_MSG(IsSignaled(), "object unavailable!"); -} - void ReadableEvent::Signal() { if (is_signaled) { return; } is_signaled = true; - SynchronizationObject::Signal(); + NotifyAvailable(); +} + +bool ReadableEvent::IsSignaled() const { + ASSERT(kernel.GlobalSchedulerContext().IsLocked()); + + return is_signaled; } void ReadableEvent::Clear() { diff --git a/src/core/hle/kernel/readable_event.h b/src/core/hle/kernel/readable_event.h index 3264dd066..34e477274 100644 --- a/src/core/hle/kernel/readable_event.h +++ b/src/core/hle/kernel/readable_event.h @@ -4,8 +4,8 @@ #pragma once +#include "core/hle/kernel/k_synchronization_object.h" #include "core/hle/kernel/object.h" -#include "core/hle/kernel/synchronization_object.h" union ResultCode; @@ -14,7 +14,7 @@ namespace Kernel { class KernelCore; class WritableEvent; -class ReadableEvent final : public SynchronizationObject { +class ReadableEvent final : public KSynchronizationObject { friend class WritableEvent; public: @@ -32,9 +32,6 @@ public: return HANDLE_TYPE; } - bool ShouldWait(const Thread* thread) const override; - void Acquire(Thread* thread) override; - /// Unconditionally clears the readable event's state. void Clear(); @@ -46,11 +43,14 @@ public: /// then ERR_INVALID_STATE will be returned. ResultCode Reset(); - void Signal() override; + void Signal(); + + bool IsSignaled() const override; private: explicit ReadableEvent(KernelCore& kernel); + bool is_signaled{}; std::string name; ///< Name of event (optional) }; diff --git a/src/core/hle/kernel/server_port.cpp b/src/core/hle/kernel/server_port.cpp index a549ae9d7..82857f93b 100644 --- a/src/core/hle/kernel/server_port.cpp +++ b/src/core/hle/kernel/server_port.cpp @@ -13,7 +13,7 @@ namespace Kernel { -ServerPort::ServerPort(KernelCore& kernel) : SynchronizationObject{kernel} {} +ServerPort::ServerPort(KernelCore& kernel) : KSynchronizationObject{kernel} {} ServerPort::~ServerPort() = default; ResultVal<std::shared_ptr<ServerSession>> ServerPort::Accept() { @@ -28,15 +28,9 @@ ResultVal<std::shared_ptr<ServerSession>> ServerPort::Accept() { void ServerPort::AppendPendingSession(std::shared_ptr<ServerSession> pending_session) { pending_sessions.push_back(std::move(pending_session)); -} - -bool ServerPort::ShouldWait(const Thread* thread) const { - // If there are no pending sessions, we wait until a new one is added. - return pending_sessions.empty(); -} - -void ServerPort::Acquire(Thread* thread) { - ASSERT_MSG(!ShouldWait(thread), "object unavailable!"); + if (pending_sessions.size() == 1) { + NotifyAvailable(); + } } bool ServerPort::IsSignaled() const { diff --git a/src/core/hle/kernel/server_port.h b/src/core/hle/kernel/server_port.h index 41b191b86..6470df993 100644 --- a/src/core/hle/kernel/server_port.h +++ b/src/core/hle/kernel/server_port.h @@ -9,8 +9,8 @@ #include <utility> #include <vector> #include "common/common_types.h" +#include "core/hle/kernel/k_synchronization_object.h" #include "core/hle/kernel/object.h" -#include "core/hle/kernel/synchronization_object.h" #include "core/hle/result.h" namespace Kernel { @@ -20,7 +20,7 @@ class KernelCore; class ServerSession; class SessionRequestHandler; -class ServerPort final : public SynchronizationObject { +class ServerPort final : public KSynchronizationObject { public: explicit ServerPort(KernelCore& kernel); ~ServerPort() override; @@ -79,9 +79,6 @@ public: /// waiting to be accepted by this port. void AppendPendingSession(std::shared_ptr<ServerSession> pending_session); - bool ShouldWait(const Thread* thread) const override; - void Acquire(Thread* thread) override; - bool IsSignaled() const override; private: diff --git a/src/core/hle/kernel/server_session.cpp b/src/core/hle/kernel/server_session.cpp index b40fe3916..4f2bb7822 100644 --- a/src/core/hle/kernel/server_session.cpp +++ b/src/core/hle/kernel/server_session.cpp @@ -24,7 +24,7 @@ namespace Kernel { -ServerSession::ServerSession(KernelCore& kernel) : SynchronizationObject{kernel} {} +ServerSession::ServerSession(KernelCore& kernel) : KSynchronizationObject{kernel} {} ServerSession::~ServerSession() { kernel.ReleaseServiceThread(service_thread); @@ -42,16 +42,6 @@ ResultVal<std::shared_ptr<ServerSession>> ServerSession::Create(KernelCore& kern return MakeResult(std::move(session)); } -bool ServerSession::ShouldWait(const Thread* thread) const { - // Closed sessions should never wait, an error will be returned from svcReplyAndReceive. - if (!parent->Client()) { - return false; - } - - // Wait if we have no pending requests, or if we're currently handling a request. - return pending_requesting_threads.empty() || currently_handling != nullptr; -} - bool ServerSession::IsSignaled() const { // Closed sessions should never wait, an error will be returned from svcReplyAndReceive. if (!parent->Client()) { @@ -62,15 +52,6 @@ bool ServerSession::IsSignaled() const { return !pending_requesting_threads.empty() && currently_handling == nullptr; } -void ServerSession::Acquire(Thread* thread) { - ASSERT_MSG(!ShouldWait(thread), "object unavailable!"); - // We are now handling a request, pop it from the stack. - // TODO(Subv): What happens if the client endpoint is closed before any requests are made? - ASSERT(!pending_requesting_threads.empty()); - currently_handling = pending_requesting_threads.back(); - pending_requesting_threads.pop_back(); -} - void ServerSession::ClientDisconnected() { // We keep a shared pointer to the hle handler to keep it alive throughout // the call to ClientDisconnected, as ClientDisconnected invalidates the @@ -172,7 +153,7 @@ ResultCode ServerSession::CompleteSyncRequest(HLERequestContext& context) { { KScopedSchedulerLock lock(kernel); if (!context.IsThreadWaiting()) { - context.GetThread().ResumeFromWait(); + context.GetThread().Wakeup(); context.GetThread().SetSynchronizationResults(nullptr, result); } } diff --git a/src/core/hle/kernel/server_session.h b/src/core/hle/kernel/server_session.h index e8d1d99ea..9155cf7f5 100644 --- a/src/core/hle/kernel/server_session.h +++ b/src/core/hle/kernel/server_session.h @@ -10,8 +10,8 @@ #include <vector> #include "common/threadsafe_queue.h" +#include "core/hle/kernel/k_synchronization_object.h" #include "core/hle/kernel/service_thread.h" -#include "core/hle/kernel/synchronization_object.h" #include "core/hle/result.h" namespace Core::Memory { @@ -43,7 +43,7 @@ class Thread; * After the server replies to the request, the response is marshalled back to the caller's * TLS buffer and control is transferred back to it. */ -class ServerSession final : public SynchronizationObject { +class ServerSession final : public KSynchronizationObject { friend class ServiceThread; public: @@ -77,8 +77,6 @@ public: return parent.get(); } - bool IsSignaled() const override; - /** * Sets the HLE handler for the session. This handler will be called to service IPC requests * instead of the regular IPC machinery. (The regular IPC machinery is currently not @@ -100,10 +98,6 @@ public: ResultCode HandleSyncRequest(std::shared_ptr<Thread> thread, Core::Memory::Memory& memory, Core::Timing::CoreTiming& core_timing); - bool ShouldWait(const Thread* thread) const override; - - void Acquire(Thread* thread) override; - /// Called when a client disconnection occurs. void ClientDisconnected(); @@ -130,6 +124,8 @@ public: convert_to_domain = true; } + bool IsSignaled() const override; + private: /// Queues a sync request from the emulated application. ResultCode QueueSyncRequest(std::shared_ptr<Thread> thread, Core::Memory::Memory& memory); diff --git a/src/core/hle/kernel/session.cpp b/src/core/hle/kernel/session.cpp index e4dd53e24..75304b961 100644 --- a/src/core/hle/kernel/session.cpp +++ b/src/core/hle/kernel/session.cpp @@ -9,7 +9,7 @@ namespace Kernel { -Session::Session(KernelCore& kernel) : SynchronizationObject{kernel} {} +Session::Session(KernelCore& kernel) : KSynchronizationObject{kernel} {} Session::~Session() = default; Session::SessionPair Session::Create(KernelCore& kernel, std::string name) { @@ -24,18 +24,9 @@ Session::SessionPair Session::Create(KernelCore& kernel, std::string name) { return std::make_pair(std::move(client_session), std::move(server_session)); } -bool Session::ShouldWait(const Thread* thread) const { - UNIMPLEMENTED(); - return {}; -} - bool Session::IsSignaled() const { UNIMPLEMENTED(); return true; } -void Session::Acquire(Thread* thread) { - UNIMPLEMENTED(); -} - } // namespace Kernel diff --git a/src/core/hle/kernel/session.h b/src/core/hle/kernel/session.h index 7cd9c0d77..f6dd2c1d2 100644 --- a/src/core/hle/kernel/session.h +++ b/src/core/hle/kernel/session.h @@ -8,7 +8,7 @@ #include <string> #include <utility> -#include "core/hle/kernel/synchronization_object.h" +#include "core/hle/kernel/k_synchronization_object.h" namespace Kernel { @@ -19,7 +19,7 @@ class ServerSession; * Parent structure to link the client and server endpoints of a session with their associated * client port. */ -class Session final : public SynchronizationObject { +class Session final : public KSynchronizationObject { public: explicit Session(KernelCore& kernel); ~Session() override; @@ -37,12 +37,8 @@ public: return HANDLE_TYPE; } - bool ShouldWait(const Thread* thread) const override; - bool IsSignaled() const override; - void Acquire(Thread* thread) override; - std::shared_ptr<ClientSession> Client() { if (auto result{client.lock()}) { return result; diff --git a/src/core/hle/kernel/svc.cpp b/src/core/hle/kernel/svc.cpp index de3ed25da..cc8b661af 100644 --- a/src/core/hle/kernel/svc.cpp +++ b/src/core/hle/kernel/svc.cpp @@ -10,6 +10,7 @@ #include "common/alignment.h" #include "common/assert.h" +#include "common/common_funcs.h" #include "common/fiber.h" #include "common/logging/log.h" #include "common/microprofile.h" @@ -19,26 +20,28 @@ #include "core/core_timing.h" #include "core/core_timing_util.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_session.h" #include "core/hle/kernel/errors.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_scoped_scheduler_lock_and_sleep.h" +#include "core/hle/kernel/k_synchronization_object.h" #include "core/hle/kernel/kernel.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/mutex.h" #include "core/hle/kernel/physical_core.h" #include "core/hle/kernel/process.h" #include "core/hle/kernel/readable_event.h" #include "core/hle/kernel/resource_limit.h" #include "core/hle/kernel/shared_memory.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_wrap.h" -#include "core/hle/kernel/synchronization.h" #include "core/hle/kernel/thread.h" #include "core/hle/kernel/time_manager.h" #include "core/hle/kernel/transfer_memory.h" @@ -343,27 +346,11 @@ static ResultCode SendSyncRequest(Core::System& system, Handle handle) { auto thread = kernel.CurrentScheduler()->GetCurrentThread(); { KScopedSchedulerLock lock(kernel); - thread->InvalidateHLECallback(); - thread->SetStatus(ThreadStatus::WaitIPC); + thread->SetState(ThreadState::Waiting); + thread->SetWaitReasonForDebugging(ThreadWaitReasonForDebugging::IPC); session->SendSyncRequest(SharedFrom(thread), system.Memory(), system.CoreTiming()); } - if (thread->HasHLECallback()) { - Handle event_handle = thread->GetHLETimeEvent(); - if (event_handle != InvalidHandle) { - auto& time_manager = kernel.TimeManager(); - time_manager.UnscheduleTimeEvent(event_handle); - } - - { - KScopedSchedulerLock lock(kernel); - auto* sync_object = thread->GetHLESyncObject(); - sync_object->RemoveWaitingThread(SharedFrom(thread)); - } - - thread->InvokeHLECallback(SharedFrom(thread)); - } - return thread->GetSignalingResult(); } @@ -436,7 +423,7 @@ static ResultCode GetProcessId32(Core::System& system, u32* process_id_low, u32* } /// Wait for the given handles to synchronize, timeout after the specified nanoseconds -static ResultCode WaitSynchronization(Core::System& system, Handle* index, VAddr handles_address, +static ResultCode WaitSynchronization(Core::System& system, s32* index, VAddr handles_address, u64 handle_count, s64 nano_seconds) { LOG_TRACE(Kernel_SVC, "called handles_address=0x{:X}, handle_count={}, nano_seconds={}", handles_address, handle_count, nano_seconds); @@ -458,28 +445,26 @@ static ResultCode WaitSynchronization(Core::System& system, Handle* index, VAddr } auto& kernel = system.Kernel(); - Thread::ThreadSynchronizationObjects objects(handle_count); + std::vector<KSynchronizationObject*> objects(handle_count); const auto& handle_table = kernel.CurrentProcess()->GetHandleTable(); for (u64 i = 0; i < handle_count; ++i) { const Handle handle = memory.Read32(handles_address + i * sizeof(Handle)); - const auto object = handle_table.Get<SynchronizationObject>(handle); + const auto object = handle_table.Get<KSynchronizationObject>(handle); if (object == nullptr) { LOG_ERROR(Kernel_SVC, "Object is a nullptr"); return ERR_INVALID_HANDLE; } - objects[i] = object; + objects[i] = object.get(); } - auto& synchronization = kernel.Synchronization(); - const auto [result, handle_result] = synchronization.WaitFor(objects, nano_seconds); - *index = handle_result; - return result; + return KSynchronizationObject::Wait(kernel, index, objects.data(), + static_cast<s32>(objects.size()), nano_seconds); } static ResultCode WaitSynchronization32(Core::System& system, u32 timeout_low, u32 handles_address, - s32 handle_count, u32 timeout_high, Handle* index) { + s32 handle_count, u32 timeout_high, s32* index) { const s64 nano_seconds{(static_cast<s64>(timeout_high) << 32) | static_cast<s64>(timeout_low)}; return WaitSynchronization(system, index, handles_address, handle_count, nano_seconds); } @@ -504,56 +489,37 @@ static ResultCode CancelSynchronization32(Core::System& system, Handle thread_ha return CancelSynchronization(system, thread_handle); } -/// Attempts to locks a mutex, creating it if it does not already exist -static ResultCode ArbitrateLock(Core::System& system, Handle holding_thread_handle, - VAddr mutex_addr, Handle requesting_thread_handle) { - LOG_TRACE(Kernel_SVC, - "called holding_thread_handle=0x{:08X}, mutex_addr=0x{:X}, " - "requesting_current_thread_handle=0x{:08X}", - holding_thread_handle, mutex_addr, requesting_thread_handle); - - if (Core::Memory::IsKernelVirtualAddress(mutex_addr)) { - LOG_ERROR(Kernel_SVC, "Mutex Address is a kernel virtual address, mutex_addr={:016X}", - mutex_addr); - return ERR_INVALID_ADDRESS_STATE; - } +/// Attempts to locks a mutex +static ResultCode ArbitrateLock(Core::System& system, Handle thread_handle, VAddr address, + u32 tag) { + LOG_TRACE(Kernel_SVC, "called thread_handle=0x{:08X}, address=0x{:X}, tag=0x{:08X}", + thread_handle, address, tag); - if (!Common::IsWordAligned(mutex_addr)) { - LOG_ERROR(Kernel_SVC, "Mutex Address is not word aligned, mutex_addr={:016X}", mutex_addr); - return ERR_INVALID_ADDRESS; - } + // Validate the input address. + R_UNLESS(!Memory::IsKernelAddress(address), Svc::ResultInvalidCurrentMemory); + R_UNLESS(Common::IsAligned(address, sizeof(u32)), Svc::ResultInvalidAddress); - auto* const current_process = system.Kernel().CurrentProcess(); - return current_process->GetMutex().TryAcquire(mutex_addr, holding_thread_handle, - requesting_thread_handle); + return system.Kernel().CurrentProcess()->WaitForAddress(thread_handle, address, tag); } -static ResultCode ArbitrateLock32(Core::System& system, Handle holding_thread_handle, - u32 mutex_addr, Handle requesting_thread_handle) { - return ArbitrateLock(system, holding_thread_handle, mutex_addr, requesting_thread_handle); +static ResultCode ArbitrateLock32(Core::System& system, Handle thread_handle, u32 address, + u32 tag) { + return ArbitrateLock(system, thread_handle, address, tag); } /// Unlock a mutex -static ResultCode ArbitrateUnlock(Core::System& system, VAddr mutex_addr) { - LOG_TRACE(Kernel_SVC, "called mutex_addr=0x{:X}", mutex_addr); - - if (Core::Memory::IsKernelVirtualAddress(mutex_addr)) { - LOG_ERROR(Kernel_SVC, "Mutex Address is a kernel virtual address, mutex_addr={:016X}", - mutex_addr); - return ERR_INVALID_ADDRESS_STATE; - } +static ResultCode ArbitrateUnlock(Core::System& system, VAddr address) { + LOG_TRACE(Kernel_SVC, "called address=0x{:X}", address); - if (!Common::IsWordAligned(mutex_addr)) { - LOG_ERROR(Kernel_SVC, "Mutex Address is not word aligned, mutex_addr={:016X}", mutex_addr); - return ERR_INVALID_ADDRESS; - } + // Validate the input address. + R_UNLESS(!Memory::IsKernelAddress(address), Svc::ResultInvalidCurrentMemory); + R_UNLESS(Common::IsAligned(address, sizeof(u32)), Svc::ResultInvalidAddress); - auto* const current_process = system.Kernel().CurrentProcess(); - return current_process->GetMutex().Release(mutex_addr); + return system.Kernel().CurrentProcess()->SignalToAddress(address); } -static ResultCode ArbitrateUnlock32(Core::System& system, u32 mutex_addr) { - return ArbitrateUnlock(system, mutex_addr); +static ResultCode ArbitrateUnlock32(Core::System& system, u32 address) { + return ArbitrateUnlock(system, address); } enum class BreakType : u32 { @@ -1180,7 +1146,7 @@ static ResultCode SetThreadPriority(Core::System& system, Handle handle, u32 pri return ERR_INVALID_HANDLE; } - thread->SetPriority(priority); + thread->SetBasePriority(priority); return RESULT_SUCCESS; } @@ -1559,7 +1525,7 @@ static ResultCode StartThread(Core::System& system, Handle thread_handle) { return ERR_INVALID_HANDLE; } - ASSERT(thread->GetStatus() == ThreadStatus::Dormant); + ASSERT(thread->GetState() == ThreadState::Initialized); return thread->Start(); } @@ -1620,224 +1586,135 @@ static void SleepThread32(Core::System& system, u32 nanoseconds_low, u32 nanosec } /// Wait process wide key atomic -static ResultCode WaitProcessWideKeyAtomic(Core::System& system, VAddr mutex_addr, - VAddr condition_variable_addr, Handle thread_handle, - s64 nano_seconds) { - LOG_TRACE( - 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)) { - LOG_ERROR( - Kernel_SVC, - "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)) { - LOG_ERROR(Kernel_SVC, "Given mutex address must be word-aligned. address=0x{:016X}", - mutex_addr); - return ERR_INVALID_ADDRESS; - } - - ASSERT(condition_variable_addr == Common::AlignDown(condition_variable_addr, 4)); - auto& kernel = system.Kernel(); - Handle event_handle; - Thread* current_thread = kernel.CurrentScheduler()->GetCurrentThread(); - auto* const current_process = kernel.CurrentProcess(); - { - KScopedSchedulerLockAndSleep lock(kernel, event_handle, current_thread, nano_seconds); - const auto& handle_table = current_process->GetHandleTable(); - std::shared_ptr<Thread> thread = handle_table.Get<Thread>(thread_handle); - ASSERT(thread); - - current_thread->SetSynchronizationResults(nullptr, RESULT_TIMEOUT); - - if (thread->IsPendingTermination()) { - lock.CancelSleep(); - return ERR_THREAD_TERMINATING; - } - - const auto release_result = current_process->GetMutex().Release(mutex_addr); - if (release_result.IsError()) { - lock.CancelSleep(); - return release_result; - } - - if (nano_seconds == 0) { - lock.CancelSleep(); - return RESULT_TIMEOUT; - } - - current_thread->SetCondVarWaitAddress(condition_variable_addr); - current_thread->SetMutexWaitAddress(mutex_addr); - current_thread->SetWaitHandle(thread_handle); - current_thread->SetStatus(ThreadStatus::WaitCondVar); - current_process->InsertConditionVariableThread(SharedFrom(current_thread)); - } - - if (event_handle != InvalidHandle) { - auto& time_manager = kernel.TimeManager(); - time_manager.UnscheduleTimeEvent(event_handle); - } - - { - KScopedSchedulerLock lock(kernel); - - auto* owner = current_thread->GetLockOwner(); - if (owner != nullptr) { - owner->RemoveMutexWaiter(SharedFrom(current_thread)); +static ResultCode WaitProcessWideKeyAtomic(Core::System& system, VAddr address, VAddr cv_key, + u32 tag, s64 timeout_ns) { + LOG_TRACE(Kernel_SVC, "called address={:X}, cv_key={:X}, tag=0x{:08X}, timeout_ns={}", address, + cv_key, tag, timeout_ns); + + // Validate input. + R_UNLESS(!Memory::IsKernelAddress(address), Svc::ResultInvalidCurrentMemory); + R_UNLESS(Common::IsAligned(address, sizeof(int32_t)), Svc::ResultInvalidAddress); + + // 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(); } - - current_process->RemoveConditionVariableThread(SharedFrom(current_thread)); + } else { + timeout = timeout_ns; } - // Note: Deliberately don't attempt to inherit the lock owner's priority. - return current_thread->GetSignalingResult(); + // Wait on the condition variable. + return system.Kernel().CurrentProcess()->WaitConditionVariable( + address, Common::AlignDown(cv_key, sizeof(u32)), tag, timeout); } -static ResultCode WaitProcessWideKeyAtomic32(Core::System& system, u32 mutex_addr, - u32 condition_variable_addr, Handle thread_handle, - u32 nanoseconds_low, u32 nanoseconds_high) { - const auto nanoseconds = static_cast<s64>(nanoseconds_low | (u64{nanoseconds_high} << 32)); - return WaitProcessWideKeyAtomic(system, mutex_addr, condition_variable_addr, thread_handle, - nanoseconds); +static ResultCode WaitProcessWideKeyAtomic32(Core::System& system, u32 address, u32 cv_key, u32 tag, + u32 timeout_ns_low, u32 timeout_ns_high) { + const auto timeout_ns = static_cast<s64>(timeout_ns_low | (u64{timeout_ns_high} << 32)); + return WaitProcessWideKeyAtomic(system, address, cv_key, tag, timeout_ns); } /// Signal process wide key -static void SignalProcessWideKey(Core::System& system, VAddr condition_variable_addr, s32 target) { - LOG_TRACE(Kernel_SVC, "called, condition_variable_addr=0x{:X}, target=0x{:08X}", - condition_variable_addr, target); +static void SignalProcessWideKey(Core::System& system, VAddr cv_key, s32 count) { + LOG_TRACE(Kernel_SVC, "called, cv_key=0x{:X}, count=0x{:08X}", cv_key, count); - 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. - auto& kernel = system.Kernel(); - 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 - // 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); - - // liberate Cond Var Thread. - current_process->RemoveConditionVariableThread(thread); - - const std::size_t current_core = system.CurrentCoreIndex(); - auto& monitor = system.Monitor(); - - // 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); - } +static void SignalProcessWideKey32(Core::System& system, u32 cv_key, s32 count) { + SignalProcessWideKey(system, cv_key, count); +} - thread->SetLockOwner(nullptr); - thread->SetSynchronizationResults(nullptr, RESULT_SUCCESS); - thread->ResumeFromWait(); - } 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); - if (thread->GetStatus() == ThreadStatus::WaitCondVar) { - thread->SetStatus(ThreadStatus::WaitMutex); - } +namespace { - owner->AddMutexWaiter(thread); - } +constexpr bool IsValidSignalType(Svc::SignalType type) { + switch (type) { + case Svc::SignalType::Signal: + case Svc::SignalType::SignalAndIncrementIfEqual: + case Svc::SignalType::SignalAndModifyByWaitingCountIfEqual: + return true; + default: + return false; } } -static void SignalProcessWideKey32(Core::System& system, u32 condition_variable_addr, s32 target) { - SignalProcessWideKey(system, condition_variable_addr, target); +constexpr bool IsValidArbitrationType(Svc::ArbitrationType type) { + switch (type) { + case Svc::ArbitrationType::WaitIfLessThan: + case Svc::ArbitrationType::DecrementAndWaitIfLessThan: + case Svc::ArbitrationType::WaitIfEqual: + return true; + default: + return false; + } } -// Wait for an address (via Address Arbiter) -static ResultCode WaitForAddress(Core::System& system, VAddr address, u32 type, s32 value, - s64 timeout) { - LOG_TRACE(Kernel_SVC, "called, address=0x{:X}, type=0x{:X}, value=0x{:X}, timeout={}", address, - type, value, timeout); - - // If the passed address is a kernel virtual address, return invalid memory state. - if (Core::Memory::IsKernelVirtualAddress(address)) { - LOG_ERROR(Kernel_SVC, "Address is a kernel virtual address, address={:016X}", address); - return ERR_INVALID_ADDRESS_STATE; - } +} // namespace - // If the address is not properly aligned to 4 bytes, return invalid address. - if (!Common::IsWordAligned(address)) { - LOG_ERROR(Kernel_SVC, "Address is not word aligned, address={:016X}", address); - return ERR_INVALID_ADDRESS; +// Wait for an address (via Address Arbiter) +static ResultCode WaitForAddress(Core::System& system, VAddr address, Svc::ArbitrationType arb_type, + s32 value, s64 timeout_ns) { + LOG_TRACE(Kernel_SVC, "called, address=0x{:X}, arb_type=0x{:X}, value=0x{:X}, timeout_ns={}", + address, arb_type, value, timeout_ns); + + // Validate input. + R_UNLESS(!Memory::IsKernelAddress(address), Svc::ResultInvalidCurrentMemory); + R_UNLESS(Common::IsAligned(address, sizeof(int32_t)), Svc::ResultInvalidAddress); + 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; } - 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; + return system.Kernel().CurrentProcess()->WaitAddressArbiter(address, arb_type, value, timeout); } -static ResultCode WaitForAddress32(Core::System& system, u32 address, u32 type, s32 value, - u32 timeout_low, u32 timeout_high) { - const auto timeout = static_cast<s64>(timeout_low | (u64{timeout_high} << 32)); - return WaitForAddress(system, address, type, value, timeout); +static ResultCode WaitForAddress32(Core::System& system, u32 address, Svc::ArbitrationType arb_type, + s32 value, u32 timeout_ns_low, u32 timeout_ns_high) { + const auto timeout = static_cast<s64>(timeout_ns_low | (u64{timeout_ns_high} << 32)); + return WaitForAddress(system, address, arb_type, value, timeout); } // Signals to an address (via Address Arbiter) -static ResultCode SignalToAddress(Core::System& system, VAddr address, u32 type, s32 value, - s32 num_to_wake) { - LOG_TRACE(Kernel_SVC, "called, address=0x{:X}, type=0x{:X}, value=0x{:X}, num_to_wake=0x{:X}", - address, type, value, num_to_wake); - - // If the passed address is a kernel virtual address, return invalid memory state. - if (Core::Memory::IsKernelVirtualAddress(address)) { - LOG_ERROR(Kernel_SVC, "Address is a kernel virtual address, address={:016X}", address); - return ERR_INVALID_ADDRESS_STATE; - } +static ResultCode SignalToAddress(Core::System& system, VAddr address, Svc::SignalType signal_type, + s32 value, s32 count) { + LOG_TRACE(Kernel_SVC, "called, address=0x{:X}, signal_type=0x{:X}, value=0x{:X}, count=0x{:X}", + address, signal_type, value, count); - // If the address is not properly aligned to 4 bytes, return invalid address. - if (!Common::IsWordAligned(address)) { - LOG_ERROR(Kernel_SVC, "Address is not word aligned, address={:016X}", address); - return ERR_INVALID_ADDRESS; - } + // Validate input. + R_UNLESS(!Memory::IsKernelAddress(address), Svc::ResultInvalidCurrentMemory); + R_UNLESS(Common::IsAligned(address, sizeof(s32)), Svc::ResultInvalidAddress); + R_UNLESS(IsValidSignalType(signal_type), Svc::ResultInvalidEnumValue); - 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); + return system.Kernel().CurrentProcess()->SignalAddressArbiter(address, signal_type, value, + count); } -static ResultCode SignalToAddress32(Core::System& system, u32 address, u32 type, s32 value, - s32 num_to_wake) { - return SignalToAddress(system, address, type, value, num_to_wake); +static ResultCode SignalToAddress32(Core::System& system, u32 address, Svc::SignalType signal_type, + s32 value, s32 count) { + return SignalToAddress(system, address, signal_type, value, count); } static void KernelDebug([[maybe_unused]] Core::System& system, diff --git a/src/core/hle/kernel/svc_common.h b/src/core/hle/kernel/svc_common.h new file mode 100644 index 000000000..4af049551 --- /dev/null +++ b/src/core/hle/kernel/svc_common.h @@ -0,0 +1,14 @@ +// Copyright 2020 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include "common/common_types.h" + +namespace Kernel::Svc { + +constexpr s32 ArgumentHandleCountMax = 0x40; +constexpr u32 HandleWaitMask{1u << 30}; + +} // namespace Kernel::Svc diff --git a/src/core/hle/kernel/svc_results.h b/src/core/hle/kernel/svc_results.h new file mode 100644 index 000000000..78282f021 --- /dev/null +++ b/src/core/hle/kernel/svc_results.h @@ -0,0 +1,20 @@ +// Copyright 2020 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include "core/hle/result.h" + +namespace Kernel::Svc { + +constexpr ResultCode ResultTerminationRequested{ErrorModule::Kernel, 59}; +constexpr ResultCode ResultInvalidAddress{ErrorModule::Kernel, 102}; +constexpr ResultCode ResultInvalidCurrentMemory{ErrorModule::Kernel, 106}; +constexpr ResultCode ResultInvalidHandle{ErrorModule::Kernel, 114}; +constexpr ResultCode ResultTimedOut{ErrorModule::Kernel, 117}; +constexpr ResultCode ResultCancelled{ErrorModule::Kernel, 118}; +constexpr ResultCode ResultInvalidEnumValue{ErrorModule::Kernel, 120}; +constexpr ResultCode ResultInvalidState{ErrorModule::Kernel, 125}; + +} // namespace Kernel::Svc diff --git a/src/core/hle/kernel/svc_types.h b/src/core/hle/kernel/svc_types.h index 11e1d8e2d..d623f7a50 100644 --- a/src/core/hle/kernel/svc_types.h +++ b/src/core/hle/kernel/svc_types.h @@ -65,4 +65,16 @@ struct MemoryInfo { u32 padding{}; }; +enum class SignalType : u32 { + Signal = 0, + SignalAndIncrementIfEqual = 1, + SignalAndModifyByWaitingCountIfEqual = 2, +}; + +enum class ArbitrationType : u32 { + WaitIfLessThan = 0, + DecrementAndWaitIfLessThan = 1, + WaitIfEqual = 2, +}; + } // namespace Kernel::Svc diff --git a/src/core/hle/kernel/svc_wrap.h b/src/core/hle/kernel/svc_wrap.h index 0b6dd9df0..a32750ed7 100644 --- a/src/core/hle/kernel/svc_wrap.h +++ b/src/core/hle/kernel/svc_wrap.h @@ -7,6 +7,7 @@ #include "common/common_types.h" #include "core/arm/arm_interface.h" #include "core/core.h" +#include "core/hle/kernel/svc_types.h" #include "core/hle/result.h" namespace Kernel { @@ -215,9 +216,10 @@ void SvcWrap64(Core::System& system) { func(system, static_cast<u32>(Param(system, 0)), Param(system, 1), Param(system, 2)).raw); } -template <ResultCode func(Core::System&, u32*, u64, u64, s64)> +// Used by WaitSynchronization +template <ResultCode func(Core::System&, s32*, u64, u64, s64)> void SvcWrap64(Core::System& system) { - u32 param_1 = 0; + s32 param_1 = 0; const u32 retval = func(system, ¶m_1, Param(system, 1), static_cast<u32>(Param(system, 2)), static_cast<s64>(Param(system, 3))) .raw; @@ -276,18 +278,22 @@ void SvcWrap64(Core::System& system) { 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) { - FuncReturn(system, func(system, Param(system, 0), static_cast<u32>(Param(system, 1)), - static_cast<s32>(Param(system, 2)), static_cast<s64>(Param(system, 3))) - .raw); + FuncReturn(system, + func(system, Param(system, 0), static_cast<Svc::ArbitrationType>(Param(system, 1)), + static_cast<s32>(Param(system, 2)), static_cast<s64>(Param(system, 3))) + .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) { - FuncReturn(system, func(system, Param(system, 0), static_cast<u32>(Param(system, 1)), - static_cast<s32>(Param(system, 2)), static_cast<s32>(Param(system, 3))) - .raw); + FuncReturn(system, + func(system, Param(system, 0), static_cast<Svc::SignalType>(Param(system, 1)), + static_cast<s32>(Param(system, 2)), static_cast<s32>(Param(system, 3))) + .raw); } //////////////////////////////////////////////////////////////////////////////////////////////////// @@ -503,22 +509,23 @@ void SvcWrap32(Core::System& system) { } // 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) { 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<u32>(Param(system, 3)), static_cast<u32>(Param(system, 4))) + static_cast<Svc::ArbitrationType>(Param(system, 1)), + static_cast<s32>(Param(system, 2)), static_cast<u32>(Param(system, 3)), + static_cast<u32>(Param(system, 4))) .raw; FuncReturn(system, retval); } // 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) { - 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<s32>(Param(system, 3))) - .raw; + const u32 retval = func(system, static_cast<u32>(Param(system, 0)), + static_cast<Svc::SignalType>(Param(system, 1)), + static_cast<s32>(Param(system, 2)), static_cast<s32>(Param(system, 3))) + .raw; FuncReturn(system, retval); } @@ -539,9 +546,9 @@ void SvcWrap32(Core::System& system) { } // Used by WaitSynchronization32 -template <ResultCode func(Core::System&, u32, u32, s32, u32, Handle*)> +template <ResultCode func(Core::System&, u32, u32, s32, u32, s32*)> void SvcWrap32(Core::System& system) { - u32 param_1 = 0; + s32 param_1 = 0; const u32 retval = func(system, Param32(system, 0), Param32(system, 1), Param32(system, 2), Param32(system, 3), ¶m_1) .raw; diff --git a/src/core/hle/kernel/synchronization.cpp b/src/core/hle/kernel/synchronization.cpp deleted file mode 100644 index d3f520ea2..000000000 --- a/src/core/hle/kernel/synchronization.cpp +++ /dev/null @@ -1,116 +0,0 @@ -// Copyright 2020 yuzu Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. - -#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/k_scoped_scheduler_lock_and_sleep.h" -#include "core/hle/kernel/kernel.h" -#include "core/hle/kernel/synchronization.h" -#include "core/hle/kernel/synchronization_object.h" -#include "core/hle/kernel/thread.h" -#include "core/hle/kernel/time_manager.h" - -namespace Kernel { - -Synchronization::Synchronization(Core::System& system) : system{system} {} - -void Synchronization::SignalObject(SynchronizationObject& obj) const { - auto& kernel = system.Kernel(); - KScopedSchedulerLock lock(kernel); - if (obj.IsSignaled()) { - for (auto thread : obj.GetWaitingThreads()) { - if (thread->GetSchedulingStatus() == ThreadSchedStatus::Paused) { - if (thread->GetStatus() != ThreadStatus::WaitHLEEvent) { - ASSERT(thread->GetStatus() == ThreadStatus::WaitSynch); - ASSERT(thread->IsWaitingSync()); - } - thread->SetSynchronizationResults(&obj, RESULT_SUCCESS); - thread->ResumeFromWait(); - } - } - obj.ClearWaitingThreads(); - } -} - -std::pair<ResultCode, Handle> Synchronization::WaitFor( - std::vector<std::shared_ptr<SynchronizationObject>>& sync_objects, s64 nano_seconds) { - auto& kernel = system.Kernel(); - auto* const thread = kernel.CurrentScheduler()->GetCurrentThread(); - Handle event_handle = InvalidHandle; - { - KScopedSchedulerLockAndSleep lock(kernel, event_handle, thread, nano_seconds); - const auto itr = - std::find_if(sync_objects.begin(), sync_objects.end(), - [thread](const std::shared_ptr<SynchronizationObject>& object) { - return object->IsSignaled(); - }); - - if (itr != sync_objects.end()) { - // We found a ready object, acquire it and set the result value - SynchronizationObject* object = itr->get(); - object->Acquire(thread); - const u32 index = static_cast<s32>(std::distance(sync_objects.begin(), itr)); - lock.CancelSleep(); - return {RESULT_SUCCESS, index}; - } - - if (nano_seconds == 0) { - lock.CancelSleep(); - return {RESULT_TIMEOUT, InvalidHandle}; - } - - if (thread->IsPendingTermination()) { - lock.CancelSleep(); - return {ERR_THREAD_TERMINATING, InvalidHandle}; - } - - if (thread->IsSyncCancelled()) { - thread->SetSyncCancelled(false); - lock.CancelSleep(); - return {ERR_SYNCHRONIZATION_CANCELED, InvalidHandle}; - } - - for (auto& object : sync_objects) { - object->AddWaitingThread(SharedFrom(thread)); - } - - thread->SetSynchronizationObjects(&sync_objects); - thread->SetSynchronizationResults(nullptr, RESULT_TIMEOUT); - thread->SetStatus(ThreadStatus::WaitSynch); - thread->SetWaitingSync(true); - } - thread->SetWaitingSync(false); - - if (event_handle != InvalidHandle) { - auto& time_manager = kernel.TimeManager(); - time_manager.UnscheduleTimeEvent(event_handle); - } - - { - KScopedSchedulerLock lock(kernel); - ResultCode signaling_result = thread->GetSignalingResult(); - SynchronizationObject* signaling_object = thread->GetSignalingObject(); - thread->SetSynchronizationObjects(nullptr); - auto shared_thread = SharedFrom(thread); - for (auto& obj : sync_objects) { - obj->RemoveWaitingThread(shared_thread); - } - if (signaling_object != nullptr) { - const auto itr = std::find_if( - sync_objects.begin(), sync_objects.end(), - [signaling_object](const std::shared_ptr<SynchronizationObject>& object) { - return object.get() == signaling_object; - }); - ASSERT(itr != sync_objects.end()); - signaling_object->Acquire(thread); - const u32 index = static_cast<s32>(std::distance(sync_objects.begin(), itr)); - return {signaling_result, index}; - } - return {signaling_result, -1}; - } -} - -} // namespace Kernel diff --git a/src/core/hle/kernel/synchronization.h b/src/core/hle/kernel/synchronization.h deleted file mode 100644 index 379f4b1d3..000000000 --- a/src/core/hle/kernel/synchronization.h +++ /dev/null @@ -1,44 +0,0 @@ -// Copyright 2020 yuzu Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. - -#pragma once - -#include <memory> -#include <utility> -#include <vector> - -#include "core/hle/kernel/object.h" -#include "core/hle/result.h" - -namespace Core { -class System; -} // namespace Core - -namespace Kernel { - -class SynchronizationObject; - -/** - * The 'Synchronization' class is an interface for handling synchronization methods - * used by Synchronization objects and synchronization SVCs. This centralizes processing of - * such - */ -class Synchronization { -public: - explicit Synchronization(Core::System& system); - - /// Signals a synchronization object, waking up all its waiting threads - void SignalObject(SynchronizationObject& obj) const; - - /// Tries to see if waiting for any of the sync_objects is necessary, if not - /// it returns Success and the handle index of the signaled sync object. In - /// case not, the current thread will be locked and wait for nano_seconds or - /// for a synchronization object to signal. - std::pair<ResultCode, Handle> WaitFor( - std::vector<std::shared_ptr<SynchronizationObject>>& sync_objects, s64 nano_seconds); - -private: - Core::System& system; -}; -} // namespace Kernel diff --git a/src/core/hle/kernel/synchronization_object.cpp b/src/core/hle/kernel/synchronization_object.cpp deleted file mode 100644 index ba4d39157..000000000 --- a/src/core/hle/kernel/synchronization_object.cpp +++ /dev/null @@ -1,49 +0,0 @@ -// Copyright 2014 Citra Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. - -#include <algorithm> -#include "common/assert.h" -#include "common/common_types.h" -#include "common/logging/log.h" -#include "core/core.h" -#include "core/hle/kernel/kernel.h" -#include "core/hle/kernel/object.h" -#include "core/hle/kernel/process.h" -#include "core/hle/kernel/synchronization.h" -#include "core/hle/kernel/synchronization_object.h" -#include "core/hle/kernel/thread.h" - -namespace Kernel { - -SynchronizationObject::SynchronizationObject(KernelCore& kernel) : Object{kernel} {} -SynchronizationObject::~SynchronizationObject() = default; - -void SynchronizationObject::Signal() { - kernel.Synchronization().SignalObject(*this); -} - -void SynchronizationObject::AddWaitingThread(std::shared_ptr<Thread> thread) { - auto itr = std::find(waiting_threads.begin(), waiting_threads.end(), thread); - if (itr == waiting_threads.end()) - waiting_threads.push_back(std::move(thread)); -} - -void SynchronizationObject::RemoveWaitingThread(std::shared_ptr<Thread> thread) { - auto itr = std::find(waiting_threads.begin(), waiting_threads.end(), thread); - // If a thread passed multiple handles to the same object, - // the kernel might attempt to remove the thread from the object's - // waiting threads list multiple times. - if (itr != waiting_threads.end()) - waiting_threads.erase(itr); -} - -void SynchronizationObject::ClearWaitingThreads() { - waiting_threads.clear(); -} - -const std::vector<std::shared_ptr<Thread>>& SynchronizationObject::GetWaitingThreads() const { - return waiting_threads; -} - -} // namespace Kernel diff --git a/src/core/hle/kernel/synchronization_object.h b/src/core/hle/kernel/synchronization_object.h deleted file mode 100644 index 7408ed51f..000000000 --- a/src/core/hle/kernel/synchronization_object.h +++ /dev/null @@ -1,77 +0,0 @@ -// Copyright 2014 Citra Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. - -#pragma once - -#include <atomic> -#include <memory> -#include <vector> - -#include "core/hle/kernel/object.h" - -namespace Kernel { - -class KernelCore; -class Synchronization; -class Thread; - -/// Class that represents a Kernel object that a thread can be waiting on -class SynchronizationObject : public Object { -public: - explicit SynchronizationObject(KernelCore& kernel); - ~SynchronizationObject() override; - - /** - * Check if the specified thread should wait until the object is available - * @param thread The thread about which we're deciding. - * @return True if the current thread should wait due to this object being unavailable - */ - virtual bool ShouldWait(const Thread* thread) const = 0; - - /// Acquire/lock the object for the specified thread if it is available - virtual void Acquire(Thread* thread) = 0; - - /// Signal this object - virtual void Signal(); - - virtual bool IsSignaled() const { - return is_signaled; - } - - /** - * Add a thread to wait on this object - * @param thread Pointer to thread to add - */ - void AddWaitingThread(std::shared_ptr<Thread> thread); - - /** - * Removes a thread from waiting on this object (e.g. if it was resumed already) - * @param thread Pointer to thread to remove - */ - void RemoveWaitingThread(std::shared_ptr<Thread> thread); - - /// Get a const reference to the waiting threads list for debug use - const std::vector<std::shared_ptr<Thread>>& GetWaitingThreads() const; - - void ClearWaitingThreads(); - -protected: - std::atomic_bool is_signaled{}; // Tells if this sync object is signaled - -private: - /// Threads waiting for this object to become available - std::vector<std::shared_ptr<Thread>> waiting_threads; -}; - -// Specialization of DynamicObjectCast for SynchronizationObjects -template <> -inline std::shared_ptr<SynchronizationObject> DynamicObjectCast<SynchronizationObject>( - std::shared_ptr<Object> object) { - if (object != nullptr && object->IsWaitable()) { - return std::static_pointer_cast<SynchronizationObject>(object); - } - return nullptr; -} - -} // namespace Kernel diff --git a/src/core/hle/kernel/thread.cpp b/src/core/hle/kernel/thread.cpp index a4f9e0d97..d97323255 100644 --- a/src/core/hle/kernel/thread.cpp +++ b/src/core/hle/kernel/thread.cpp @@ -17,9 +17,11 @@ #include "core/hardware_properties.h" #include "core/hle/kernel/errors.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_scoped_scheduler_lock_and_sleep.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/process.h" #include "core/hle/kernel/thread.h" @@ -34,26 +36,19 @@ namespace Kernel { -bool Thread::ShouldWait(const Thread* thread) const { - return status != ThreadStatus::Dead; -} - bool Thread::IsSignaled() const { - return status == ThreadStatus::Dead; -} - -void Thread::Acquire(Thread* thread) { - ASSERT_MSG(!ShouldWait(thread), "object unavailable!"); + return signaled; } -Thread::Thread(KernelCore& kernel) : SynchronizationObject{kernel} {} +Thread::Thread(KernelCore& kernel) : KSynchronizationObject{kernel} {} Thread::~Thread() = default; void Thread::Stop() { { KScopedSchedulerLock lock(kernel); - SetStatus(ThreadStatus::Dead); - Signal(); + SetState(ThreadState::Terminated); + signaled = true; + NotifyAvailable(); kernel.GlobalHandleTable().Close(global_handle); if (owner_process) { @@ -67,59 +62,27 @@ void Thread::Stop() { global_handle = 0; } -void Thread::ResumeFromWait() { +void Thread::Wakeup() { KScopedSchedulerLock lock(kernel); - switch (status) { - case ThreadStatus::Paused: - case ThreadStatus::WaitSynch: - case ThreadStatus::WaitHLEEvent: - case ThreadStatus::WaitSleep: - case ThreadStatus::WaitIPC: - case ThreadStatus::WaitMutex: - case ThreadStatus::WaitCondVar: - case ThreadStatus::WaitArb: - case ThreadStatus::Dormant: - break; - - case ThreadStatus::Ready: - // The thread's wakeup callback must have already been cleared when the thread was first - // awoken. - ASSERT(hle_callback == nullptr); - // 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 ThreadStatus::Dead: - // 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; - } - - SetStatus(ThreadStatus::Ready); -} - -void Thread::OnWakeUp() { - KScopedSchedulerLock lock(kernel); - SetStatus(ThreadStatus::Ready); + SetState(ThreadState::Runnable); } ResultCode Thread::Start() { KScopedSchedulerLock lock(kernel); - SetStatus(ThreadStatus::Ready); + SetState(ThreadState::Runnable); return RESULT_SUCCESS; } void Thread::CancelWait() { KScopedSchedulerLock lock(kernel); - if (GetSchedulingStatus() != ThreadSchedStatus::Paused || !is_waiting_on_sync) { + if (GetState() != ThreadState::Waiting || !is_cancellable) { is_sync_cancelled = true; return; } // TODO(Blinkhawk): Implement cancel of server session is_sync_cancelled = false; SetSynchronizationResults(nullptr, ERR_SYNCHRONIZATION_CANCELED); - SetStatus(ThreadStatus::Ready); + SetState(ThreadState::Runnable); } static void ResetThreadContext32(Core::ARM_Interface::ThreadContext32& context, u32 stack_top, @@ -183,25 +146,24 @@ ResultVal<std::shared_ptr<Thread>> Thread::Create(Core::System& system, ThreadTy std::shared_ptr<Thread> thread = std::make_shared<Thread>(kernel); thread->thread_id = kernel.CreateNewThreadID(); - thread->status = ThreadStatus::Dormant; + thread->thread_state = ThreadState::Initialized; thread->entry_point = entry_point; thread->stack_top = stack_top; thread->disable_count = 1; 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->last_scheduled_tick = 0; thread->processor_id = processor_id; thread->ideal_core = processor_id; thread->affinity_mask.SetAffinity(processor_id, true); - thread->wait_objects = nullptr; - thread->mutex_wait_address = 0; - thread->condvar_wait_address = 0; - thread->wait_handle = 0; thread->name = std::move(name); thread->global_handle = kernel.GlobalHandleTable().Create(thread).Unwrap(); thread->owner_process = owner_process; thread->type = type_flags; + thread->signaled = false; if ((type_flags & THREADTYPE_IDLE) == 0) { auto& scheduler = kernel.GlobalSchedulerContext(); scheduler.AddThread(thread); @@ -226,153 +188,185 @@ ResultVal<std::shared_ptr<Thread>> Thread::Create(Core::System& system, ThreadTy return MakeResult<std::shared_ptr<Thread>>(std::move(thread)); } -void Thread::SetPriority(u32 priority) { - KScopedSchedulerLock lock(kernel); +void Thread::SetBasePriority(u32 priority) { ASSERT_MSG(priority <= THREADPRIO_LOWEST && priority >= THREADPRIO_HIGHEST, "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(SynchronizationObject* object, ResultCode result) { +void Thread::SetSynchronizationResults(KSynchronizationObject* object, ResultCode result) { signaling_object = object; signaling_result = result; } -s32 Thread::GetSynchronizationObjectIndex(std::shared_ptr<SynchronizationObject> object) const { - ASSERT_MSG(!wait_objects->empty(), "Thread is not waiting for anything"); - const auto match = std::find(wait_objects->rbegin(), wait_objects->rend(), object); - return static_cast<s32>(std::distance(match, wait_objects->rend()) - 1); -} - VAddr Thread::GetCommandBufferAddress() const { // Offset from the start of TLS at which the IPC command buffer begins. constexpr u64 command_header_offset = 0x80; return GetTLSAddress() + command_header_offset; } -void Thread::SetStatus(ThreadStatus new_status) { - if (new_status == status) { - return; - } +void Thread::SetState(ThreadState state) { + KScopedSchedulerLock sl(kernel); - switch (new_status) { - case ThreadStatus::Ready: - SetSchedulingStatus(ThreadSchedStatus::Runnable); - break; - case ThreadStatus::Dormant: - SetSchedulingStatus(ThreadSchedStatus::None); - break; - case ThreadStatus::Dead: - SetSchedulingStatus(ThreadSchedStatus::Exited); - break; - default: - SetSchedulingStatus(ThreadSchedStatus::Paused); - break; - } + // Clear debugging state + SetMutexWaitAddressForDebugging({}); + SetWaitReasonForDebugging({}); - status = new_status; + 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); + } } -void Thread::AddMutexWaiter(std::shared_ptr<Thread> thread) { - if (thread->lock_owner.get() == this) { - // 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. - const auto iter = std::find(wait_mutex_threads.begin(), wait_mutex_threads.end(), thread); - ASSERT(iter != wait_mutex_threads.end()); - return; +void Thread::AddWaiterImpl(Thread* thread) { + ASSERT(kernel.GlobalSchedulerContext().IsLocked()); + + // Find the right spot to insert the waiter. + auto it = waiter_list.begin(); + while (it != waiter_list.end()) { + if (it->GetPriority() > thread->GetPriority()) { + break; + } + it++; } - // A thread can't wait on two different mutexes at the same time. - ASSERT(thread->lock_owner == nullptr); + // Keep track of how many kernel waiters we have. + if (Memory::IsKernelAddressKey(thread->GetAddressKey())) { + ASSERT((num_kernel_waiters++) >= 0); + } - // Ensure that the thread is not already in the list of mutex waiters - const auto iter = std::find(wait_mutex_threads.begin(), wait_mutex_threads.end(), thread); - ASSERT(iter == wait_mutex_threads.end()); + // Insert the waiter. + waiter_list.insert(it, *thread); + 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); +void Thread::RemoveWaiterImpl(Thread* thread) { + ASSERT(kernel.GlobalSchedulerContext().IsLocked()); - UpdatePriority(); -} + // Keep track of how many kernel waiters we have. + if (Memory::IsKernelAddressKey(thread->GetAddressKey())) { + ASSERT((num_kernel_waiters--) > 0); + } -void Thread::RemoveMutexWaiter(std::shared_ptr<Thread> thread) { - ASSERT(thread->lock_owner.get() == this); + // Remove the waiter. + waiter_list.erase(waiter_list.iterator_to(*thread)); + thread->SetLockOwner(nullptr); +} - // Ensure that the thread is in the list of mutex waiters - const auto iter = std::find(wait_mutex_threads.begin(), wait_mutex_threads.end(), thread); - ASSERT(iter != wait_mutex_threads.end()); +void Thread::RestorePriority(KernelCore& kernel, Thread* thread) { + ASSERT(kernel.GlobalSchedulerContext().IsLocked()); - wait_mutex_threads.erase(iter); + while (true) { + // We want to inherit priority where possible. + s32 new_priority = thread->GetBasePriority(); + if (thread->HasWaiters()) { + new_priority = std::min(new_priority, thread->waiter_list.front().GetPriority()); + } - thread->lock_owner = nullptr; - UpdatePriority(); -} + // If the priority we would inherit is not different from ours, don't do anything. + if (new_priority == thread->GetPriority()) { + return; + } -void Thread::UpdatePriority() { - // If any of the threads waiting on the mutex have a higher priority - // (taking into account priority inheritance), then this thread inherits - // that thread's priority. - u32 new_priority = nominal_priority; - if (!wait_mutex_threads.empty()) { - if (wait_mutex_threads.front()->current_priority < new_priority) { - new_priority = wait_mutex_threads.front()->current_priority; + // 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); } - } - if (new_priority == current_priority) { - return; - } + // Change the priority. + const s32 old_priority = thread->GetPriority(); + thread->SetPriority(new_priority); - if (GetStatus() == ThreadStatus::WaitCondVar) { - owner_process->RemoveConditionVariableThread(SharedFrom(this)); - } + // Restore the condition variable, if relevant. + if (auto* cv_tree = thread->GetConditionVariableTree(); cv_tree != nullptr) { + AfterUpdatePriority(kernel, cv_tree, thread); + } - SetCurrentPriority(new_priority); + // Update the scheduler. + KScheduler::OnThreadPriorityChanged(kernel, thread, old_priority); - if (GetStatus() == ThreadStatus::WaitCondVar) { - owner_process->InsertConditionVariableThread(SharedFrom(this)); - } + // Keep the lock owner up to date. + Thread* lock_owner = thread->GetLockOwner(); + if (lock_owner == nullptr) { + return; + } - if (!lock_owner) { - 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; } +} - // 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(); +void Thread::AddWaiter(Thread* thread) { + AddWaiterImpl(thread); + RestorePriority(kernel, this); } -bool Thread::AllSynchronizationObjectsReady() const { - return std::none_of(wait_objects->begin(), wait_objects->end(), - [this](const std::shared_ptr<SynchronizationObject>& object) { - return object->ShouldWait(this); - }); +void Thread::RemoveWaiter(Thread* thread) { + RemoveWaiterImpl(thread); + RestorePriority(kernel, this); } -bool Thread::InvokeHLECallback(std::shared_ptr<Thread> thread) { - ASSERT(hle_callback); - return hle_callback(std::move(thread)); +Thread* Thread::RemoveWaiterByKey(s32* out_num_waiters, VAddr key) { + ASSERT(kernel.GlobalSchedulerContext().IsLocked()); + + s32 num_waiters{}; + Thread* next_lock_owner{}; + auto it = waiter_list.begin(); + while (it != waiter_list.end()) { + if (it->GetAddressKey() == key) { + Thread* thread = std::addressof(*it); + + // Keep track of how many kernel waiters we have. + if (Memory::IsKernelAddressKey(thread->GetAddressKey())) { + ASSERT((num_kernel_waiters--) > 0); + } + it = waiter_list.erase(it); + + // Update the next lock owner. + if (next_lock_owner == nullptr) { + next_lock_owner = thread; + next_lock_owner->SetLockOwner(nullptr); + } else { + next_lock_owner->AddWaiterImpl(thread); + } + num_waiters++; + } else { + it++; + } + } + + // Do priority updates, if we have a next owner. + if (next_lock_owner) { + RestorePriority(kernel, this); + RestorePriority(kernel, next_lock_owner); + } + + // Return output. + *out_num_waiters = num_waiters; + return next_lock_owner; } ResultCode Thread::SetActivity(ThreadActivity value) { KScopedSchedulerLock lock(kernel); - auto sched_status = GetSchedulingStatus(); + auto sched_status = GetState(); - if (sched_status != ThreadSchedStatus::Runnable && sched_status != ThreadSchedStatus::Paused) { + if (sched_status != ThreadState::Runnable && sched_status != ThreadState::Waiting) { return ERR_INVALID_STATE; } - if (IsPendingTermination()) { + if (IsTerminationRequested()) { return RESULT_SUCCESS; } @@ -394,7 +388,8 @@ ResultCode Thread::Sleep(s64 nanoseconds) { Handle event_handle{}; { KScopedSchedulerLockAndSleep lock(kernel, event_handle, this, nanoseconds); - SetStatus(ThreadStatus::WaitSleep); + SetState(ThreadState::Waiting); + SetWaitReasonForDebugging(ThreadWaitReasonForDebugging::Sleep); } if (event_handle != InvalidHandle) { @@ -405,34 +400,21 @@ ResultCode Thread::Sleep(s64 nanoseconds) { } void Thread::AddSchedulingFlag(ThreadSchedFlags flag) { - const u32 old_state = scheduling_state; + const auto old_state = GetRawState(); pausing_state |= static_cast<u32>(flag); - const u32 base_scheduling = static_cast<u32>(GetSchedulingStatus()); - scheduling_state = base_scheduling | pausing_state; + const auto base_scheduling = GetState(); + thread_state = base_scheduling | static_cast<ThreadState>(pausing_state); KScheduler::OnThreadStateChanged(kernel, this, old_state); } void Thread::RemoveSchedulingFlag(ThreadSchedFlags flag) { - const u32 old_state = scheduling_state; + const auto old_state = GetRawState(); pausing_state &= ~static_cast<u32>(flag); - const u32 base_scheduling = static_cast<u32>(GetSchedulingStatus()); - scheduling_state = base_scheduling | pausing_state; + const auto base_scheduling = GetState(); + thread_state = base_scheduling | static_cast<ThreadState>(pausing_state); KScheduler::OnThreadStateChanged(kernel, this, old_state); } -void Thread::SetSchedulingStatus(ThreadSchedStatus new_status) { - const u32 old_state = scheduling_state; - scheduling_state = (scheduling_state & static_cast<u32>(ThreadSchedMasks::HighMask)) | - static_cast<u32>(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) { KScopedSchedulerLock lock(kernel); const auto HighestSetCore = [](u64 mask, u32 max_cores) { diff --git a/src/core/hle/kernel/thread.h b/src/core/hle/kernel/thread.h index 11ef29888..6b66c9a0e 100644 --- a/src/core/hle/kernel/thread.h +++ b/src/core/hle/kernel/thread.h @@ -6,16 +6,21 @@ #include <array> #include <functional> +#include <span> #include <string> #include <utility> #include <vector> +#include <boost/intrusive/list.hpp> + #include "common/common_types.h" +#include "common/intrusive_red_black_tree.h" #include "common/spin_lock.h" #include "core/arm/arm_interface.h" #include "core/hle/kernel/k_affinity_mask.h" +#include "core/hle/kernel/k_synchronization_object.h" #include "core/hle/kernel/object.h" -#include "core/hle/kernel/synchronization_object.h" +#include "core/hle/kernel/svc_common.h" #include "core/hle/result.h" namespace Common { @@ -73,19 +78,24 @@ enum ThreadProcessorId : s32 { (1 << THREADPROCESSORID_2) | (1 << THREADPROCESSORID_3) }; -enum class ThreadStatus { - Ready, ///< Ready to run - Paused, ///< Paused by SetThreadActivity or debug - WaitHLEEvent, ///< Waiting for hle event to finish - WaitSleep, ///< Waiting due to a SleepThread SVC - WaitIPC, ///< Waiting for the reply from an IPC request - WaitSynch, ///< Waiting due to WaitSynchronization - WaitMutex, ///< Waiting due to an ArbitrateLock svc - WaitCondVar, ///< Waiting due to an WaitProcessWideKey svc - WaitArb, ///< Waiting due to a SignalToAddress/WaitForAddress svc - Dormant, ///< Created but not yet made ready - Dead ///< Run to completion, or forcefully terminated +enum class ThreadState : u16 { + Initialized = 0, + Waiting = 1, + Runnable = 2, + Terminated = 3, + + SuspendShift = 4, + Mask = (1 << SuspendShift) - 1, + + ProcessSuspended = (1 << (0 + SuspendShift)), + ThreadSuspended = (1 << (1 + SuspendShift)), + DebugSuspended = (1 << (2 + SuspendShift)), + BacktraceSuspended = (1 << (3 + SuspendShift)), + InitSuspended = (1 << (4 + SuspendShift)), + + SuspendFlagMask = ((1 << 5) - 1) << SuspendShift, }; +DECLARE_ENUM_FLAG_OPERATORS(ThreadState); enum class ThreadWakeupReason { Signal, // The thread was woken up by WakeupAllWaitingThreads due to an object signal. @@ -97,13 +107,6 @@ enum class ThreadActivity : u32 { Paused = 1, }; -enum class ThreadSchedStatus : u32 { - None = 0, - Paused = 1, - Runnable = 2, - Exited = 3, -}; - enum class ThreadSchedFlags : u32 { ProcessPauseFlag = 1 << 4, ThreadPauseFlag = 1 << 5, @@ -111,13 +114,20 @@ enum class ThreadSchedFlags : u32 { KernelInitPauseFlag = 1 << 8, }; -enum class ThreadSchedMasks : u32 { - LowMask = 0x000f, - HighMask = 0xfff0, - ForcePauseMask = 0x0070, +enum class ThreadWaitReasonForDebugging : u32 { + None, ///< Thread is not waiting + Sleep, ///< Thread is waiting due to a SleepThread SVC + IPC, ///< Thread is waiting for the reply from an IPC request + Synchronization, ///< Thread is waiting due to a WaitSynchronization SVC + ConditionVar, ///< Thread is waiting due to a WaitProcessWideKey SVC + Arbitration, ///< Thread is waiting due to a SignalToAddress/WaitForAddress SVC + Suspended, ///< Thread is waiting due to process suspension }; -class Thread final : public SynchronizationObject { +class Thread final : public KSynchronizationObject, public boost::intrusive::list_base_hook<> { + friend class KScheduler; + friend class Process; + public: explicit Thread(KernelCore& kernel); ~Thread() override; @@ -127,10 +137,6 @@ public: using ThreadContext32 = Core::ARM_Interface::ThreadContext32; using ThreadContext64 = Core::ARM_Interface::ThreadContext64; - using ThreadSynchronizationObjects = std::vector<std::shared_ptr<SynchronizationObject>>; - - using HLECallback = std::function<bool(std::shared_ptr<Thread> thread)>; - /** * Creates and returns a new thread. The new thread is immediately scheduled * @param system The instance of the whole system @@ -186,59 +192,54 @@ public: return HANDLE_TYPE; } - bool ShouldWait(const Thread* thread) const override; - void Acquire(Thread* thread) override; - bool IsSignaled() const override; - /** * Gets the thread's current priority * @return The current thread's priority */ - u32 GetPriority() const { + [[nodiscard]] s32 GetPriority() const { 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. * @return The current thread's nominal priority. */ - u32 GetNominalPriority() const { - return nominal_priority; + [[nodiscard]] s32 GetBasePriority() const { + return base_priority; } /** - * Sets the thread's current priority - * @param priority The new priority + * Sets the thread's nominal priority. + * @param priority The new priority. */ - void SetPriority(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(); + void SetBasePriority(u32 priority); /// 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 * @return The thread's ID */ - u64 GetThreadID() const { + [[nodiscard]] u64 GetThreadID() const { return thread_id; } /// Resumes a thread from waiting - void ResumeFromWait(); - - void OnWakeUp(); + void Wakeup(); ResultCode Start(); + virtual bool IsSignaled() const override; + /// Cancels a waiting operation that this thread may or may not be within. /// /// When the thread is within a waiting state, this will set the thread's @@ -247,29 +248,20 @@ public: /// void CancelWait(); - void SetSynchronizationResults(SynchronizationObject* object, ResultCode result); + void SetSynchronizationResults(KSynchronizationObject* object, ResultCode result); - SynchronizationObject* GetSignalingObject() const { - return signaling_object; + void SetSyncedObject(KSynchronizationObject* object, ResultCode result) { + SetSynchronizationResults(object, result); } - ResultCode GetSignalingResult() const { + ResultCode GetWaitResult(KSynchronizationObject** out) const { + *out = signaling_object; return signaling_result; } - /** - * Retrieves the index that this particular object occupies in the list of objects - * that the thread passed to WaitSynchronization, starting the search from the last element. - * - * It is used to set the output index of WaitSynchronization when the thread is awakened. - * - * When a thread wakes up due to an object signal, the kernel will use the index of the last - * matching object in the wait objects list in case of having multiple instances of the same - * object in the list. - * - * @param object Object to query the index of. - */ - s32 GetSynchronizationObjectIndex(std::shared_ptr<SynchronizationObject> object) const; + ResultCode GetSignalingResult() const { + return signaling_result; + } /** * Stops a thread, invalidating it from further use @@ -341,18 +333,22 @@ public: std::shared_ptr<Common::Fiber>& GetHostContext(); - ThreadStatus GetStatus() const { - return status; + ThreadState GetState() const { + return thread_state & ThreadState::Mask; + } + + ThreadState GetRawState() const { + return thread_state; } - void SetStatus(ThreadStatus new_status); + void SetState(ThreadState state); s64 GetLastScheduledTick() const { - return this->last_scheduled_tick; + return last_scheduled_tick; } void SetLastScheduledTick(s64 tick) { - this->last_scheduled_tick = tick; + last_scheduled_tick = tick; } u64 GetTotalCPUTimeTicks() const { @@ -387,98 +383,18 @@ public: return owner_process; } - const ThreadSynchronizationObjects& GetSynchronizationObjects() const { - return *wait_objects; - } - - void SetSynchronizationObjects(ThreadSynchronizationObjects* objects) { - wait_objects = objects; - } - - void ClearSynchronizationObjects() { - for (const auto& waiting_object : *wait_objects) { - waiting_object->RemoveWaitingThread(SharedFrom(this)); - } - wait_objects->clear(); - } - - /// Determines whether all the objects this thread is waiting on are ready. - bool AllSynchronizationObjectsReady() const; - const MutexWaitingThreads& GetMutexWaitingThreads() const { return wait_mutex_threads; } Thread* GetLockOwner() const { - return lock_owner.get(); - } - - void SetLockOwner(std::shared_ptr<Thread> owner) { - lock_owner = std::move(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; - } - - bool HasHLECallback() const { - return hle_callback != nullptr; - } - - void SetHLECallback(HLECallback callback) { - hle_callback = std::move(callback); - } - - void SetHLETimeEvent(Handle time_event) { - hle_time_event = time_event; - } - - void SetHLESyncObject(SynchronizationObject* object) { - hle_object = object; - } - - Handle GetHLETimeEvent() const { - return hle_time_event; - } - - SynchronizationObject* GetHLESyncObject() const { - return hle_object; + return lock_owner; } - void InvalidateHLECallback() { - SetHLECallback(nullptr); + void SetLockOwner(Thread* owner) { + lock_owner = owner; } - bool InvokeHLECallback(std::shared_ptr<Thread> thread); - u32 GetIdealCore() const { return ideal_core; } @@ -493,20 +409,11 @@ public: ResultCode Sleep(s64 nanoseconds); s64 GetYieldScheduleCount() const { - return this->schedule_count; + return schedule_count; } void SetYieldScheduleCount(s64 count) { - this->schedule_count = count; - } - - ThreadSchedStatus GetSchedulingStatus() const { - return static_cast<ThreadSchedStatus>(scheduling_state & - static_cast<u32>(ThreadSchedMasks::LowMask)); - } - - bool IsRunnable() const { - return scheduling_state == static_cast<u32>(ThreadSchedStatus::Runnable); + schedule_count = count; } bool IsRunning() const { @@ -517,36 +424,32 @@ public: is_running = value; } - bool IsSyncCancelled() const { + bool IsWaitCancelled() const { return is_sync_cancelled; } - void SetSyncCancelled(bool value) { - is_sync_cancelled = value; + void ClearWaitCancelled() { + is_sync_cancelled = false; } Handle GetGlobalHandle() const { return global_handle; } - bool IsWaitingForArbitration() const { - return waiting_for_arbitration; + bool IsCancellable() const { + return is_cancellable; } - void WaitForArbitration(bool set) { - waiting_for_arbitration = set; + void SetCancellable() { + is_cancellable = true; } - bool IsWaitingSync() const { - return is_waiting_on_sync; + void ClearCancellable() { + is_cancellable = false; } - void SetWaitingSync(bool is_waiting) { - is_waiting_on_sync = is_waiting; - } - - bool IsPendingTermination() const { - return will_be_terminated || GetSchedulingStatus() == ThreadSchedStatus::Exited; + bool IsTerminationRequested() const { + return will_be_terminated || GetRawState() == ThreadState::Terminated; } bool IsPaused() const { @@ -578,21 +481,21 @@ public: constexpr QueueEntry() = default; constexpr void Initialize() { - this->prev = nullptr; - this->next = nullptr; + prev = nullptr; + next = nullptr; } constexpr Thread* GetPrev() const { - return this->prev; + return prev; } constexpr Thread* GetNext() const { - return this->next; + return next; } constexpr void SetPrev(Thread* thread) { - this->prev = thread; + prev = thread; } constexpr void SetNext(Thread* thread) { - this->next = thread; + next = thread; } private: @@ -601,11 +504,11 @@ public: }; 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 { - return this->per_core_priority_queue_entry[core]; + return per_core_priority_queue_entry[core]; } s32 GetDisableDispatchCount() const { @@ -622,24 +525,170 @@ public: disable_count--; } + void SetWaitReasonForDebugging(ThreadWaitReasonForDebugging reason) { + wait_reason_for_debugging = reason; + } + + [[nodiscard]] ThreadWaitReasonForDebugging GetWaitReasonForDebugging() const { + return wait_reason_for_debugging; + } + + void SetWaitObjectsForDebugging(const std::span<KSynchronizationObject*>& objects) { + wait_objects_for_debugging.clear(); + wait_objects_for_debugging.reserve(objects.size()); + for (const auto& object : objects) { + wait_objects_for_debugging.emplace_back(object); + } + } + + [[nodiscard]] const std::vector<KSynchronizationObject*>& GetWaitObjectsForDebugging() const { + return wait_objects_for_debugging; + } + + void SetMutexWaitAddressForDebugging(VAddr address) { + mutex_wait_address_for_debugging = address; + } + + [[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: - friend class GlobalSchedulerContext; - friend class KScheduler; - friend class Process; + 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 SetSchedulingStatus(ThreadSchedStatus new_status); + 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 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{}; ThreadContext32 context_32{}; ThreadContext64 context_64{}; std::shared_ptr<Common::Fiber> host_context{}; - ThreadStatus status = ThreadStatus::Dormant; - u32 scheduling_state = 0; + ThreadState thread_state = ThreadState::Initialized; u64 thread_id = 0; @@ -652,11 +701,11 @@ private: /// Nominal thread priority, as set by the emulated application. /// The nominal priority is the thread priority without priority /// inheritance taken into account. - u32 nominal_priority = 0; + s32 base_priority{}; /// Current thread priority. This may change over the course of the /// 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. s64 schedule_count{}; @@ -671,37 +720,27 @@ private: Process* owner_process; /// Objects that the thread is waiting on, in the same order as they were - /// passed to WaitSynchronization. - ThreadSynchronizationObjects* wait_objects; + /// passed to WaitSynchronization. This is used for debugging only. + std::vector<KSynchronizationObject*> wait_objects_for_debugging; - SynchronizationObject* signaling_object; + /// The current mutex wait address. This is used for debugging only. + VAddr mutex_wait_address_for_debugging{}; + + /// The reason the thread is waiting. This is used for debugging only. + ThreadWaitReasonForDebugging wait_reason_for_debugging{}; + + KSynchronizationObject* signaling_object; ResultCode signaling_result{RESULT_SUCCESS}; /// List of threads that are waiting for a mutex that is held by this thread. MutexWaitingThreads wait_mutex_threads; /// Thread that owns the lock that this thread is waiting for. - std::shared_ptr<Thread> lock_owner; - - /// If waiting on a ConditionVariable, this is the ConditionVariable address - VAddr condvar_wait_address = 0; - /// 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{}; + Thread* lock_owner{}; /// Handle used as userdata to reference this object when inserting into the CoreTiming queue. Handle global_handle = 0; - /// Callback for HLE Events - HLECallback hle_callback; - Handle hle_time_event; - SynchronizationObject* hle_object; - KScheduler* scheduler = nullptr; std::array<QueueEntry, Core::Hardware::NUM_CPU_CORES> per_core_priority_queue_entry{}; @@ -714,7 +753,7 @@ private: u32 pausing_state = 0; bool is_running = false; - bool is_waiting_on_sync = false; + bool is_cancellable = false; bool is_sync_cancelled = false; bool is_continuous_on_svc = false; @@ -725,6 +764,18 @@ private: bool was_running = false; + 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; }; diff --git a/src/core/hle/kernel/time_manager.cpp b/src/core/hle/kernel/time_manager.cpp index 79628e2b4..832edd629 100644 --- a/src/core/hle/kernel/time_manager.cpp +++ b/src/core/hle/kernel/time_manager.cpp @@ -18,12 +18,10 @@ TimeManager::TimeManager(Core::System& system_) : system{system_} { time_manager_event_type = Core::Timing::CreateEvent( "Kernel::TimeManagerCallback", [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::lock_guard lock{mutex}; + const auto proper_handle = static_cast<Handle>(thread_handle); if (cancelled_events[proper_handle]) { return; } @@ -32,7 +30,7 @@ TimeManager::TimeManager(Core::System& system_) : system{system_} { if (thread) { // Thread can be null if process has exited - thread->OnWakeUp(); + thread->Wakeup(); } }); } @@ -42,8 +40,7 @@ void TimeManager::ScheduleTimeEvent(Handle& event_handle, Thread* timetask, s64 event_handle = timetask->GetGlobalHandle(); if (nanoseconds > 0) { ASSERT(timetask); - ASSERT(timetask->GetStatus() != ThreadStatus::Ready); - ASSERT(timetask->GetStatus() != ThreadStatus::WaitMutex); + ASSERT(timetask->GetState() != ThreadState::Runnable); system.CoreTiming().ScheduleEvent(std::chrono::nanoseconds{nanoseconds}, time_manager_event_type, event_handle); } else { diff --git a/src/core/hle/service/nfp/nfp.cpp b/src/core/hle/service/nfp/nfp.cpp index 5557da72e..641bcadea 100644 --- a/src/core/hle/service/nfp/nfp.cpp +++ b/src/core/hle/service/nfp/nfp.cpp @@ -190,12 +190,6 @@ private: void GetDeviceState(Kernel::HLERequestContext& ctx) { LOG_DEBUG(Service_NFP, "called"); - auto nfc_event = nfp_interface.GetNFCEvent(); - if (!nfc_event->ShouldWait(&ctx.GetThread()) && !has_attached_handle) { - device_state = DeviceState::TagFound; - nfc_event->Clear(); - } - IPC::ResponseBuilder rb{ctx, 3}; rb.Push(RESULT_SUCCESS); rb.Push<u32>(static_cast<u32>(device_state)); diff --git a/src/core/hle/service/nvflinger/nvflinger.cpp b/src/core/hle/service/nvflinger/nvflinger.cpp index 4b3581949..ceaa93d28 100644 --- a/src/core/hle/service/nvflinger/nvflinger.cpp +++ b/src/core/hle/service/nvflinger/nvflinger.cpp @@ -38,6 +38,10 @@ void NVFlinger::SplitVSync() { system.RegisterHostThread(); std::string name = "yuzu:VSyncThread"; MicroProfileOnThreadCreate(name.c_str()); + + // Cleanup + SCOPE_EXIT({ MicroProfileOnThreadExit(); }); + Common::SetCurrentThreadName(name.c_str()); Common::SetCurrentThreadPriority(Common::ThreadPriority::High); s64 delay = 0; diff --git a/src/core/hle/service/sm/sm.cpp b/src/core/hle/service/sm/sm.cpp index 4da69f503..2b91a89d1 100644 --- a/src/core/hle/service/sm/sm.cpp +++ b/src/core/hle/service/sm/sm.cpp @@ -139,9 +139,6 @@ void SM::GetService(Kernel::HLERequestContext& ctx) { server_port->AppendPendingSession(server); } - // Wake the threads waiting on the ServerPort - server_port->Signal(); - LOG_DEBUG(Service_SM, "called service={} -> session={}", name, client->GetObjectId()); IPC::ResponseBuilder rb{ctx, 2, 0, 1, IPC::ResponseBuilder::Flags::AlwaysMoveHandles}; rb.Push(RESULT_SUCCESS); |