Kernel: Refactor synchronization to better match RE
This commit is contained in:
		
				
					committed by
					
						 FernandoS27
						FernandoS27
					
				
			
			
				
	
			
			
			
						parent
						
							c5aefe42aa
						
					
				
				
					commit
					d23d504d77
				
			| @@ -183,6 +183,8 @@ add_library(core STATIC | |||||||
|     hle/kernel/svc_wrap.h |     hle/kernel/svc_wrap.h | ||||||
|     hle/kernel/synchronization_object.cpp |     hle/kernel/synchronization_object.cpp | ||||||
|     hle/kernel/synchronization_object.h |     hle/kernel/synchronization_object.h | ||||||
|  |     hle/kernel/synchronization.cpp | ||||||
|  |     hle/kernel/synchronization.h | ||||||
|     hle/kernel/thread.cpp |     hle/kernel/thread.cpp | ||||||
|     hle/kernel/thread.h |     hle/kernel/thread.h | ||||||
|     hle/kernel/transfer_memory.cpp |     hle/kernel/transfer_memory.cpp | ||||||
|   | |||||||
| @@ -31,6 +31,11 @@ void ClientSession::Acquire(Thread* thread) { | |||||||
|     UNIMPLEMENTED(); |     UNIMPLEMENTED(); | ||||||
| } | } | ||||||
|  |  | ||||||
|  | bool ClientSession::IsSignaled() const { | ||||||
|  |     UNIMPLEMENTED(); | ||||||
|  |     return true; | ||||||
|  | } | ||||||
|  |  | ||||||
| ResultVal<std::shared_ptr<ClientSession>> ClientSession::Create(KernelCore& kernel, | ResultVal<std::shared_ptr<ClientSession>> ClientSession::Create(KernelCore& kernel, | ||||||
|                                                                 std::shared_ptr<Session> parent, |                                                                 std::shared_ptr<Session> parent, | ||||||
|                                                                 std::string name) { |                                                                 std::string name) { | ||||||
|   | |||||||
| @@ -48,6 +48,8 @@ public: | |||||||
|  |  | ||||||
|     void Acquire(Thread* thread) override; |     void Acquire(Thread* thread) override; | ||||||
|  |  | ||||||
|  |     bool IsSignaled() const override; | ||||||
|  |  | ||||||
| private: | private: | ||||||
|     static ResultVal<std::shared_ptr<ClientSession>> Create(KernelCore& kernel, |     static ResultVal<std::shared_ptr<ClientSession>> Create(KernelCore& kernel, | ||||||
|                                                             std::shared_ptr<Session> parent, |                                                             std::shared_ptr<Session> parent, | ||||||
|   | |||||||
| @@ -23,6 +23,7 @@ | |||||||
| #include "core/hle/kernel/process.h" | #include "core/hle/kernel/process.h" | ||||||
| #include "core/hle/kernel/resource_limit.h" | #include "core/hle/kernel/resource_limit.h" | ||||||
| #include "core/hle/kernel/scheduler.h" | #include "core/hle/kernel/scheduler.h" | ||||||
|  | #include "core/hle/kernel/synchronization.h" | ||||||
| #include "core/hle/kernel/thread.h" | #include "core/hle/kernel/thread.h" | ||||||
| #include "core/hle/lock.h" | #include "core/hle/lock.h" | ||||||
| #include "core/hle/result.h" | #include "core/hle/result.h" | ||||||
| @@ -96,7 +97,8 @@ static void ThreadWakeupCallback(u64 thread_handle, [[maybe_unused]] s64 cycles_ | |||||||
| } | } | ||||||
|  |  | ||||||
| struct KernelCore::Impl { | 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) { |     void Initialize(KernelCore& kernel) { | ||||||
|         Shutdown(); |         Shutdown(); | ||||||
| @@ -191,6 +193,7 @@ struct KernelCore::Impl { | |||||||
|     std::vector<std::shared_ptr<Process>> process_list; |     std::vector<std::shared_ptr<Process>> process_list; | ||||||
|     Process* current_process = nullptr; |     Process* current_process = nullptr; | ||||||
|     Kernel::GlobalScheduler global_scheduler; |     Kernel::GlobalScheduler global_scheduler; | ||||||
|  |     Kernel::Synchronization synchronization; | ||||||
|  |  | ||||||
|     std::shared_ptr<ResourceLimit> system_resource_limit; |     std::shared_ptr<ResourceLimit> system_resource_limit; | ||||||
|  |  | ||||||
| @@ -270,6 +273,14 @@ const Kernel::PhysicalCore& KernelCore::PhysicalCore(std::size_t id) const { | |||||||
|     return impl->cores[id]; |     return impl->cores[id]; | ||||||
| } | } | ||||||
|  |  | ||||||
|  | Kernel::Synchronization& KernelCore::Synchronization() { | ||||||
|  |     return impl->synchronization; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | const Kernel::Synchronization& KernelCore::Synchronization() const { | ||||||
|  |     return impl->synchronization; | ||||||
|  | } | ||||||
|  |  | ||||||
| Core::ExclusiveMonitor& KernelCore::GetExclusiveMonitor() { | Core::ExclusiveMonitor& KernelCore::GetExclusiveMonitor() { | ||||||
|     return *impl->exclusive_monitor; |     return *impl->exclusive_monitor; | ||||||
| } | } | ||||||
|   | |||||||
| @@ -29,6 +29,7 @@ class HandleTable; | |||||||
| class PhysicalCore; | class PhysicalCore; | ||||||
| class Process; | class Process; | ||||||
| class ResourceLimit; | class ResourceLimit; | ||||||
|  | class Synchronization; | ||||||
| class Thread; | class Thread; | ||||||
|  |  | ||||||
| /// Represents a single instance of the kernel. | /// Represents a single instance of the kernel. | ||||||
| @@ -92,6 +93,12 @@ public: | |||||||
|     /// Gets the an instance of the respective physical CPU core. |     /// Gets the an instance of the respective physical CPU core. | ||||||
|     const Kernel::PhysicalCore& PhysicalCore(std::size_t id) const; |     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. |     /// Stops execution of 'id' core, in order to reschedule a new thread. | ||||||
|     void PrepareReschedule(std::size_t id); |     void PrepareReschedule(std::size_t id); | ||||||
|  |  | ||||||
|   | |||||||
| @@ -357,7 +357,7 @@ void Process::ChangeStatus(ProcessStatus new_status) { | |||||||
|  |  | ||||||
|     status = new_status; |     status = new_status; | ||||||
|     is_signaled = true; |     is_signaled = true; | ||||||
|     WakeupAllWaitingThreads(); |     Signal(); | ||||||
| } | } | ||||||
|  |  | ||||||
| void Process::AllocateMainThreadStack(u64 stack_size) { | void Process::AllocateMainThreadStack(u64 stack_size) { | ||||||
|   | |||||||
| @@ -359,10 +359,6 @@ private: | |||||||
|     /// specified by metadata provided to the process during loading. |     /// specified by metadata provided to the process during loading. | ||||||
|     bool is_64bit_process = true; |     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. |     /// Total running time for the process in ticks. | ||||||
|     u64 total_process_running_time_ticks = 0; |     u64 total_process_running_time_ticks = 0; | ||||||
|  |  | ||||||
|   | |||||||
| @@ -15,26 +15,26 @@ ReadableEvent::ReadableEvent(KernelCore& kernel) : SynchronizationObject{kernel} | |||||||
| ReadableEvent::~ReadableEvent() = default; | ReadableEvent::~ReadableEvent() = default; | ||||||
|  |  | ||||||
| bool ReadableEvent::ShouldWait(const Thread* thread) const { | bool ReadableEvent::ShouldWait(const Thread* thread) const { | ||||||
|     return !signaled; |     return !is_signaled; | ||||||
| } | } | ||||||
|  |  | ||||||
| void ReadableEvent::Acquire(Thread* thread) { | void ReadableEvent::Acquire(Thread* thread) { | ||||||
|     ASSERT_MSG(!ShouldWait(thread), "object unavailable!"); |     ASSERT_MSG(IsSignaled(), "object unavailable!"); | ||||||
| } | } | ||||||
|  |  | ||||||
| void ReadableEvent::Signal() { | void ReadableEvent::Signal() { | ||||||
|     if (!signaled) { |     if (!is_signaled) { | ||||||
|         signaled = true; |         is_signaled = true; | ||||||
|         WakeupAllWaitingThreads(); |         SynchronizationObject::Signal(); | ||||||
|     }; |     }; | ||||||
| } | } | ||||||
|  |  | ||||||
| void ReadableEvent::Clear() { | void ReadableEvent::Clear() { | ||||||
|     signaled = false; |     is_signaled = false; | ||||||
| } | } | ||||||
|  |  | ||||||
| ResultCode ReadableEvent::Reset() { | ResultCode ReadableEvent::Reset() { | ||||||
|     if (!signaled) { |     if (!is_signaled) { | ||||||
|         return ERR_INVALID_STATE; |         return ERR_INVALID_STATE; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|   | |||||||
| @@ -46,13 +46,11 @@ public: | |||||||
|     ///      then ERR_INVALID_STATE will be returned. |     ///      then ERR_INVALID_STATE will be returned. | ||||||
|     ResultCode Reset(); |     ResultCode Reset(); | ||||||
|  |  | ||||||
|  |     void Signal() override; | ||||||
|  |  | ||||||
| private: | private: | ||||||
|     explicit ReadableEvent(KernelCore& kernel); |     explicit ReadableEvent(KernelCore& kernel); | ||||||
|  |  | ||||||
|     void Signal(); |  | ||||||
|  |  | ||||||
|     bool signaled{}; |  | ||||||
|  |  | ||||||
|     std::string name; ///< Name of event (optional) |     std::string name; ///< Name of event (optional) | ||||||
| }; | }; | ||||||
|  |  | ||||||
|   | |||||||
| @@ -39,6 +39,10 @@ void ServerPort::Acquire(Thread* thread) { | |||||||
|     ASSERT_MSG(!ShouldWait(thread), "object unavailable!"); |     ASSERT_MSG(!ShouldWait(thread), "object unavailable!"); | ||||||
| } | } | ||||||
|  |  | ||||||
|  | bool ServerPort::IsSignaled() const { | ||||||
|  |     return !pending_sessions.empty(); | ||||||
|  | } | ||||||
|  |  | ||||||
| ServerPort::PortPair ServerPort::CreatePortPair(KernelCore& kernel, u32 max_sessions, | ServerPort::PortPair ServerPort::CreatePortPair(KernelCore& kernel, u32 max_sessions, | ||||||
|                                                 std::string name) { |                                                 std::string name) { | ||||||
|     std::shared_ptr<ServerPort> server_port = std::make_shared<ServerPort>(kernel); |     std::shared_ptr<ServerPort> server_port = std::make_shared<ServerPort>(kernel); | ||||||
|   | |||||||
| @@ -82,6 +82,8 @@ public: | |||||||
|     bool ShouldWait(const Thread* thread) const override; |     bool ShouldWait(const Thread* thread) const override; | ||||||
|     void Acquire(Thread* thread) override; |     void Acquire(Thread* thread) override; | ||||||
|  |  | ||||||
|  |     bool IsSignaled() const override; | ||||||
|  |  | ||||||
| private: | private: | ||||||
|     /// ServerSessions waiting to be accepted by the port |     /// ServerSessions waiting to be accepted by the port | ||||||
|     std::vector<std::shared_ptr<ServerSession>> pending_sessions; |     std::vector<std::shared_ptr<ServerSession>> pending_sessions; | ||||||
|   | |||||||
| @@ -50,6 +50,16 @@ bool ServerSession::ShouldWait(const Thread* thread) const { | |||||||
|     return pending_requesting_threads.empty() || currently_handling != nullptr; |     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) { | void ServerSession::Acquire(Thread* thread) { | ||||||
|     ASSERT_MSG(!ShouldWait(thread), "object unavailable!"); |     ASSERT_MSG(!ShouldWait(thread), "object unavailable!"); | ||||||
|     // We are now handling a request, pop it from the stack. |     // We are now handling a request, pop it from the stack. | ||||||
|   | |||||||
| @@ -73,6 +73,8 @@ public: | |||||||
|         return parent.get(); |         return parent.get(); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     bool IsSignaled() const override; | ||||||
|  |  | ||||||
|     /** |     /** | ||||||
|      * Sets the HLE handler for the session. This handler will be called to service IPC requests |      * 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 |      * instead of the regular IPC machinery. (The regular IPC machinery is currently not | ||||||
|   | |||||||
| @@ -29,6 +29,11 @@ bool Session::ShouldWait(const Thread* thread) const { | |||||||
|     return {}; |     return {}; | ||||||
| } | } | ||||||
|  |  | ||||||
|  | bool Session::IsSignaled() const { | ||||||
|  |     UNIMPLEMENTED(); | ||||||
|  |     return true; | ||||||
|  | } | ||||||
|  |  | ||||||
| void Session::Acquire(Thread* thread) { | void Session::Acquire(Thread* thread) { | ||||||
|     UNIMPLEMENTED(); |     UNIMPLEMENTED(); | ||||||
| } | } | ||||||
|   | |||||||
| @@ -39,6 +39,8 @@ public: | |||||||
|  |  | ||||||
|     bool ShouldWait(const Thread* thread) const override; |     bool ShouldWait(const Thread* thread) const override; | ||||||
|  |  | ||||||
|  |     bool IsSignaled() const override; | ||||||
|  |  | ||||||
|     void Acquire(Thread* thread) override; |     void Acquire(Thread* thread) override; | ||||||
|  |  | ||||||
|     std::shared_ptr<ClientSession> Client() { |     std::shared_ptr<ClientSession> Client() { | ||||||
|   | |||||||
| @@ -32,6 +32,7 @@ | |||||||
| #include "core/hle/kernel/shared_memory.h" | #include "core/hle/kernel/shared_memory.h" | ||||||
| #include "core/hle/kernel/svc.h" | #include "core/hle/kernel/svc.h" | ||||||
| #include "core/hle/kernel/svc_wrap.h" | #include "core/hle/kernel/svc_wrap.h" | ||||||
|  | #include "core/hle/kernel/synchronization.h" | ||||||
| #include "core/hle/kernel/thread.h" | #include "core/hle/kernel/thread.h" | ||||||
| #include "core/hle/kernel/transfer_memory.h" | #include "core/hle/kernel/transfer_memory.h" | ||||||
| #include "core/hle/kernel/writable_event.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; |     return ERR_INVALID_HANDLE; | ||||||
| } | } | ||||||
|  |  | ||||||
| /// Default thread wakeup callback for WaitSynchronization |  | ||||||
| static bool DefaultThreadWakeupCallback(ThreadWakeupReason reason, std::shared_ptr<Thread> thread, |  | ||||||
|                                         std::shared_ptr<SynchronizationObject> 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<u32>(index)); |  | ||||||
|     return true; |  | ||||||
| }; |  | ||||||
|  |  | ||||||
| /// Wait for the given handles to synchronize, timeout after the specified nanoseconds | /// 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, Handle* index, VAddr handles_address, | ||||||
|                                       u64 handle_count, s64 nano_seconds) { |                                       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* const thread = system.CurrentScheduler().GetCurrentThread(); | ||||||
|  |     auto& kernel = system.Kernel(); | ||||||
|     using ObjectPtr = Thread::ThreadSynchronizationObjects::value_type; |     using ObjectPtr = Thread::ThreadSynchronizationObjects::value_type; | ||||||
|     Thread::ThreadSynchronizationObjects objects(handle_count); |     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) { |     for (u64 i = 0; i < handle_count; ++i) { | ||||||
|         const Handle handle = memory.Read32(handles_address + i * sizeof(Handle)); |         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; |         objects[i] = object; | ||||||
|     } |     } | ||||||
|  |     auto& synchronization = kernel.Synchronization(); | ||||||
|     // Find the first object that is acquirable in the provided list of objects |     auto [result, handle_result] = synchronization.WaitFor(objects, nano_seconds); | ||||||
|     auto itr = std::find_if(objects.begin(), objects.end(), [thread](const ObjectPtr& object) { |     *index = handle_result; | ||||||
|         return !object->ShouldWait(thread); |     return result; | ||||||
|     }); |  | ||||||
|  |  | ||||||
|     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<s32>(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; |  | ||||||
| } | } | ||||||
|  |  | ||||||
| /// Resumes a thread waiting on WaitSynchronization | /// Resumes a thread waiting on WaitSynchronization | ||||||
|   | |||||||
							
								
								
									
										86
									
								
								src/core/hle/kernel/synchronization.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										86
									
								
								src/core/hle/kernel/synchronization.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -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> thread, | ||||||
|  |                                         std::shared_ptr<SynchronizationObject> 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<u32>(index)); | ||||||
|  |     return true; | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | Synchronization::Synchronization(Core::System& system) : system{system} {} | ||||||
|  |  | ||||||
|  | void Synchronization::SignalObject(SynchronizationObject& obj) const { | ||||||
|  |     if (obj.IsSignaled()) { | ||||||
|  |         obj.WakeupAllWaitingThreads(); | ||||||
|  |     }; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | std::pair<ResultCode, Handle> Synchronization::WaitFor( | ||||||
|  |     std::vector<std::shared_ptr<SynchronizationObject>>& 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<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); | ||||||
|  |         u32 index = static_cast<s32>(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 | ||||||
							
								
								
									
										34
									
								
								src/core/hle/kernel/synchronization.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										34
									
								
								src/core/hle/kernel/synchronization.h
									
									
									
									
									
										Normal file
									
								
							| @@ -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 <memory> | ||||||
|  | #include <utility> | ||||||
|  |  | ||||||
|  | #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<ResultCode, Handle> WaitFor( | ||||||
|  |         std::vector<std::shared_ptr<SynchronizationObject>>& sync_objects, s64 nano_seconds); | ||||||
|  |  | ||||||
|  | private: | ||||||
|  |     Core::System& system; | ||||||
|  | }; | ||||||
|  | } // namespace Kernel | ||||||
| @@ -10,6 +10,7 @@ | |||||||
| #include "core/hle/kernel/kernel.h" | #include "core/hle/kernel/kernel.h" | ||||||
| #include "core/hle/kernel/object.h" | #include "core/hle/kernel/object.h" | ||||||
| #include "core/hle/kernel/process.h" | #include "core/hle/kernel/process.h" | ||||||
|  | #include "core/hle/kernel/synchronization.h" | ||||||
| #include "core/hle/kernel/synchronization_object.h" | #include "core/hle/kernel/synchronization_object.h" | ||||||
| #include "core/hle/kernel/thread.h" | #include "core/hle/kernel/thread.h" | ||||||
|  |  | ||||||
| @@ -18,6 +19,10 @@ namespace Kernel { | |||||||
| SynchronizationObject::SynchronizationObject(KernelCore& kernel) : Object{kernel} {} | SynchronizationObject::SynchronizationObject(KernelCore& kernel) : Object{kernel} {} | ||||||
| SynchronizationObject::~SynchronizationObject() = default; | SynchronizationObject::~SynchronizationObject() = default; | ||||||
|  |  | ||||||
|  | void SynchronizationObject::Signal() { | ||||||
|  |     kernel.Synchronization().SignalObject(*this); | ||||||
|  | } | ||||||
|  |  | ||||||
| void SynchronizationObject::AddWaitingThread(std::shared_ptr<Thread> thread) { | void SynchronizationObject::AddWaitingThread(std::shared_ptr<Thread> thread) { | ||||||
|     auto itr = std::find(waiting_threads.begin(), waiting_threads.end(), thread); |     auto itr = std::find(waiting_threads.begin(), waiting_threads.end(), thread); | ||||||
|     if (itr == waiting_threads.end()) |     if (itr == waiting_threads.end()) | ||||||
|   | |||||||
| @@ -30,6 +30,13 @@ public: | |||||||
|     /// Acquire/lock the object for the specified thread if it is available |     /// Acquire/lock the object for the specified thread if it is available | ||||||
|     virtual void Acquire(Thread* thread) = 0; |     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 |      * Add a thread to wait on this object | ||||||
|      * @param thread Pointer to thread to add |      * @param thread Pointer to thread to add | ||||||
| @@ -60,6 +67,9 @@ public: | |||||||
|     /// Get a const reference to the waiting threads list for debug use |     /// Get a const reference to the waiting threads list for debug use | ||||||
|     const std::vector<std::shared_ptr<Thread>>& GetWaitingThreads() const; |     const std::vector<std::shared_ptr<Thread>>& GetWaitingThreads() const; | ||||||
|  |  | ||||||
|  | protected: | ||||||
|  |     bool is_signaled{}; // Tells if this sync object is signalled; | ||||||
|  |  | ||||||
| private: | private: | ||||||
|     /// Threads waiting for this object to become available |     /// Threads waiting for this object to become available | ||||||
|     std::vector<std::shared_ptr<Thread>> waiting_threads; |     std::vector<std::shared_ptr<Thread>> waiting_threads; | ||||||
|   | |||||||
| @@ -31,6 +31,10 @@ bool Thread::ShouldWait(const Thread* thread) const { | |||||||
|     return status != ThreadStatus::Dead; |     return status != ThreadStatus::Dead; | ||||||
| } | } | ||||||
|  |  | ||||||
|  | bool Thread::IsSignaled() const { | ||||||
|  |     return status == ThreadStatus::Dead; | ||||||
|  | } | ||||||
|  |  | ||||||
| void Thread::Acquire(Thread* thread) { | void Thread::Acquire(Thread* thread) { | ||||||
|     ASSERT_MSG(!ShouldWait(thread), "object unavailable!"); |     ASSERT_MSG(!ShouldWait(thread), "object unavailable!"); | ||||||
| } | } | ||||||
| @@ -45,7 +49,7 @@ void Thread::Stop() { | |||||||
|     kernel.ThreadWakeupCallbackHandleTable().Close(callback_handle); |     kernel.ThreadWakeupCallbackHandleTable().Close(callback_handle); | ||||||
|     callback_handle = 0; |     callback_handle = 0; | ||||||
|     SetStatus(ThreadStatus::Dead); |     SetStatus(ThreadStatus::Dead); | ||||||
|     WakeupAllWaitingThreads(); |     Signal(); | ||||||
|  |  | ||||||
|     // Clean up any dangling references in objects that this thread was waiting for |     // Clean up any dangling references in objects that this thread was waiting for | ||||||
|     for (auto& wait_object : wait_objects) { |     for (auto& wait_object : wait_objects) { | ||||||
|   | |||||||
| @@ -146,6 +146,7 @@ public: | |||||||
|  |  | ||||||
|     bool ShouldWait(const Thread* thread) const override; |     bool ShouldWait(const Thread* thread) const override; | ||||||
|     void Acquire(Thread* thread) override; |     void Acquire(Thread* thread) override; | ||||||
|  |     bool IsSignaled() const override; | ||||||
|  |  | ||||||
|     /** |     /** | ||||||
|      * Gets the thread's current priority |      * Gets the thread's current priority | ||||||
|   | |||||||
| @@ -22,7 +22,6 @@ EventPair WritableEvent::CreateEventPair(KernelCore& kernel, std::string name) { | |||||||
|     writable_event->name = name + ":Writable"; |     writable_event->name = name + ":Writable"; | ||||||
|     writable_event->readable = readable_event; |     writable_event->readable = readable_event; | ||||||
|     readable_event->name = name + ":Readable"; |     readable_event->name = name + ":Readable"; | ||||||
|     readable_event->signaled = false; |  | ||||||
|  |  | ||||||
|     return {std::move(readable_event), std::move(writable_event)}; |     return {std::move(readable_event), std::move(writable_event)}; | ||||||
| } | } | ||||||
| @@ -40,7 +39,7 @@ void WritableEvent::Clear() { | |||||||
| } | } | ||||||
|  |  | ||||||
| bool WritableEvent::IsSignaled() const { | bool WritableEvent::IsSignaled() const { | ||||||
|     return readable->signaled; |     return readable->IsSignaled(); | ||||||
| } | } | ||||||
|  |  | ||||||
| } // namespace Kernel | } // namespace Kernel | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user