From d23d504d776007c1244a85ac1b7bb67c407067b2 Mon Sep 17 00:00:00 2001 From: Fernando Sahmkow Date: Tue, 11 Feb 2020 17:36:39 -0400 Subject: Kernel: Refactor synchronization to better match RE --- src/core/CMakeLists.txt | 2 + src/core/hle/kernel/client_session.cpp | 5 ++ src/core/hle/kernel/client_session.h | 2 + src/core/hle/kernel/kernel.cpp | 13 +++- src/core/hle/kernel/kernel.h | 7 +++ src/core/hle/kernel/process.cpp | 2 +- src/core/hle/kernel/process.h | 4 -- src/core/hle/kernel/readable_event.cpp | 14 ++--- src/core/hle/kernel/readable_event.h | 6 +- src/core/hle/kernel/server_port.cpp | 4 ++ src/core/hle/kernel/server_port.h | 2 + src/core/hle/kernel/server_session.cpp | 10 +++ src/core/hle/kernel/server_session.h | 2 + src/core/hle/kernel/session.cpp | 5 ++ src/core/hle/kernel/session.h | 2 + src/core/hle/kernel/svc.cpp | 67 +++----------------- src/core/hle/kernel/synchronization.cpp | 86 ++++++++++++++++++++++++++ src/core/hle/kernel/synchronization.h | 34 ++++++++++ src/core/hle/kernel/synchronization_object.cpp | 5 ++ src/core/hle/kernel/synchronization_object.h | 10 +++ src/core/hle/kernel/thread.cpp | 6 +- src/core/hle/kernel/thread.h | 1 + src/core/hle/kernel/writable_event.cpp | 3 +- 23 files changed, 212 insertions(+), 80 deletions(-) create mode 100644 src/core/hle/kernel/synchronization.cpp create mode 100644 src/core/hle/kernel/synchronization.h (limited to 'src/core') diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index 052907f08..26612e692 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -183,6 +183,8 @@ add_library(core STATIC hle/kernel/svc_wrap.h hle/kernel/synchronization_object.cpp hle/kernel/synchronization_object.h + hle/kernel/synchronization.cpp + hle/kernel/synchronization.h hle/kernel/thread.cpp hle/kernel/thread.h hle/kernel/transfer_memory.cpp diff --git a/src/core/hle/kernel/client_session.cpp b/src/core/hle/kernel/client_session.cpp index 3dfeb9813..6d66276bc 100644 --- a/src/core/hle/kernel/client_session.cpp +++ b/src/core/hle/kernel/client_session.cpp @@ -31,6 +31,11 @@ void ClientSession::Acquire(Thread* thread) { UNIMPLEMENTED(); } +bool ClientSession::IsSignaled() const { + UNIMPLEMENTED(); + return true; +} + ResultVal> ClientSession::Create(KernelCore& kernel, std::shared_ptr parent, std::string name) { diff --git a/src/core/hle/kernel/client_session.h b/src/core/hle/kernel/client_session.h index 9cf9219b1..d15b09554 100644 --- a/src/core/hle/kernel/client_session.h +++ b/src/core/hle/kernel/client_session.h @@ -48,6 +48,8 @@ public: void Acquire(Thread* thread) override; + bool IsSignaled() const override; + private: static ResultVal> Create(KernelCore& kernel, std::shared_ptr parent, diff --git a/src/core/hle/kernel/kernel.cpp b/src/core/hle/kernel/kernel.cpp index 26799f6b5..4eb1d8703 100644 --- a/src/core/hle/kernel/kernel.cpp +++ b/src/core/hle/kernel/kernel.cpp @@ -23,6 +23,7 @@ #include "core/hle/kernel/process.h" #include "core/hle/kernel/resource_limit.h" #include "core/hle/kernel/scheduler.h" +#include "core/hle/kernel/synchronization.h" #include "core/hle/kernel/thread.h" #include "core/hle/lock.h" #include "core/hle/result.h" @@ -96,7 +97,8 @@ static void ThreadWakeupCallback(u64 thread_handle, [[maybe_unused]] s64 cycles_ } struct KernelCore::Impl { - explicit Impl(Core::System& system) : system{system}, global_scheduler{system} {} + explicit Impl(Core::System& system) + : system{system}, global_scheduler{system}, synchronization{system} {} void Initialize(KernelCore& kernel) { Shutdown(); @@ -191,6 +193,7 @@ struct KernelCore::Impl { std::vector> process_list; Process* current_process = nullptr; Kernel::GlobalScheduler global_scheduler; + Kernel::Synchronization synchronization; std::shared_ptr system_resource_limit; @@ -270,6 +273,14 @@ const Kernel::PhysicalCore& KernelCore::PhysicalCore(std::size_t id) const { return impl->cores[id]; } +Kernel::Synchronization& KernelCore::Synchronization() { + return impl->synchronization; +} + +const Kernel::Synchronization& KernelCore::Synchronization() const { + return impl->synchronization; +} + Core::ExclusiveMonitor& KernelCore::GetExclusiveMonitor() { return *impl->exclusive_monitor; } diff --git a/src/core/hle/kernel/kernel.h b/src/core/hle/kernel/kernel.h index fccffaf3a..1eede3063 100644 --- a/src/core/hle/kernel/kernel.h +++ b/src/core/hle/kernel/kernel.h @@ -29,6 +29,7 @@ class HandleTable; class PhysicalCore; class Process; class ResourceLimit; +class Synchronization; class Thread; /// Represents a single instance of the kernel. @@ -92,6 +93,12 @@ public: /// Gets the an instance of the respective physical CPU core. const Kernel::PhysicalCore& PhysicalCore(std::size_t id) 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; + /// Stops execution of 'id' core, in order to reschedule a new thread. void PrepareReschedule(std::size_t id); diff --git a/src/core/hle/kernel/process.cpp b/src/core/hle/kernel/process.cpp index 7a616435a..2fcb7326c 100644 --- a/src/core/hle/kernel/process.cpp +++ b/src/core/hle/kernel/process.cpp @@ -357,7 +357,7 @@ void Process::ChangeStatus(ProcessStatus new_status) { status = new_status; is_signaled = true; - WakeupAllWaitingThreads(); + Signal(); } void Process::AllocateMainThreadStack(u64 stack_size) { diff --git a/src/core/hle/kernel/process.h b/src/core/hle/kernel/process.h index 7b64c564a..4887132a7 100644 --- a/src/core/hle/kernel/process.h +++ b/src/core/hle/kernel/process.h @@ -359,10 +359,6 @@ private: /// specified by metadata provided to the process during loading. bool is_64bit_process = true; - /// Whether or not this process is signaled. This occurs - /// upon the process changing to a different state. - bool is_signaled = false; - /// Total running time for the process in ticks. u64 total_process_running_time_ticks = 0; diff --git a/src/core/hle/kernel/readable_event.cpp b/src/core/hle/kernel/readable_event.cpp index 8ab796ba8..9d3d3a81b 100644 --- a/src/core/hle/kernel/readable_event.cpp +++ b/src/core/hle/kernel/readable_event.cpp @@ -15,26 +15,26 @@ ReadableEvent::ReadableEvent(KernelCore& kernel) : SynchronizationObject{kernel} ReadableEvent::~ReadableEvent() = default; bool ReadableEvent::ShouldWait(const Thread* thread) const { - return !signaled; + return !is_signaled; } void ReadableEvent::Acquire(Thread* thread) { - ASSERT_MSG(!ShouldWait(thread), "object unavailable!"); + ASSERT_MSG(IsSignaled(), "object unavailable!"); } void ReadableEvent::Signal() { - if (!signaled) { - signaled = true; - WakeupAllWaitingThreads(); + if (!is_signaled) { + is_signaled = true; + SynchronizationObject::Signal(); }; } void ReadableEvent::Clear() { - signaled = false; + is_signaled = false; } ResultCode ReadableEvent::Reset() { - if (!signaled) { + if (!is_signaled) { return ERR_INVALID_STATE; } diff --git a/src/core/hle/kernel/readable_event.h b/src/core/hle/kernel/readable_event.h index c7b0d6add..3264dd066 100644 --- a/src/core/hle/kernel/readable_event.h +++ b/src/core/hle/kernel/readable_event.h @@ -46,13 +46,11 @@ public: /// then ERR_INVALID_STATE will be returned. ResultCode Reset(); + void Signal() override; + private: explicit ReadableEvent(KernelCore& kernel); - void Signal(); - - bool 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 4f02f8df2..a549ae9d7 100644 --- a/src/core/hle/kernel/server_port.cpp +++ b/src/core/hle/kernel/server_port.cpp @@ -39,6 +39,10 @@ void ServerPort::Acquire(Thread* thread) { ASSERT_MSG(!ShouldWait(thread), "object unavailable!"); } +bool ServerPort::IsSignaled() const { + return !pending_sessions.empty(); +} + ServerPort::PortPair ServerPort::CreatePortPair(KernelCore& kernel, u32 max_sessions, std::string name) { std::shared_ptr server_port = std::make_shared(kernel); diff --git a/src/core/hle/kernel/server_port.h b/src/core/hle/kernel/server_port.h index 43cf3ae18..41b191b86 100644 --- a/src/core/hle/kernel/server_port.h +++ b/src/core/hle/kernel/server_port.h @@ -82,6 +82,8 @@ public: bool ShouldWait(const Thread* thread) const override; void Acquire(Thread* thread) override; + bool IsSignaled() const override; + private: /// ServerSessions waiting to be accepted by the port std::vector> pending_sessions; diff --git a/src/core/hle/kernel/server_session.cpp b/src/core/hle/kernel/server_session.cpp index 8207f71c3..ca98fd984 100644 --- a/src/core/hle/kernel/server_session.cpp +++ b/src/core/hle/kernel/server_session.cpp @@ -50,6 +50,16 @@ bool ServerSession::ShouldWait(const Thread* thread) const { 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()) { + return true; + } + + // Wait if we have no pending requests, or if we're currently handling a request. + 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. diff --git a/src/core/hle/kernel/server_session.h b/src/core/hle/kernel/server_session.h index 3688c7d11..77e4f6721 100644 --- a/src/core/hle/kernel/server_session.h +++ b/src/core/hle/kernel/server_session.h @@ -73,6 +73,8 @@ 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 diff --git a/src/core/hle/kernel/session.cpp b/src/core/hle/kernel/session.cpp index 1c1fc440d..e4dd53e24 100644 --- a/src/core/hle/kernel/session.cpp +++ b/src/core/hle/kernel/session.cpp @@ -29,6 +29,11 @@ bool Session::ShouldWait(const Thread* thread) const { return {}; } +bool Session::IsSignaled() const { + UNIMPLEMENTED(); + return true; +} + void Session::Acquire(Thread* thread) { UNIMPLEMENTED(); } diff --git a/src/core/hle/kernel/session.h b/src/core/hle/kernel/session.h index d107dd9aa..7cd9c0d77 100644 --- a/src/core/hle/kernel/session.h +++ b/src/core/hle/kernel/session.h @@ -39,6 +39,8 @@ public: bool ShouldWait(const Thread* thread) const override; + bool IsSignaled() const override; + void Acquire(Thread* thread) override; std::shared_ptr Client() { diff --git a/src/core/hle/kernel/svc.cpp b/src/core/hle/kernel/svc.cpp index 39552a176..86c660cdf 100644 --- a/src/core/hle/kernel/svc.cpp +++ b/src/core/hle/kernel/svc.cpp @@ -32,6 +32,7 @@ #include "core/hle/kernel/shared_memory.h" #include "core/hle/kernel/svc.h" #include "core/hle/kernel/svc_wrap.h" +#include "core/hle/kernel/synchronization.h" #include "core/hle/kernel/thread.h" #include "core/hle/kernel/transfer_memory.h" #include "core/hle/kernel/writable_event.h" @@ -433,23 +434,6 @@ static ResultCode GetProcessId(Core::System& system, u64* process_id, Handle han return ERR_INVALID_HANDLE; } -/// Default thread wakeup callback for WaitSynchronization -static bool DefaultThreadWakeupCallback(ThreadWakeupReason reason, std::shared_ptr thread, - std::shared_ptr object, - std::size_t index) { - ASSERT(thread->GetStatus() == ThreadStatus::WaitSynch); - - if (reason == ThreadWakeupReason::Timeout) { - thread->SetWaitSynchronizationResult(RESULT_TIMEOUT); - return true; - } - - ASSERT(reason == ThreadWakeupReason::Signal); - thread->SetWaitSynchronizationResult(RESULT_SUCCESS); - thread->SetWaitSynchronizationOutput(static_cast(index)); - return true; -}; - /// Wait for the given handles to synchronize, timeout after the specified nanoseconds static ResultCode WaitSynchronization(Core::System& system, Handle* index, VAddr handles_address, u64 handle_count, s64 nano_seconds) { @@ -473,10 +457,10 @@ static ResultCode WaitSynchronization(Core::System& system, Handle* index, VAddr } auto* const thread = system.CurrentScheduler().GetCurrentThread(); - + auto& kernel = system.Kernel(); using ObjectPtr = Thread::ThreadSynchronizationObjects::value_type; Thread::ThreadSynchronizationObjects objects(handle_count); - const auto& handle_table = system.Kernel().CurrentProcess()->GetHandleTable(); + 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)); @@ -489,47 +473,10 @@ static ResultCode WaitSynchronization(Core::System& system, Handle* index, VAddr objects[i] = object; } - - // Find the first object that is acquirable in the provided list of objects - auto itr = std::find_if(objects.begin(), objects.end(), [thread](const ObjectPtr& object) { - return !object->ShouldWait(thread); - }); - - if (itr != objects.end()) { - // We found a ready object, acquire it and set the result value - SynchronizationObject* object = itr->get(); - object->Acquire(thread); - *index = static_cast(std::distance(objects.begin(), itr)); - return RESULT_SUCCESS; - } - - // No objects were ready to be acquired, prepare to suspend the thread. - - // If a timeout value of 0 was provided, just return the Timeout error code instead of - // suspending the thread. - if (nano_seconds == 0) { - return RESULT_TIMEOUT; - } - - if (thread->IsSyncCancelled()) { - thread->SetSyncCancelled(false); - return ERR_SYNCHRONIZATION_CANCELED; - } - - for (auto& object : objects) { - object->AddWaitingThread(SharedFrom(thread)); - } - - thread->SetSynchronizationObjects(std::move(objects)); - thread->SetStatus(ThreadStatus::WaitSynch); - - // Create an event to wake the thread up after the specified nanosecond delay has passed - thread->WakeAfterDelay(nano_seconds); - thread->SetWakeupCallback(DefaultThreadWakeupCallback); - - system.PrepareReschedule(thread->GetProcessorID()); - - return RESULT_TIMEOUT; + auto& synchronization = kernel.Synchronization(); + auto [result, handle_result] = synchronization.WaitFor(objects, nano_seconds); + *index = handle_result; + return result; } /// Resumes a thread waiting on WaitSynchronization diff --git a/src/core/hle/kernel/synchronization.cpp b/src/core/hle/kernel/synchronization.cpp new file mode 100644 index 000000000..25afc162f --- /dev/null +++ b/src/core/hle/kernel/synchronization.cpp @@ -0,0 +1,86 @@ +// 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/kernel.h" +#include "core/hle/kernel/scheduler.h" +#include "core/hle/kernel/synchronization.h" +#include "core/hle/kernel/synchronization_object.h" +#include "core/hle/kernel/thread.h" + +namespace Kernel { + +/// Default thread wakeup callback for WaitSynchronization +static bool DefaultThreadWakeupCallback(ThreadWakeupReason reason, std::shared_ptr thread, + std::shared_ptr object, + std::size_t index) { + ASSERT(thread->GetStatus() == ThreadStatus::WaitSynch); + + if (reason == ThreadWakeupReason::Timeout) { + thread->SetWaitSynchronizationResult(RESULT_TIMEOUT); + return true; + } + + ASSERT(reason == ThreadWakeupReason::Signal); + thread->SetWaitSynchronizationResult(RESULT_SUCCESS); + thread->SetWaitSynchronizationOutput(static_cast(index)); + return true; +}; + +Synchronization::Synchronization(Core::System& system) : system{system} {} + +void Synchronization::SignalObject(SynchronizationObject& obj) const { + if (obj.IsSignaled()) { + obj.WakeupAllWaitingThreads(); + }; +} + +std::pair Synchronization::WaitFor( + std::vector>& sync_objects, s64 nano_seconds) { + auto* const thread = system.CurrentScheduler().GetCurrentThread(); + // Find the first object that is acquirable in the provided list of objects + auto itr = std::find_if(sync_objects.begin(), sync_objects.end(), + [thread](const std::shared_ptr& 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); + u32 index = static_cast(std::distance(sync_objects.begin(), itr)); + return {RESULT_SUCCESS, index}; + } + + // No objects were ready to be acquired, prepare to suspend the thread. + + // If a timeout value of 0 was provided, just return the Timeout error code instead of + // suspending the thread. + if (nano_seconds == 0) { + return {RESULT_TIMEOUT, 0}; + } + + if (thread->IsSyncCancelled()) { + thread->SetSyncCancelled(false); + return {ERR_SYNCHRONIZATION_CANCELED, 0}; + } + + for (auto& object : sync_objects) { + object->AddWaitingThread(SharedFrom(thread)); + } + + thread->SetSynchronizationObjects(std::move(sync_objects)); + thread->SetStatus(ThreadStatus::WaitSynch); + + // Create an event to wake the thread up after the specified nanosecond delay has passed + thread->WakeAfterDelay(nano_seconds); + thread->SetWakeupCallback(DefaultThreadWakeupCallback); + + system.PrepareReschedule(thread->GetProcessorID()); + + return {RESULT_TIMEOUT, 0}; +} + +} // namespace Kernel diff --git a/src/core/hle/kernel/synchronization.h b/src/core/hle/kernel/synchronization.h new file mode 100644 index 000000000..3417a9f13 --- /dev/null +++ b/src/core/hle/kernel/synchronization.h @@ -0,0 +1,34 @@ +// Copyright 2020 yuzu Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include +#include + +#include "core/hle/kernel/object.h" +#include "core/hle/result.h" + +namespace Core { +class System; +} // namespace Core + +namespace Kernel { + +class KernelCore; +class SynchronizationObject; + +class Synchronization { +public: + Synchronization(Core::System& system); + + void SignalObject(SynchronizationObject& obj) const; + + std::pair WaitFor( + std::vector>& 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 index 95f3f9245..43f3eef18 100644 --- a/src/core/hle/kernel/synchronization_object.cpp +++ b/src/core/hle/kernel/synchronization_object.cpp @@ -10,6 +10,7 @@ #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" @@ -18,6 +19,10 @@ 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) { auto itr = std::find(waiting_threads.begin(), waiting_threads.end(), thread); if (itr == waiting_threads.end()) diff --git a/src/core/hle/kernel/synchronization_object.h b/src/core/hle/kernel/synchronization_object.h index a0f891c97..741c31faf 100644 --- a/src/core/hle/kernel/synchronization_object.h +++ b/src/core/hle/kernel/synchronization_object.h @@ -30,6 +30,13 @@ public: /// 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 @@ -60,6 +67,9 @@ public: /// Get a const reference to the waiting threads list for debug use const std::vector>& GetWaitingThreads() const; +protected: + bool is_signaled{}; // Tells if this sync object is signalled; + private: /// Threads waiting for this object to become available std::vector> waiting_threads; diff --git a/src/core/hle/kernel/thread.cpp b/src/core/hle/kernel/thread.cpp index 0f096ed6d..ee9ea7d67 100644 --- a/src/core/hle/kernel/thread.cpp +++ b/src/core/hle/kernel/thread.cpp @@ -31,6 +31,10 @@ 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!"); } @@ -45,7 +49,7 @@ void Thread::Stop() { kernel.ThreadWakeupCallbackHandleTable().Close(callback_handle); callback_handle = 0; SetStatus(ThreadStatus::Dead); - WakeupAllWaitingThreads(); + Signal(); // Clean up any dangling references in objects that this thread was waiting for for (auto& wait_object : wait_objects) { diff --git a/src/core/hle/kernel/thread.h b/src/core/hle/kernel/thread.h index 895258095..7a4916318 100644 --- a/src/core/hle/kernel/thread.h +++ b/src/core/hle/kernel/thread.h @@ -146,6 +146,7 @@ public: bool ShouldWait(const Thread* thread) const override; void Acquire(Thread* thread) override; + bool IsSignaled() const override; /** * Gets the thread's current priority diff --git a/src/core/hle/kernel/writable_event.cpp b/src/core/hle/kernel/writable_event.cpp index c9332e3e1..fc2f7c424 100644 --- a/src/core/hle/kernel/writable_event.cpp +++ b/src/core/hle/kernel/writable_event.cpp @@ -22,7 +22,6 @@ EventPair WritableEvent::CreateEventPair(KernelCore& kernel, std::string name) { writable_event->name = name + ":Writable"; writable_event->readable = readable_event; readable_event->name = name + ":Readable"; - readable_event->signaled = false; return {std::move(readable_event), std::move(writable_event)}; } @@ -40,7 +39,7 @@ void WritableEvent::Clear() { } bool WritableEvent::IsSignaled() const { - return readable->signaled; + return readable->IsSignaled(); } } // namespace Kernel -- cgit v1.2.3