Merge pull request #12949 from liamwhite/multi-wait
service: add os types and multi wait API
This commit is contained in:
		| @@ -548,8 +548,6 @@ add_library(core STATIC | |||||||
|     hle/service/es/es.h |     hle/service/es/es.h | ||||||
|     hle/service/eupld/eupld.cpp |     hle/service/eupld/eupld.cpp | ||||||
|     hle/service/eupld/eupld.h |     hle/service/eupld/eupld.h | ||||||
|     hle/service/event.cpp |  | ||||||
|     hle/service/event.h |  | ||||||
|     hle/service/fatal/fatal.cpp |     hle/service/fatal/fatal.cpp | ||||||
|     hle/service/fatal/fatal.h |     hle/service/fatal/fatal.h | ||||||
|     hle/service/fatal/fatal_p.cpp |     hle/service/fatal/fatal_p.cpp | ||||||
| @@ -676,8 +674,6 @@ add_library(core STATIC | |||||||
|     hle/service/mm/mm_u.h |     hle/service/mm/mm_u.h | ||||||
|     hle/service/mnpp/mnpp_app.cpp |     hle/service/mnpp/mnpp_app.cpp | ||||||
|     hle/service/mnpp/mnpp_app.h |     hle/service/mnpp/mnpp_app.h | ||||||
|     hle/service/mutex.cpp |  | ||||||
|     hle/service/mutex.h |  | ||||||
|     hle/service/ncm/ncm.cpp |     hle/service/ncm/ncm.cpp | ||||||
|     hle/service/ncm/ncm.h |     hle/service/ncm/ncm.h | ||||||
|     hle/service/nfc/common/amiibo_crypto.cpp |     hle/service/nfc/common/amiibo_crypto.cpp | ||||||
| @@ -790,6 +786,15 @@ add_library(core STATIC | |||||||
|     hle/service/nvnflinger/window.h |     hle/service/nvnflinger/window.h | ||||||
|     hle/service/olsc/olsc.cpp |     hle/service/olsc/olsc.cpp | ||||||
|     hle/service/olsc/olsc.h |     hle/service/olsc/olsc.h | ||||||
|  |     hle/service/os/event.cpp | ||||||
|  |     hle/service/os/event.h | ||||||
|  |     hle/service/os/multi_wait_holder.cpp | ||||||
|  |     hle/service/os/multi_wait_holder.h | ||||||
|  |     hle/service/os/multi_wait_utils.h | ||||||
|  |     hle/service/os/multi_wait.cpp | ||||||
|  |     hle/service/os/multi_wait.h | ||||||
|  |     hle/service/os/mutex.cpp | ||||||
|  |     hle/service/os/mutex.h | ||||||
|     hle/service/pcie/pcie.cpp |     hle/service/pcie/pcie.cpp | ||||||
|     hle/service/pcie/pcie.h |     hle/service/pcie/pcie.h | ||||||
|     hle/service/pctl/pctl.cpp |     hle/service/pctl/pctl.cpp | ||||||
|   | |||||||
| @@ -9,8 +9,8 @@ | |||||||
| #include "common/math_util.h" | #include "common/math_util.h" | ||||||
| #include "core/hle/service/apm/apm_controller.h" | #include "core/hle/service/apm/apm_controller.h" | ||||||
| #include "core/hle/service/caps/caps_types.h" | #include "core/hle/service/caps/caps_types.h" | ||||||
| #include "core/hle/service/event.h" |  | ||||||
| #include "core/hle/service/kernel_helpers.h" | #include "core/hle/service/kernel_helpers.h" | ||||||
|  | #include "core/hle/service/os/event.h" | ||||||
| #include "core/hle/service/service.h" | #include "core/hle/service/service.h" | ||||||
|  |  | ||||||
| #include "core/hle/service/am/am_types.h" | #include "core/hle/service/am/am_types.h" | ||||||
|   | |||||||
| @@ -7,8 +7,8 @@ | |||||||
| #include <memory> | #include <memory> | ||||||
| #include <mutex> | #include <mutex> | ||||||
|  |  | ||||||
| #include "core/hle/service/event.h" |  | ||||||
| #include "core/hle/service/kernel_helpers.h" | #include "core/hle/service/kernel_helpers.h" | ||||||
|  | #include "core/hle/service/os/event.h" | ||||||
|  |  | ||||||
| union Result; | union Result; | ||||||
|  |  | ||||||
|   | |||||||
| @@ -7,6 +7,7 @@ | |||||||
| #include "core/hle/service/glue/time/file_timestamp_worker.h" | #include "core/hle/service/glue/time/file_timestamp_worker.h" | ||||||
| #include "core/hle/service/glue/time/standard_steady_clock_resource.h" | #include "core/hle/service/glue/time/standard_steady_clock_resource.h" | ||||||
| #include "core/hle/service/glue/time/worker.h" | #include "core/hle/service/glue/time/worker.h" | ||||||
|  | #include "core/hle/service/os/multi_wait_utils.h" | ||||||
| #include "core/hle/service/psc/time/common.h" | #include "core/hle/service/psc/time/common.h" | ||||||
| #include "core/hle/service/psc/time/service_manager.h" | #include "core/hle/service/psc/time/service_manager.h" | ||||||
| #include "core/hle/service/psc/time/static.h" | #include "core/hle/service/psc/time/static.h" | ||||||
| @@ -143,82 +144,46 @@ void TimeWorker::ThreadFunc(std::stop_token stop_token) { | |||||||
|     Common::SetCurrentThreadName("TimeWorker"); |     Common::SetCurrentThreadName("TimeWorker"); | ||||||
|     Common::SetCurrentThreadPriority(Common::ThreadPriority::Low); |     Common::SetCurrentThreadPriority(Common::ThreadPriority::Low); | ||||||
|  |  | ||||||
|     enum class EventType { |  | ||||||
|         Exit = 0, |  | ||||||
|         IpmModuleService_GetEvent = 1, |  | ||||||
|         PowerStateChange = 2, |  | ||||||
|         SignalAlarms = 3, |  | ||||||
|         UpdateLocalSystemClock = 4, |  | ||||||
|         UpdateNetworkSystemClock = 5, |  | ||||||
|         UpdateEphemeralSystemClock = 6, |  | ||||||
|         UpdateSteadyClock = 7, |  | ||||||
|         UpdateFileTimestamp = 8, |  | ||||||
|         AutoCorrect = 9, |  | ||||||
|         Max = 10, |  | ||||||
|     }; |  | ||||||
|  |  | ||||||
|     s32 num_objs{}; |  | ||||||
|     std::array<Kernel::KSynchronizationObject*, static_cast<u32>(EventType::Max)> wait_objs{}; |  | ||||||
|     std::array<EventType, static_cast<u32>(EventType::Max)> wait_indices{}; |  | ||||||
|  |  | ||||||
|     const auto AddWaiter{ |  | ||||||
|         [&](Kernel::KSynchronizationObject* synchronization_object, EventType type) { |  | ||||||
|             // Open a new reference to the object. |  | ||||||
|             synchronization_object->Open(); |  | ||||||
|  |  | ||||||
|             // Insert into the list. |  | ||||||
|             wait_indices[num_objs] = type; |  | ||||||
|             wait_objs[num_objs++] = synchronization_object; |  | ||||||
|         }}; |  | ||||||
|  |  | ||||||
|     while (!stop_token.stop_requested()) { |     while (!stop_token.stop_requested()) { | ||||||
|         SCOPE_EXIT({ |         enum class EventType : s32 { | ||||||
|             for (s32 i = 0; i < num_objs; i++) { |             Exit = 0, | ||||||
|                 wait_objs[i]->Close(); |             PowerStateChange = 1, | ||||||
|             } |             SignalAlarms = 2, | ||||||
|         }); |             UpdateLocalSystemClock = 3, | ||||||
|  |             UpdateNetworkSystemClock = 4, | ||||||
|  |             UpdateEphemeralSystemClock = 5, | ||||||
|  |             UpdateSteadyClock = 6, | ||||||
|  |             UpdateFileTimestamp = 7, | ||||||
|  |             AutoCorrect = 8, | ||||||
|  |         }; | ||||||
|  |  | ||||||
|  |         s32 index{}; | ||||||
|  |  | ||||||
|         num_objs = {}; |  | ||||||
|         wait_objs = {}; |  | ||||||
|         if (m_pm_state_change_handler.m_priority != 0) { |         if (m_pm_state_change_handler.m_priority != 0) { | ||||||
|             AddWaiter(&m_event->GetReadableEvent(), EventType::Exit); |             // TODO: gIPmModuleService::GetEvent() 1 | ||||||
|             // TODO |             index = WaitAny(m_system.Kernel(), | ||||||
|             // AddWaiter(gIPmModuleService::GetEvent(), 1); |                             &m_event->GetReadableEvent(), // 0 | ||||||
|             AddWaiter(&m_alarm_worker.GetEvent(), EventType::PowerStateChange); |                             &m_alarm_worker.GetEvent()    // 1 | ||||||
|  |             ); | ||||||
|         } else { |         } else { | ||||||
|             AddWaiter(&m_event->GetReadableEvent(), EventType::Exit); |             // TODO: gIPmModuleService::GetEvent() 1 | ||||||
|             // TODO |             index = WaitAny(m_system.Kernel(), | ||||||
|             // AddWaiter(gIPmModuleService::GetEvent(), 1); |                             &m_event->GetReadableEvent(),                       // 0 | ||||||
|             AddWaiter(&m_alarm_worker.GetEvent(), EventType::PowerStateChange); |                             &m_alarm_worker.GetEvent(),                         // 1 | ||||||
|             AddWaiter(&m_alarm_worker.GetTimerEvent().GetReadableEvent(), EventType::SignalAlarms); |                             &m_alarm_worker.GetTimerEvent().GetReadableEvent(), // 2 | ||||||
|             AddWaiter(m_local_clock_event, EventType::UpdateLocalSystemClock); |                             m_local_clock_event,                                // 3 | ||||||
|             AddWaiter(m_network_clock_event, EventType::UpdateNetworkSystemClock); |                             m_network_clock_event,                              // 4 | ||||||
|             AddWaiter(m_ephemeral_clock_event, EventType::UpdateEphemeralSystemClock); |                             m_ephemeral_clock_event,                            // 5 | ||||||
|             AddWaiter(&m_timer_steady_clock->GetReadableEvent(), EventType::UpdateSteadyClock); |                             &m_timer_steady_clock->GetReadableEvent(),          // 6 | ||||||
|             AddWaiter(&m_timer_file_system->GetReadableEvent(), EventType::UpdateFileTimestamp); |                             &m_timer_file_system->GetReadableEvent(),           // 7 | ||||||
|             AddWaiter(m_standard_user_auto_correct_clock_event, EventType::AutoCorrect); |                             m_standard_user_auto_correct_clock_event            // 8 | ||||||
|  |             ); | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         s32 out_index{-1}; |         switch (static_cast<EventType>(index)) { | ||||||
|         Kernel::KSynchronizationObject::Wait(m_system.Kernel(), &out_index, wait_objs.data(), |  | ||||||
|                                              num_objs, -1); |  | ||||||
|         ASSERT(out_index >= 0 && out_index < num_objs); |  | ||||||
|  |  | ||||||
|         if (stop_token.stop_requested()) { |  | ||||||
|             return; |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         switch (wait_indices[out_index]) { |  | ||||||
|         case EventType::Exit: |         case EventType::Exit: | ||||||
|             return; |             return; | ||||||
|  |  | ||||||
|         case EventType::IpmModuleService_GetEvent: |  | ||||||
|             // TODO |  | ||||||
|             // IPmModuleService::GetEvent() |  | ||||||
|             // clear the event |  | ||||||
|             // Handle power state change event |  | ||||||
|             break; |  | ||||||
|  |  | ||||||
|         case EventType::PowerStateChange: |         case EventType::PowerStateChange: | ||||||
|             m_alarm_worker.GetEvent().Clear(); |             m_alarm_worker.GetEvent().Clear(); | ||||||
|             if (m_pm_state_change_handler.m_priority <= 1) { |             if (m_pm_state_change_handler.m_priority <= 1) { | ||||||
| @@ -235,19 +200,19 @@ void TimeWorker::ThreadFunc(std::stop_token stop_token) { | |||||||
|             m_local_clock_event->Clear(); |             m_local_clock_event->Clear(); | ||||||
|  |  | ||||||
|             Service::PSC::Time::SystemClockContext context{}; |             Service::PSC::Time::SystemClockContext context{}; | ||||||
|             auto res = m_local_clock->GetSystemClockContext(&context); |             R_ASSERT(m_local_clock->GetSystemClockContext(&context)); | ||||||
|             ASSERT(res == ResultSuccess); |  | ||||||
|  |  | ||||||
|             m_set_sys->SetUserSystemClockContext(context); |             m_set_sys->SetUserSystemClockContext(context); | ||||||
|  |  | ||||||
|             m_file_timestamp_worker.SetFilesystemPosixTime(); |             m_file_timestamp_worker.SetFilesystemPosixTime(); | ||||||
|         } break; |             break; | ||||||
|  |         } | ||||||
|  |  | ||||||
|         case EventType::UpdateNetworkSystemClock: { |         case EventType::UpdateNetworkSystemClock: { | ||||||
|             m_network_clock_event->Clear(); |             m_network_clock_event->Clear(); | ||||||
|  |  | ||||||
|             Service::PSC::Time::SystemClockContext context{}; |             Service::PSC::Time::SystemClockContext context{}; | ||||||
|             auto res = m_network_clock->GetSystemClockContext(&context); |             R_ASSERT(m_network_clock->GetSystemClockContext(&context)); | ||||||
|             ASSERT(res == ResultSuccess); |  | ||||||
|             m_set_sys->SetNetworkSystemClockContext(context); |             m_set_sys->SetNetworkSystemClockContext(context); | ||||||
|  |  | ||||||
|             s64 time{}; |             s64 time{}; | ||||||
| @@ -267,7 +232,8 @@ void TimeWorker::ThreadFunc(std::stop_token stop_token) { | |||||||
|             } |             } | ||||||
|  |  | ||||||
|             m_file_timestamp_worker.SetFilesystemPosixTime(); |             m_file_timestamp_worker.SetFilesystemPosixTime(); | ||||||
|         } break; |             break; | ||||||
|  |         } | ||||||
|  |  | ||||||
|         case EventType::UpdateEphemeralSystemClock: { |         case EventType::UpdateEphemeralSystemClock: { | ||||||
|             m_ephemeral_clock_event->Clear(); |             m_ephemeral_clock_event->Clear(); | ||||||
| @@ -295,7 +261,8 @@ void TimeWorker::ThreadFunc(std::stop_token stop_token) { | |||||||
|             if (!g_ig_report_ephemeral_clock_context_set) { |             if (!g_ig_report_ephemeral_clock_context_set) { | ||||||
|                 g_ig_report_ephemeral_clock_context_set = true; |                 g_ig_report_ephemeral_clock_context_set = true; | ||||||
|             } |             } | ||||||
|         } break; |             break; | ||||||
|  |         } | ||||||
|  |  | ||||||
|         case EventType::UpdateSteadyClock: |         case EventType::UpdateSteadyClock: | ||||||
|             m_timer_steady_clock->Clear(); |             m_timer_steady_clock->Clear(); | ||||||
| @@ -314,21 +281,20 @@ void TimeWorker::ThreadFunc(std::stop_token stop_token) { | |||||||
|             m_standard_user_auto_correct_clock_event->Clear(); |             m_standard_user_auto_correct_clock_event->Clear(); | ||||||
|  |  | ||||||
|             bool automatic_correction{}; |             bool automatic_correction{}; | ||||||
|             auto res = m_time_sm->IsStandardUserSystemClockAutomaticCorrectionEnabled( |             R_ASSERT(m_time_sm->IsStandardUserSystemClockAutomaticCorrectionEnabled( | ||||||
|                 &automatic_correction); |                 &automatic_correction)); | ||||||
|             ASSERT(res == ResultSuccess); |  | ||||||
|  |  | ||||||
|             Service::PSC::Time::SteadyClockTimePoint time_point{}; |             Service::PSC::Time::SteadyClockTimePoint time_point{}; | ||||||
|             res = m_time_sm->GetStandardUserSystemClockAutomaticCorrectionUpdatedTime(&time_point); |             R_ASSERT( | ||||||
|             ASSERT(res == ResultSuccess); |                 m_time_sm->GetStandardUserSystemClockAutomaticCorrectionUpdatedTime(&time_point)); | ||||||
|  |  | ||||||
|             m_set_sys->SetUserSystemClockAutomaticCorrectionEnabled(automatic_correction); |             m_set_sys->SetUserSystemClockAutomaticCorrectionEnabled(automatic_correction); | ||||||
|             m_set_sys->SetUserSystemClockAutomaticCorrectionUpdatedTime(time_point); |             m_set_sys->SetUserSystemClockAutomaticCorrectionUpdatedTime(time_point); | ||||||
|         } break; |             break; | ||||||
|  |         } | ||||||
|  |  | ||||||
|         default: |         default: | ||||||
|             UNREACHABLE(); |             UNREACHABLE(); | ||||||
|             break; |  | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| } | } | ||||||
|   | |||||||
| @@ -2,8 +2,8 @@ | |||||||
| // SPDX-License-Identifier: GPL-2.0-or-later
 | // SPDX-License-Identifier: GPL-2.0-or-later
 | ||||||
| 
 | 
 | ||||||
| #include "core/hle/kernel/k_event.h" | #include "core/hle/kernel/k_event.h" | ||||||
| #include "core/hle/service/event.h" |  | ||||||
| #include "core/hle/service/kernel_helpers.h" | #include "core/hle/service/kernel_helpers.h" | ||||||
|  | #include "core/hle/service/os/event.h" | ||||||
| 
 | 
 | ||||||
| namespace Service { | namespace Service { | ||||||
| 
 | 
 | ||||||
							
								
								
									
										59
									
								
								src/core/hle/service/os/multi_wait.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										59
									
								
								src/core/hle/service/os/multi_wait.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,59 @@ | |||||||
|  | // SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project | ||||||
|  | // SPDX-License-Identifier: GPL-2.0-or-later | ||||||
|  |  | ||||||
|  | #include "core/hle/kernel/k_hardware_timer.h" | ||||||
|  | #include "core/hle/kernel/k_synchronization_object.h" | ||||||
|  | #include "core/hle/kernel/kernel.h" | ||||||
|  | #include "core/hle/kernel/svc_common.h" | ||||||
|  | #include "core/hle/service/os/multi_wait.h" | ||||||
|  |  | ||||||
|  | namespace Service { | ||||||
|  |  | ||||||
|  | MultiWait::MultiWait() = default; | ||||||
|  | MultiWait::~MultiWait() = default; | ||||||
|  |  | ||||||
|  | MultiWaitHolder* MultiWait::WaitAny(Kernel::KernelCore& kernel) { | ||||||
|  |     return this->TimedWaitImpl(kernel, -1); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | MultiWaitHolder* MultiWait::TryWaitAny(Kernel::KernelCore& kernel) { | ||||||
|  |     return this->TimedWaitImpl(kernel, 0); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | MultiWaitHolder* MultiWait::TimedWaitAny(Kernel::KernelCore& kernel, s64 timeout_ns) { | ||||||
|  |     return this->TimedWaitImpl(kernel, kernel.HardwareTimer().GetTick() + timeout_ns); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | MultiWaitHolder* MultiWait::TimedWaitImpl(Kernel::KernelCore& kernel, s64 timeout_tick) { | ||||||
|  |     std::array<MultiWaitHolder*, Kernel::Svc::ArgumentHandleCountMax> holders{}; | ||||||
|  |     std::array<Kernel::KSynchronizationObject*, Kernel::Svc::ArgumentHandleCountMax> objects{}; | ||||||
|  |  | ||||||
|  |     s32 out_index = -1; | ||||||
|  |     s32 num_objects = 0; | ||||||
|  |  | ||||||
|  |     for (auto it = m_wait_list.begin(); it != m_wait_list.end(); it++) { | ||||||
|  |         ASSERT(num_objects < Kernel::Svc::ArgumentHandleCountMax); | ||||||
|  |         holders[num_objects] = std::addressof(*it); | ||||||
|  |         objects[num_objects] = it->GetNativeHandle(); | ||||||
|  |         num_objects++; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     Kernel::KSynchronizationObject::Wait(kernel, std::addressof(out_index), objects.data(), | ||||||
|  |                                          num_objects, timeout_tick); | ||||||
|  |  | ||||||
|  |     if (out_index == -1) { | ||||||
|  |         return nullptr; | ||||||
|  |     } else { | ||||||
|  |         return holders[out_index]; | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void MultiWait::MoveAll(MultiWait* other) { | ||||||
|  |     while (!other->m_wait_list.empty()) { | ||||||
|  |         MultiWaitHolder& holder = other->m_wait_list.front(); | ||||||
|  |         holder.UnlinkFromMultiWait(); | ||||||
|  |         holder.LinkToMultiWait(this); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | } // namespace Service | ||||||
							
								
								
									
										36
									
								
								src/core/hle/service/os/multi_wait.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										36
									
								
								src/core/hle/service/os/multi_wait.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,36 @@ | |||||||
|  | // SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project | ||||||
|  | // SPDX-License-Identifier: GPL-2.0-or-later | ||||||
|  |  | ||||||
|  | #pragma once | ||||||
|  |  | ||||||
|  | #include "core/hle/service/os/multi_wait_holder.h" | ||||||
|  |  | ||||||
|  | namespace Kernel { | ||||||
|  | class KernelCore; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | namespace Service { | ||||||
|  |  | ||||||
|  | class MultiWait final { | ||||||
|  | public: | ||||||
|  |     explicit MultiWait(); | ||||||
|  |     ~MultiWait(); | ||||||
|  |  | ||||||
|  | public: | ||||||
|  |     MultiWaitHolder* WaitAny(Kernel::KernelCore& kernel); | ||||||
|  |     MultiWaitHolder* TryWaitAny(Kernel::KernelCore& kernel); | ||||||
|  |     MultiWaitHolder* TimedWaitAny(Kernel::KernelCore& kernel, s64 timeout_ns); | ||||||
|  |     // TODO: SdkReplyAndReceive? | ||||||
|  |  | ||||||
|  |     void MoveAll(MultiWait* other); | ||||||
|  |  | ||||||
|  | private: | ||||||
|  |     MultiWaitHolder* TimedWaitImpl(Kernel::KernelCore& kernel, s64 timeout_tick); | ||||||
|  |  | ||||||
|  | private: | ||||||
|  |     friend class MultiWaitHolder; | ||||||
|  |     using ListType = Common::IntrusiveListMemberTraits<&MultiWaitHolder::m_list_node>::ListType; | ||||||
|  |     ListType m_wait_list{}; | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | } // namespace Service | ||||||
							
								
								
									
										25
									
								
								src/core/hle/service/os/multi_wait_holder.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										25
									
								
								src/core/hle/service/os/multi_wait_holder.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,25 @@ | |||||||
|  | // SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project | ||||||
|  | // SPDX-License-Identifier: GPL-2.0-or-later | ||||||
|  |  | ||||||
|  | #include "core/hle/service/os/multi_wait.h" | ||||||
|  | #include "core/hle/service/os/multi_wait_holder.h" | ||||||
|  |  | ||||||
|  | namespace Service { | ||||||
|  |  | ||||||
|  | void MultiWaitHolder::LinkToMultiWait(MultiWait* multi_wait) { | ||||||
|  |     if (m_multi_wait != nullptr) { | ||||||
|  |         UNREACHABLE(); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     m_multi_wait = multi_wait; | ||||||
|  |     m_multi_wait->m_wait_list.push_back(*this); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void MultiWaitHolder::UnlinkFromMultiWait() { | ||||||
|  |     if (m_multi_wait) { | ||||||
|  |         m_multi_wait->m_wait_list.erase(m_multi_wait->m_wait_list.iterator_to(*this)); | ||||||
|  |         m_multi_wait = nullptr; | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | } // namespace Service | ||||||
							
								
								
									
										44
									
								
								src/core/hle/service/os/multi_wait_holder.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										44
									
								
								src/core/hle/service/os/multi_wait_holder.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,44 @@ | |||||||
|  | // SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project | ||||||
|  | // SPDX-License-Identifier: GPL-2.0-or-later | ||||||
|  |  | ||||||
|  | #pragma once | ||||||
|  |  | ||||||
|  | #include "common/intrusive_list.h" | ||||||
|  |  | ||||||
|  | namespace Kernel { | ||||||
|  | class KSynchronizationObject; | ||||||
|  | } // namespace Kernel | ||||||
|  |  | ||||||
|  | namespace Service { | ||||||
|  |  | ||||||
|  | class MultiWait; | ||||||
|  |  | ||||||
|  | class MultiWaitHolder { | ||||||
|  | public: | ||||||
|  |     explicit MultiWaitHolder(Kernel::KSynchronizationObject* native_handle) | ||||||
|  |         : m_native_handle(native_handle) {} | ||||||
|  |  | ||||||
|  |     void LinkToMultiWait(MultiWait* multi_wait); | ||||||
|  |     void UnlinkFromMultiWait(); | ||||||
|  |  | ||||||
|  |     void SetUserData(uintptr_t user_data) { | ||||||
|  |         m_user_data = user_data; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     uintptr_t GetUserData() const { | ||||||
|  |         return m_user_data; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     Kernel::KSynchronizationObject* GetNativeHandle() const { | ||||||
|  |         return m_native_handle; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  | private: | ||||||
|  |     friend class MultiWait; | ||||||
|  |     Common::IntrusiveListNode m_list_node{}; | ||||||
|  |     MultiWait* m_multi_wait{}; | ||||||
|  |     Kernel::KSynchronizationObject* m_native_handle{}; | ||||||
|  |     uintptr_t m_user_data{}; | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | } // namespace Service | ||||||
							
								
								
									
										109
									
								
								src/core/hle/service/os/multi_wait_utils.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										109
									
								
								src/core/hle/service/os/multi_wait_utils.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,109 @@ | |||||||
|  | // SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project | ||||||
|  | // SPDX-License-Identifier: GPL-2.0-or-later | ||||||
|  |  | ||||||
|  | #pragma once | ||||||
|  |  | ||||||
|  | #include "core/hle/service/os/multi_wait.h" | ||||||
|  |  | ||||||
|  | namespace Service { | ||||||
|  |  | ||||||
|  | namespace impl { | ||||||
|  |  | ||||||
|  | class AutoMultiWaitHolder { | ||||||
|  | private: | ||||||
|  |     MultiWaitHolder m_holder; | ||||||
|  |  | ||||||
|  | public: | ||||||
|  |     template <typename T> | ||||||
|  |     explicit AutoMultiWaitHolder(MultiWait* multi_wait, T&& arg) : m_holder(arg) { | ||||||
|  |         m_holder.LinkToMultiWait(multi_wait); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     ~AutoMultiWaitHolder() { | ||||||
|  |         m_holder.UnlinkFromMultiWait(); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     std::pair<MultiWaitHolder*, int> ConvertResult(const std::pair<MultiWaitHolder*, int> result, | ||||||
|  |                                                    int index) { | ||||||
|  |         if (result.first == std::addressof(m_holder)) { | ||||||
|  |             return std::make_pair(static_cast<MultiWaitHolder*>(nullptr), index); | ||||||
|  |         } else { | ||||||
|  |             return result; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | using WaitAnyFunction = decltype(&MultiWait::WaitAny); | ||||||
|  |  | ||||||
|  | inline std::pair<MultiWaitHolder*, int> WaitAnyImpl(Kernel::KernelCore& kernel, | ||||||
|  |                                                     MultiWait* multi_wait, WaitAnyFunction func, | ||||||
|  |                                                     int) { | ||||||
|  |     return std::pair<MultiWaitHolder*, int>((multi_wait->*func)(kernel), -1); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | template <typename T, typename... Args> | ||||||
|  | inline std::pair<MultiWaitHolder*, int> WaitAnyImpl(Kernel::KernelCore& kernel, | ||||||
|  |                                                     MultiWait* multi_wait, WaitAnyFunction func, | ||||||
|  |                                                     int index, T&& x, Args&&... args) { | ||||||
|  |     AutoMultiWaitHolder holder(multi_wait, std::forward<T>(x)); | ||||||
|  |     return holder.ConvertResult( | ||||||
|  |         WaitAnyImpl(kernel, multi_wait, func, index + 1, std::forward<Args>(args)...), index); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | template <typename... Args> | ||||||
|  | inline std::pair<MultiWaitHolder*, int> WaitAnyImpl(Kernel::KernelCore& kernel, | ||||||
|  |                                                     MultiWait* multi_wait, WaitAnyFunction func, | ||||||
|  |                                                     Args&&... args) { | ||||||
|  |     return WaitAnyImpl(kernel, multi_wait, func, 0, std::forward<Args>(args)...); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | template <typename... Args> | ||||||
|  | inline std::pair<MultiWaitHolder*, int> WaitAnyImpl(Kernel::KernelCore& kernel, | ||||||
|  |                                                     WaitAnyFunction func, Args&&... args) { | ||||||
|  |     MultiWait temp_multi_wait; | ||||||
|  |     return WaitAnyImpl(kernel, std::addressof(temp_multi_wait), func, 0, | ||||||
|  |                        std::forward<Args>(args)...); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | class NotBoolButInt { | ||||||
|  | public: | ||||||
|  |     constexpr NotBoolButInt(int v) : m_value(v) {} | ||||||
|  |     constexpr operator int() const { | ||||||
|  |         return m_value; | ||||||
|  |     } | ||||||
|  |     explicit operator bool() const = delete; | ||||||
|  |  | ||||||
|  | private: | ||||||
|  |     int m_value; | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | } // namespace impl | ||||||
|  |  | ||||||
|  | template <typename... Args> | ||||||
|  |     requires(sizeof...(Args) > 0) | ||||||
|  | inline std::pair<MultiWaitHolder*, int> WaitAny(Kernel::KernelCore& kernel, MultiWait* multi_wait, | ||||||
|  |                                                 Args&&... args) { | ||||||
|  |     return impl::WaitAnyImpl(kernel, &MultiWait::WaitAny, multi_wait, std::forward<Args>(args)...); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | template <typename... Args> | ||||||
|  |     requires(sizeof...(Args) > 0) | ||||||
|  | inline int WaitAny(Kernel::KernelCore& kernel, Args&&... args) { | ||||||
|  |     return impl::WaitAnyImpl(kernel, &MultiWait::WaitAny, std::forward<Args>(args)...).second; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | template <typename... Args> | ||||||
|  |     requires(sizeof...(Args) > 0) | ||||||
|  | inline std::pair<MultiWaitHolder*, int> TryWaitAny(Kernel::KernelCore& kernel, | ||||||
|  |                                                    MultiWait* multi_wait, Args&&... args) { | ||||||
|  |     return impl::WaitAnyImpl(kernel, &MultiWait::TryWaitAny, multi_wait, | ||||||
|  |                              std::forward<Args>(args)...); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | template <typename... Args> | ||||||
|  |     requires(sizeof...(Args) > 0) | ||||||
|  | inline impl::NotBoolButInt TryWaitAny(Kernel::KernelCore& kernel, Args&&... args) { | ||||||
|  |     return impl::WaitAnyImpl(kernel, &MultiWait::TryWaitAny, std::forward<Args>(args)...).second; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | } // namespace Service | ||||||
| @@ -4,7 +4,7 @@ | |||||||
| #include "core/core.h" | #include "core/core.h" | ||||||
| #include "core/hle/kernel/k_event.h" | #include "core/hle/kernel/k_event.h" | ||||||
| #include "core/hle/kernel/k_synchronization_object.h" | #include "core/hle/kernel/k_synchronization_object.h" | ||||||
| #include "core/hle/service/mutex.h" | #include "core/hle/service/os/mutex.h" | ||||||
| 
 | 
 | ||||||
| namespace Service { | namespace Service { | ||||||
| 
 | 
 | ||||||
| @@ -20,50 +20,91 @@ | |||||||
|  |  | ||||||
| namespace Service { | namespace Service { | ||||||
|  |  | ||||||
| constexpr size_t MaximumWaitObjects = 0x40; | enum class UserDataTag { | ||||||
|  |  | ||||||
| enum HandleType { |  | ||||||
|     Port, |     Port, | ||||||
|     Session, |     Session, | ||||||
|     DeferEvent, |     DeferEvent, | ||||||
|     Event, |  | ||||||
| }; | }; | ||||||
|  |  | ||||||
| ServerManager::ServerManager(Core::System& system) : m_system{system}, m_serve_mutex{system} { | class Port : public MultiWaitHolder, public Common::IntrusiveListBaseNode<Port> { | ||||||
|  | public: | ||||||
|  |     explicit Port(Kernel::KServerPort* server_port, SessionRequestHandlerFactory&& handler_factory) | ||||||
|  |         : MultiWaitHolder(server_port), m_handler_factory(std::move(handler_factory)) { | ||||||
|  |         this->SetUserData(static_cast<uintptr_t>(UserDataTag::Port)); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     ~Port() { | ||||||
|  |         this->GetNativeHandle()->Close(); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     SessionRequestHandlerPtr CreateHandler() { | ||||||
|  |         return m_handler_factory(); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  | private: | ||||||
|  |     const SessionRequestHandlerFactory m_handler_factory; | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | class Session : public MultiWaitHolder, public Common::IntrusiveListBaseNode<Session> { | ||||||
|  | public: | ||||||
|  |     explicit Session(Kernel::KServerSession* server_session, | ||||||
|  |                      std::shared_ptr<SessionRequestManager>&& manager) | ||||||
|  |         : MultiWaitHolder(server_session), m_manager(std::move(manager)) { | ||||||
|  |         this->SetUserData(static_cast<uintptr_t>(UserDataTag::Session)); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     ~Session() { | ||||||
|  |         this->GetNativeHandle()->Close(); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     std::shared_ptr<SessionRequestManager>& GetManager() { | ||||||
|  |         return m_manager; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     std::shared_ptr<HLERequestContext>& GetContext() { | ||||||
|  |         return m_context; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  | private: | ||||||
|  |     std::shared_ptr<SessionRequestManager> m_manager; | ||||||
|  |     std::shared_ptr<HLERequestContext> m_context; | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | ServerManager::ServerManager(Core::System& system) : m_system{system}, m_selection_mutex{system} { | ||||||
|     // Initialize event. |     // Initialize event. | ||||||
|     m_event = Kernel::KEvent::Create(system.Kernel()); |     m_wakeup_event = Kernel::KEvent::Create(system.Kernel()); | ||||||
|     m_event->Initialize(nullptr); |     m_wakeup_event->Initialize(nullptr); | ||||||
|  |  | ||||||
|     // Register event. |     // Register event. | ||||||
|     Kernel::KEvent::Register(system.Kernel(), m_event); |     Kernel::KEvent::Register(system.Kernel(), m_wakeup_event); | ||||||
|  |  | ||||||
|  |     // Link to holder. | ||||||
|  |     m_wakeup_holder.emplace(std::addressof(m_wakeup_event->GetReadableEvent())); | ||||||
|  |     m_wakeup_holder->LinkToMultiWait(std::addressof(m_deferred_list)); | ||||||
| } | } | ||||||
|  |  | ||||||
| ServerManager::~ServerManager() { | ServerManager::~ServerManager() { | ||||||
|     // Signal stop. |     // Signal stop. | ||||||
|     m_stop_source.request_stop(); |     m_stop_source.request_stop(); | ||||||
|     m_event->Signal(); |     m_wakeup_event->Signal(); | ||||||
|  |  | ||||||
|     // Wait for processing to stop. |     // Wait for processing to stop. | ||||||
|     m_stopped.Wait(); |     m_stopped.Wait(); | ||||||
|     m_threads.clear(); |     m_threads.clear(); | ||||||
|  |  | ||||||
|     // Clean up server ports. |     // Clean up ports. | ||||||
|     for (const auto& [port, handler] : m_ports) { |     for (auto it = m_servers.begin(); it != m_servers.end(); it = m_servers.erase(it)) { | ||||||
|         port->Close(); |         delete std::addressof(*it); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     // Clean up sessions. |     // Clean up sessions. | ||||||
|     for (const auto& [session, manager] : m_sessions) { |     for (auto it = m_sessions.begin(); it != m_sessions.end(); it = m_sessions.erase(it)) { | ||||||
|         session->Close(); |         delete std::addressof(*it); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     for (const auto& request : m_deferrals) { |     // Close wakeup event. | ||||||
|         request.session->Close(); |     m_wakeup_event->GetReadableEvent().Close(); | ||||||
|     } |     m_wakeup_event->Close(); | ||||||
|  |  | ||||||
|     // Close event. |  | ||||||
|     m_event->GetReadableEvent().Close(); |  | ||||||
|     m_event->Close(); |  | ||||||
|  |  | ||||||
|     if (m_deferral_event) { |     if (m_deferral_event) { | ||||||
|         m_deferral_event->GetReadableEvent().Close(); |         m_deferral_event->GetReadableEvent().Close(); | ||||||
| @@ -75,19 +116,19 @@ void ServerManager::RunServer(std::unique_ptr<ServerManager>&& server_manager) { | |||||||
|     server_manager->m_system.RunServer(std::move(server_manager)); |     server_manager->m_system.RunServer(std::move(server_manager)); | ||||||
| } | } | ||||||
|  |  | ||||||
| Result ServerManager::RegisterSession(Kernel::KServerSession* session, | Result ServerManager::RegisterSession(Kernel::KServerSession* server_session, | ||||||
|                                       std::shared_ptr<SessionRequestManager> manager) { |                                       std::shared_ptr<SessionRequestManager> manager) { | ||||||
|     ASSERT(m_sessions.size() + m_ports.size() < MaximumWaitObjects); |  | ||||||
|  |  | ||||||
|     // We are taking ownership of the server session, so don't open it. |     // We are taking ownership of the server session, so don't open it. | ||||||
|  |     auto* session = new Session(server_session, std::move(manager)); | ||||||
|  |  | ||||||
|     // Begin tracking the server session. |     // Begin tracking the server session. | ||||||
|     { |     { | ||||||
|         std::scoped_lock ll{m_list_mutex}; |         std::scoped_lock ll{m_deferred_list_mutex}; | ||||||
|         m_sessions.emplace(session, std::move(manager)); |         m_sessions.push_back(*session); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     // Signal the wakeup event. |     // Register to wait on the session. | ||||||
|     m_event->Signal(); |     this->LinkToDeferredList(session); | ||||||
|  |  | ||||||
|     R_SUCCEED(); |     R_SUCCEED(); | ||||||
| } | } | ||||||
| @@ -95,21 +136,22 @@ Result ServerManager::RegisterSession(Kernel::KServerSession* session, | |||||||
| Result ServerManager::RegisterNamedService(const std::string& service_name, | Result ServerManager::RegisterNamedService(const std::string& service_name, | ||||||
|                                            SessionRequestHandlerFactory&& handler_factory, |                                            SessionRequestHandlerFactory&& handler_factory, | ||||||
|                                            u32 max_sessions) { |                                            u32 max_sessions) { | ||||||
|     ASSERT(m_sessions.size() + m_ports.size() < MaximumWaitObjects); |  | ||||||
|  |  | ||||||
|     // Add the new server to sm: and get the moved server port. |     // Add the new server to sm: and get the moved server port. | ||||||
|     Kernel::KServerPort* server_port{}; |     Kernel::KServerPort* server_port{}; | ||||||
|     R_ASSERT(m_system.ServiceManager().RegisterService(std::addressof(server_port), service_name, |     R_ASSERT(m_system.ServiceManager().RegisterService(std::addressof(server_port), service_name, | ||||||
|                                                        max_sessions, handler_factory)); |                                                        max_sessions, handler_factory)); | ||||||
|  |  | ||||||
|  |     // We are taking ownership of the server port, so don't open it. | ||||||
|  |     auto* server = new Port(server_port, std::move(handler_factory)); | ||||||
|  |  | ||||||
|     // Begin tracking the server port. |     // Begin tracking the server port. | ||||||
|     { |     { | ||||||
|         std::scoped_lock ll{m_list_mutex}; |         std::scoped_lock ll{m_deferred_list_mutex}; | ||||||
|         m_ports.emplace(server_port, std::move(handler_factory)); |         m_servers.push_back(*server); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     // Signal the wakeup event. |     // Register to wait on the server port. | ||||||
|     m_event->Signal(); |     this->LinkToDeferredList(server); | ||||||
|  |  | ||||||
|     R_SUCCEED(); |     R_SUCCEED(); | ||||||
| } | } | ||||||
| @@ -127,8 +169,6 @@ Result ServerManager::RegisterNamedService(const std::string& service_name, | |||||||
| Result ServerManager::ManageNamedPort(const std::string& service_name, | Result ServerManager::ManageNamedPort(const std::string& service_name, | ||||||
|                                       SessionRequestHandlerFactory&& handler_factory, |                                       SessionRequestHandlerFactory&& handler_factory, | ||||||
|                                       u32 max_sessions) { |                                       u32 max_sessions) { | ||||||
|     ASSERT(m_sessions.size() + m_ports.size() < MaximumWaitObjects); |  | ||||||
|  |  | ||||||
|     // Create a new port. |     // Create a new port. | ||||||
|     auto* port = Kernel::KPort::Create(m_system.Kernel()); |     auto* port = Kernel::KPort::Create(m_system.Kernel()); | ||||||
|     port->Initialize(max_sessions, false, 0); |     port->Initialize(max_sessions, false, 0); | ||||||
| @@ -149,12 +189,18 @@ Result ServerManager::ManageNamedPort(const std::string& service_name, | |||||||
|     // Open a new reference to the server port. |     // Open a new reference to the server port. | ||||||
|     port->GetServerPort().Open(); |     port->GetServerPort().Open(); | ||||||
|  |  | ||||||
|     // Begin tracking the server port. |     // Transfer ownership into a new port object. | ||||||
|  |     auto* server = new Port(std::addressof(port->GetServerPort()), std::move(handler_factory)); | ||||||
|  |  | ||||||
|  |     // Begin tracking the port. | ||||||
|     { |     { | ||||||
|         std::scoped_lock ll{m_list_mutex}; |         std::scoped_lock ll{m_deferred_list_mutex}; | ||||||
|         m_ports.emplace(std::addressof(port->GetServerPort()), std::move(handler_factory)); |         m_servers.push_back(*server); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     // Register to wait on the port. | ||||||
|  |     this->LinkToDeferredList(server); | ||||||
|  |  | ||||||
|     // We succeeded. |     // We succeeded. | ||||||
|     R_SUCCEED(); |     R_SUCCEED(); | ||||||
| } | } | ||||||
| @@ -173,6 +219,11 @@ Result ServerManager::ManageDeferral(Kernel::KEvent** out_event) { | |||||||
|     // Set the output. |     // Set the output. | ||||||
|     *out_event = m_deferral_event; |     *out_event = m_deferral_event; | ||||||
|  |  | ||||||
|  |     // Register to wait on the event. | ||||||
|  |     m_deferral_holder.emplace(std::addressof(m_deferral_event->GetReadableEvent())); | ||||||
|  |     m_deferral_holder->SetUserData(static_cast<uintptr_t>(UserDataTag::DeferEvent)); | ||||||
|  |     this->LinkToDeferredList(std::addressof(*m_deferral_holder)); | ||||||
|  |  | ||||||
|     // We succeeded. |     // We succeeded. | ||||||
|     R_SUCCEED(); |     R_SUCCEED(); | ||||||
| } | } | ||||||
| @@ -191,270 +242,185 @@ Result ServerManager::LoopProcess() { | |||||||
|     R_RETURN(this->LoopProcessImpl()); |     R_RETURN(this->LoopProcessImpl()); | ||||||
| } | } | ||||||
|  |  | ||||||
|  | void ServerManager::LinkToDeferredList(MultiWaitHolder* holder) { | ||||||
|  |     // Link. | ||||||
|  |     { | ||||||
|  |         std::scoped_lock lk{m_deferred_list_mutex}; | ||||||
|  |         holder->LinkToMultiWait(std::addressof(m_deferred_list)); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     // Signal the wakeup event. | ||||||
|  |     m_wakeup_event->Signal(); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void ServerManager::LinkDeferred() { | ||||||
|  |     std::scoped_lock lk{m_deferred_list_mutex}; | ||||||
|  |     m_multi_wait.MoveAll(std::addressof(m_deferred_list)); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | MultiWaitHolder* ServerManager::WaitSignaled() { | ||||||
|  |     // Ensure we are the only thread waiting for this server. | ||||||
|  |     std::scoped_lock lk{m_selection_mutex}; | ||||||
|  |  | ||||||
|  |     while (true) { | ||||||
|  |         this->LinkDeferred(); | ||||||
|  |  | ||||||
|  |         // If we're done, return before we start waiting. | ||||||
|  |         if (m_stop_source.stop_requested()) { | ||||||
|  |             return nullptr; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         auto* selected = m_multi_wait.WaitAny(m_system.Kernel()); | ||||||
|  |         if (selected == std::addressof(*m_wakeup_holder)) { | ||||||
|  |             // Clear and restart if we were woken up. | ||||||
|  |             m_wakeup_event->Clear(); | ||||||
|  |         } else { | ||||||
|  |             // Unlink and handle the event. | ||||||
|  |             selected->UnlinkFromMultiWait(); | ||||||
|  |             return selected; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | Result ServerManager::Process(MultiWaitHolder* holder) { | ||||||
|  |     switch (static_cast<UserDataTag>(holder->GetUserData())) { | ||||||
|  |     case UserDataTag::Session: | ||||||
|  |         R_RETURN(this->OnSessionEvent(static_cast<Session*>(holder))); | ||||||
|  |     case UserDataTag::Port: | ||||||
|  |         R_RETURN(this->OnPortEvent(static_cast<Port*>(holder))); | ||||||
|  |     case UserDataTag::DeferEvent: | ||||||
|  |         R_RETURN(this->OnDeferralEvent()); | ||||||
|  |     default: | ||||||
|  |         UNREACHABLE(); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | bool ServerManager::WaitAndProcessImpl() { | ||||||
|  |     if (auto* signaled_holder = this->WaitSignaled(); signaled_holder != nullptr) { | ||||||
|  |         R_ASSERT(this->Process(signaled_holder)); | ||||||
|  |         return true; | ||||||
|  |     } else { | ||||||
|  |         return false; | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
| Result ServerManager::LoopProcessImpl() { | Result ServerManager::LoopProcessImpl() { | ||||||
|     while (!m_stop_source.stop_requested()) { |     while (!m_stop_source.stop_requested()) { | ||||||
|         R_TRY(this->WaitAndProcessImpl()); |         this->WaitAndProcessImpl(); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     R_SUCCEED(); |     R_SUCCEED(); | ||||||
| } | } | ||||||
|  |  | ||||||
| Result ServerManager::WaitAndProcessImpl() { | Result ServerManager::OnPortEvent(Port* server) { | ||||||
|     Kernel::KScopedAutoObject<Kernel::KSynchronizationObject> wait_obj; |  | ||||||
|     HandleType wait_type{}; |  | ||||||
|  |  | ||||||
|     // Ensure we are the only thread waiting for this server. |  | ||||||
|     std::unique_lock sl{m_serve_mutex}; |  | ||||||
|  |  | ||||||
|     // If we're done, return before we start waiting. |  | ||||||
|     R_SUCCEED_IF(m_stop_source.stop_requested()); |  | ||||||
|  |  | ||||||
|     // Wait for a tracked object to become signaled. |  | ||||||
|     { |  | ||||||
|         s32 num_objs{}; |  | ||||||
|         std::array<HandleType, MaximumWaitObjects> wait_types{}; |  | ||||||
|         std::array<Kernel::KSynchronizationObject*, MaximumWaitObjects> wait_objs{}; |  | ||||||
|  |  | ||||||
|         const auto AddWaiter{ |  | ||||||
|             [&](Kernel::KSynchronizationObject* synchronization_object, HandleType type) { |  | ||||||
|                 // Open a new reference to the object. |  | ||||||
|                 synchronization_object->Open(); |  | ||||||
|  |  | ||||||
|                 // Insert into the list. |  | ||||||
|                 wait_types[num_objs] = type; |  | ||||||
|                 wait_objs[num_objs++] = synchronization_object; |  | ||||||
|             }}; |  | ||||||
|  |  | ||||||
|         { |  | ||||||
|             std::scoped_lock ll{m_list_mutex}; |  | ||||||
|  |  | ||||||
|             // Add all of our ports. |  | ||||||
|             for (const auto& [port, handler] : m_ports) { |  | ||||||
|                 AddWaiter(port, HandleType::Port); |  | ||||||
|             } |  | ||||||
|  |  | ||||||
|             // Add all of our sessions. |  | ||||||
|             for (const auto& [session, manager] : m_sessions) { |  | ||||||
|                 AddWaiter(session, HandleType::Session); |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         // Add the deferral wakeup event. |  | ||||||
|         if (m_deferral_event != nullptr) { |  | ||||||
|             AddWaiter(std::addressof(m_deferral_event->GetReadableEvent()), HandleType::DeferEvent); |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         // Add the wakeup event. |  | ||||||
|         AddWaiter(std::addressof(m_event->GetReadableEvent()), HandleType::Event); |  | ||||||
|  |  | ||||||
|         // Clean up extra references on exit. |  | ||||||
|         SCOPE_EXIT({ |  | ||||||
|             for (s32 i = 0; i < num_objs; i++) { |  | ||||||
|                 wait_objs[i]->Close(); |  | ||||||
|             } |  | ||||||
|         }); |  | ||||||
|  |  | ||||||
|         // Wait for a signal. |  | ||||||
|         s32 out_index{-1}; |  | ||||||
|         R_TRY_CATCH(Kernel::KSynchronizationObject::Wait(m_system.Kernel(), &out_index, |  | ||||||
|                                                          wait_objs.data(), num_objs, -1)) { |  | ||||||
|             R_CATCH(Kernel::ResultSessionClosed) { |  | ||||||
|                 // On session closed, index is updated and we don't want to return an error. |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
|         R_END_TRY_CATCH; |  | ||||||
|         ASSERT(out_index >= 0 && out_index < num_objs); |  | ||||||
|  |  | ||||||
|         // Set the output index. |  | ||||||
|         wait_obj = wait_objs[out_index]; |  | ||||||
|         wait_type = wait_types[out_index]; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     // Process what we just received, temporarily removing the object so it is |  | ||||||
|     // not processed concurrently by another thread. |  | ||||||
|     { |  | ||||||
|         switch (wait_type) { |  | ||||||
|         case HandleType::Port: { |  | ||||||
|             // Port signaled. |  | ||||||
|             auto* port = wait_obj->DynamicCast<Kernel::KServerPort*>(); |  | ||||||
|             SessionRequestHandlerFactory handler_factory; |  | ||||||
|  |  | ||||||
|             // Remove from tracking. |  | ||||||
|             { |  | ||||||
|                 std::scoped_lock ll{m_list_mutex}; |  | ||||||
|                 ASSERT(m_ports.contains(port)); |  | ||||||
|                 m_ports.at(port).swap(handler_factory); |  | ||||||
|                 m_ports.erase(port); |  | ||||||
|             } |  | ||||||
|  |  | ||||||
|             // Allow other threads to serve. |  | ||||||
|             sl.unlock(); |  | ||||||
|  |  | ||||||
|             // Finish. |  | ||||||
|             R_RETURN(this->OnPortEvent(port, std::move(handler_factory))); |  | ||||||
|         } |  | ||||||
|         case HandleType::Session: { |  | ||||||
|             // Session signaled. |  | ||||||
|             auto* session = wait_obj->DynamicCast<Kernel::KServerSession*>(); |  | ||||||
|             std::shared_ptr<SessionRequestManager> manager; |  | ||||||
|  |  | ||||||
|             // Remove from tracking. |  | ||||||
|             { |  | ||||||
|                 std::scoped_lock ll{m_list_mutex}; |  | ||||||
|                 ASSERT(m_sessions.contains(session)); |  | ||||||
|                 m_sessions.at(session).swap(manager); |  | ||||||
|                 m_sessions.erase(session); |  | ||||||
|             } |  | ||||||
|  |  | ||||||
|             // Allow other threads to serve. |  | ||||||
|             sl.unlock(); |  | ||||||
|  |  | ||||||
|             // Finish. |  | ||||||
|             R_RETURN(this->OnSessionEvent(session, std::move(manager))); |  | ||||||
|         } |  | ||||||
|         case HandleType::DeferEvent: { |  | ||||||
|             // Clear event. |  | ||||||
|             ASSERT(R_SUCCEEDED(m_deferral_event->Clear())); |  | ||||||
|  |  | ||||||
|             // Drain the list of deferrals while we process. |  | ||||||
|             std::list<RequestState> deferrals; |  | ||||||
|             { |  | ||||||
|                 std::scoped_lock ll{m_list_mutex}; |  | ||||||
|                 m_deferrals.swap(deferrals); |  | ||||||
|             } |  | ||||||
|  |  | ||||||
|             // Allow other threads to serve. |  | ||||||
|             sl.unlock(); |  | ||||||
|  |  | ||||||
|             // Finish. |  | ||||||
|             R_RETURN(this->OnDeferralEvent(std::move(deferrals))); |  | ||||||
|         } |  | ||||||
|         case HandleType::Event: { |  | ||||||
|             // Clear event and finish. |  | ||||||
|             R_RETURN(m_event->Clear()); |  | ||||||
|         } |  | ||||||
|         default: { |  | ||||||
|             UNREACHABLE(); |  | ||||||
|         } |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| } |  | ||||||
|  |  | ||||||
| Result ServerManager::OnPortEvent(Kernel::KServerPort* port, |  | ||||||
|                                   SessionRequestHandlerFactory&& handler_factory) { |  | ||||||
|     // Accept a new server session. |     // Accept a new server session. | ||||||
|     Kernel::KServerSession* session = port->AcceptSession(); |     auto* server_port = static_cast<Kernel::KServerPort*>(server->GetNativeHandle()); | ||||||
|     ASSERT(session != nullptr); |     Kernel::KServerSession* server_session = server_port->AcceptSession(); | ||||||
|  |     ASSERT(server_session != nullptr); | ||||||
|  |  | ||||||
|     // Create the session manager and install the handler. |     // Create the session manager and install the handler. | ||||||
|     auto manager = std::make_shared<SessionRequestManager>(m_system.Kernel(), *this); |     auto manager = std::make_shared<SessionRequestManager>(m_system.Kernel(), *this); | ||||||
|     manager->SetSessionHandler(handler_factory()); |     manager->SetSessionHandler(server->CreateHandler()); | ||||||
|  |  | ||||||
|     // Track the server session. |     // Create and register the new session. | ||||||
|     { |     this->RegisterSession(server_session, std::move(manager)); | ||||||
|         std::scoped_lock ll{m_list_mutex}; |  | ||||||
|         m_ports.emplace(port, std::move(handler_factory)); |  | ||||||
|         m_sessions.emplace(session, std::move(manager)); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     // Signal the wakeup event. |     // Resume tracking the port. | ||||||
|     m_event->Signal(); |     this->LinkToDeferredList(server); | ||||||
|  |  | ||||||
|     // We succeeded. |     // We succeeded. | ||||||
|     R_SUCCEED(); |     R_SUCCEED(); | ||||||
| } | } | ||||||
|  |  | ||||||
| Result ServerManager::OnSessionEvent(Kernel::KServerSession* session, | Result ServerManager::OnSessionEvent(Session* session) { | ||||||
|                                      std::shared_ptr<SessionRequestManager>&& manager) { |     Result res = ResultSuccess; | ||||||
|     Result rc{ResultSuccess}; |  | ||||||
|  |  | ||||||
|     // Try to receive a message. |     // Try to receive a message. | ||||||
|     std::shared_ptr<HLERequestContext> context; |     auto* server_session = static_cast<Kernel::KServerSession*>(session->GetNativeHandle()); | ||||||
|     rc = session->ReceiveRequestHLE(&context, manager); |     res = server_session->ReceiveRequestHLE(&session->GetContext(), session->GetManager()); | ||||||
|  |  | ||||||
|     // If the session has been closed, we're done. |     // If the session has been closed, we're done. | ||||||
|     if (rc == Kernel::ResultSessionClosed) { |     if (res == Kernel::ResultSessionClosed) { | ||||||
|         // Close the session. |         this->DestroySession(session); | ||||||
|         session->Close(); |  | ||||||
|  |  | ||||||
|         // Finish. |  | ||||||
|         R_SUCCEED(); |         R_SUCCEED(); | ||||||
|     } |     } | ||||||
|     ASSERT(R_SUCCEEDED(rc)); |  | ||||||
|  |  | ||||||
|     RequestState request{ |     R_ASSERT(res); | ||||||
|         .session = session, |  | ||||||
|         .context = std::move(context), |  | ||||||
|         .manager = std::move(manager), |  | ||||||
|     }; |  | ||||||
|  |  | ||||||
|     // Complete the sync request with deferral handling. |     // Complete the sync request with deferral handling. | ||||||
|     R_RETURN(this->CompleteSyncRequest(std::move(request))); |     R_RETURN(this->CompleteSyncRequest(session)); | ||||||
| } | } | ||||||
|  |  | ||||||
| Result ServerManager::CompleteSyncRequest(RequestState&& request) { | Result ServerManager::CompleteSyncRequest(Session* session) { | ||||||
|     Result rc{ResultSuccess}; |     Result res = ResultSuccess; | ||||||
|     Result service_rc{ResultSuccess}; |     Result service_res = ResultSuccess; | ||||||
|  |  | ||||||
|     // Mark the request as not deferred. |     // Mark the request as not deferred. | ||||||
|     request.context->SetIsDeferred(false); |     session->GetContext()->SetIsDeferred(false); | ||||||
|  |  | ||||||
|     // Complete the request. We have exclusive access to this session. |     // Complete the request. We have exclusive access to this session. | ||||||
|     service_rc = request.manager->CompleteSyncRequest(request.session, *request.context); |     auto* server_session = static_cast<Kernel::KServerSession*>(session->GetNativeHandle()); | ||||||
|  |     service_res = | ||||||
|  |         session->GetManager()->CompleteSyncRequest(server_session, *session->GetContext()); | ||||||
|  |  | ||||||
|     // If we've been deferred, we're done. |     // If we've been deferred, we're done. | ||||||
|     if (request.context->GetIsDeferred()) { |     if (session->GetContext()->GetIsDeferred()) { | ||||||
|         // Insert into deferral list. |         // Insert into deferred session list. | ||||||
|         std::scoped_lock ll{m_list_mutex}; |         std::scoped_lock ll{m_deferred_list_mutex}; | ||||||
|         m_deferrals.emplace_back(std::move(request)); |         m_deferred_sessions.push_back(session); | ||||||
|  |  | ||||||
|         // Finish. |         // Finish. | ||||||
|         R_SUCCEED(); |         R_SUCCEED(); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     // Send the reply. |     // Send the reply. | ||||||
|     rc = request.session->SendReplyHLE(); |     res = server_session->SendReplyHLE(); | ||||||
|  |  | ||||||
|     // If the session has been closed, we're done. |     // If the session has been closed, we're done. | ||||||
|     if (rc == Kernel::ResultSessionClosed || service_rc == IPC::ResultSessionClosed) { |     if (res == Kernel::ResultSessionClosed || service_res == IPC::ResultSessionClosed) { | ||||||
|         // Close the session. |         this->DestroySession(session); | ||||||
|         request.session->Close(); |  | ||||||
|  |  | ||||||
|         // Finish. |  | ||||||
|         R_SUCCEED(); |         R_SUCCEED(); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     ASSERT(R_SUCCEEDED(rc)); |     R_ASSERT(res); | ||||||
|     ASSERT(R_SUCCEEDED(service_rc)); |     R_ASSERT(service_res); | ||||||
|  |  | ||||||
|     // Reinsert the session. |     // We succeeded, so we can process future messages on this session. | ||||||
|     { |     this->LinkToDeferredList(session); | ||||||
|         std::scoped_lock ll{m_list_mutex}; |  | ||||||
|         m_sessions.emplace(request.session, std::move(request.manager)); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     // Signal the wakeup event. |  | ||||||
|     m_event->Signal(); |  | ||||||
|  |  | ||||||
|     // We succeeded. |  | ||||||
|     R_SUCCEED(); |     R_SUCCEED(); | ||||||
| } | } | ||||||
|  |  | ||||||
| Result ServerManager::OnDeferralEvent(std::list<RequestState>&& deferrals) { | Result ServerManager::OnDeferralEvent() { | ||||||
|     ON_RESULT_FAILURE { |     // Clear event before grabbing the list. | ||||||
|         std::scoped_lock ll{m_list_mutex}; |     m_deferral_event->Clear(); | ||||||
|         m_deferrals.splice(m_deferrals.end(), deferrals); |  | ||||||
|     }; |  | ||||||
|  |  | ||||||
|     while (!deferrals.empty()) { |     // Get and clear list. | ||||||
|         RequestState request = deferrals.front(); |     const auto deferrals = [&] { | ||||||
|         deferrals.pop_front(); |         std::scoped_lock lk{m_deferred_list_mutex}; | ||||||
|  |         return std::move(m_deferred_sessions); | ||||||
|  |     }(); | ||||||
|  |  | ||||||
|         // Try again to complete the request. |     // Relink deferral event. | ||||||
|         R_TRY(this->CompleteSyncRequest(std::move(request))); |     this->LinkToDeferredList(std::addressof(*m_deferral_holder)); | ||||||
|  |  | ||||||
|  |     // For each session, try again to complete the request. | ||||||
|  |     for (auto* session : deferrals) { | ||||||
|  |         R_ASSERT(this->CompleteSyncRequest(session)); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     R_SUCCEED(); |     R_SUCCEED(); | ||||||
| } | } | ||||||
|  |  | ||||||
|  | void ServerManager::DestroySession(Session* session) { | ||||||
|  |     // Unlink. | ||||||
|  |     { | ||||||
|  |         std::scoped_lock lk{m_deferred_list_mutex}; | ||||||
|  |         m_sessions.erase(m_sessions.iterator_to(*session)); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     // Free the session. | ||||||
|  |     delete session; | ||||||
|  | } | ||||||
|  |  | ||||||
| } // namespace Service | } // namespace Service | ||||||
|   | |||||||
| @@ -3,18 +3,17 @@ | |||||||
|  |  | ||||||
| #pragma once | #pragma once | ||||||
|  |  | ||||||
| #include <functional> |  | ||||||
| #include <list> | #include <list> | ||||||
| #include <map> |  | ||||||
| #include <mutex> | #include <mutex> | ||||||
| #include <string_view> | #include <optional> | ||||||
| #include <vector> | #include <vector> | ||||||
|  |  | ||||||
| #include "common/polyfill_thread.h" | #include "common/polyfill_thread.h" | ||||||
| #include "common/thread.h" | #include "common/thread.h" | ||||||
| #include "core/hle/result.h" | #include "core/hle/result.h" | ||||||
| #include "core/hle/service/hle_ipc.h" | #include "core/hle/service/hle_ipc.h" | ||||||
| #include "core/hle/service/mutex.h" | #include "core/hle/service/os/multi_wait.h" | ||||||
|  | #include "core/hle/service/os/mutex.h" | ||||||
|  |  | ||||||
| namespace Core { | namespace Core { | ||||||
| class System; | class System; | ||||||
| @@ -24,11 +23,13 @@ namespace Kernel { | |||||||
| class KEvent; | class KEvent; | ||||||
| class KServerPort; | class KServerPort; | ||||||
| class KServerSession; | class KServerSession; | ||||||
| class KSynchronizationObject; |  | ||||||
| } // namespace Kernel | } // namespace Kernel | ||||||
|  |  | ||||||
| namespace Service { | namespace Service { | ||||||
|  |  | ||||||
|  | class Port; | ||||||
|  | class Session; | ||||||
|  |  | ||||||
| class ServerManager { | class ServerManager { | ||||||
| public: | public: | ||||||
|     explicit ServerManager(Core::System& system); |     explicit ServerManager(Core::System& system); | ||||||
| @@ -52,34 +53,40 @@ public: | |||||||
|     static void RunServer(std::unique_ptr<ServerManager>&& server); |     static void RunServer(std::unique_ptr<ServerManager>&& server); | ||||||
|  |  | ||||||
| private: | private: | ||||||
|     struct RequestState; |     void LinkToDeferredList(MultiWaitHolder* holder); | ||||||
|  |     void LinkDeferred(); | ||||||
|  |     MultiWaitHolder* WaitSignaled(); | ||||||
|  |     Result Process(MultiWaitHolder* holder); | ||||||
|  |     bool WaitAndProcessImpl(); | ||||||
|     Result LoopProcessImpl(); |     Result LoopProcessImpl(); | ||||||
|     Result WaitAndProcessImpl(); |  | ||||||
|     Result OnPortEvent(Kernel::KServerPort* port, SessionRequestHandlerFactory&& handler_factory); |     Result OnPortEvent(Port* port); | ||||||
|     Result OnSessionEvent(Kernel::KServerSession* session, |     Result OnSessionEvent(Session* session); | ||||||
|                           std::shared_ptr<SessionRequestManager>&& manager); |     Result OnDeferralEvent(); | ||||||
|     Result OnDeferralEvent(std::list<RequestState>&& deferrals); |     Result CompleteSyncRequest(Session* session); | ||||||
|     Result CompleteSyncRequest(RequestState&& state); |  | ||||||
|  | private: | ||||||
|  |     void DestroySession(Session* session); | ||||||
|  |  | ||||||
| private: | private: | ||||||
|     Core::System& m_system; |     Core::System& m_system; | ||||||
|     Mutex m_serve_mutex; |     Mutex m_selection_mutex; | ||||||
|     std::mutex m_list_mutex; |  | ||||||
|  |  | ||||||
|     // Guest state tracking |     // Events | ||||||
|     std::map<Kernel::KServerPort*, SessionRequestHandlerFactory> m_ports{}; |     Kernel::KEvent* m_wakeup_event{}; | ||||||
|     std::map<Kernel::KServerSession*, std::shared_ptr<SessionRequestManager>> m_sessions{}; |  | ||||||
|     Kernel::KEvent* m_event{}; |  | ||||||
|     Kernel::KEvent* m_deferral_event{}; |     Kernel::KEvent* m_deferral_event{}; | ||||||
|  |  | ||||||
|     // Deferral tracking |     // Deferred wait list | ||||||
|     struct RequestState { |     std::mutex m_deferred_list_mutex{}; | ||||||
|         Kernel::KServerSession* session; |     MultiWait m_deferred_list{}; | ||||||
|         std::shared_ptr<HLERequestContext> context; |  | ||||||
|         std::shared_ptr<SessionRequestManager> manager; |     // Guest state tracking | ||||||
|     }; |     MultiWait m_multi_wait{}; | ||||||
|     std::list<RequestState> m_deferrals{}; |     Common::IntrusiveListBaseTraits<Port>::ListType m_servers{}; | ||||||
|  |     Common::IntrusiveListBaseTraits<Session>::ListType m_sessions{}; | ||||||
|  |     std::list<Session*> m_deferred_sessions{}; | ||||||
|  |     std::optional<MultiWaitHolder> m_wakeup_holder{}; | ||||||
|  |     std::optional<MultiWaitHolder> m_deferral_holder{}; | ||||||
|  |  | ||||||
|     // Host state tracking |     // Host state tracking | ||||||
|     Common::Event m_stopped{}; |     Common::Event m_stopped{}; | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user