mirror of
				https://git.tardis.systems/mirrors/yuzu
				synced 2025-10-31 02:34:11 +01:00 
			
		
		
		
	Merge pull request #12949 from liamwhite/multi-wait
service: add os types and multi wait API
This commit is contained in:
		
						commit
						7c56ecca3f
					
				| @ -548,8 +548,6 @@ add_library(core STATIC | ||||
|     hle/service/es/es.h | ||||
|     hle/service/eupld/eupld.cpp | ||||
|     hle/service/eupld/eupld.h | ||||
|     hle/service/event.cpp | ||||
|     hle/service/event.h | ||||
|     hle/service/fatal/fatal.cpp | ||||
|     hle/service/fatal/fatal.h | ||||
|     hle/service/fatal/fatal_p.cpp | ||||
| @ -676,8 +674,6 @@ add_library(core STATIC | ||||
|     hle/service/mm/mm_u.h | ||||
|     hle/service/mnpp/mnpp_app.cpp | ||||
|     hle/service/mnpp/mnpp_app.h | ||||
|     hle/service/mutex.cpp | ||||
|     hle/service/mutex.h | ||||
|     hle/service/ncm/ncm.cpp | ||||
|     hle/service/ncm/ncm.h | ||||
|     hle/service/nfc/common/amiibo_crypto.cpp | ||||
| @ -790,6 +786,15 @@ add_library(core STATIC | ||||
|     hle/service/nvnflinger/window.h | ||||
|     hle/service/olsc/olsc.cpp | ||||
|     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.h | ||||
|     hle/service/pctl/pctl.cpp | ||||
|  | ||||
| @ -9,8 +9,8 @@ | ||||
| #include "common/math_util.h" | ||||
| #include "core/hle/service/apm/apm_controller.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/os/event.h" | ||||
| #include "core/hle/service/service.h" | ||||
| 
 | ||||
| #include "core/hle/service/am/am_types.h" | ||||
|  | ||||
| @ -7,8 +7,8 @@ | ||||
| #include <memory> | ||||
| #include <mutex> | ||||
| 
 | ||||
| #include "core/hle/service/event.h" | ||||
| #include "core/hle/service/kernel_helpers.h" | ||||
| #include "core/hle/service/os/event.h" | ||||
| 
 | ||||
| union Result; | ||||
| 
 | ||||
|  | ||||
| @ -7,6 +7,7 @@ | ||||
| #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/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/service_manager.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::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()) { | ||||
|         SCOPE_EXIT({ | ||||
|             for (s32 i = 0; i < num_objs; i++) { | ||||
|                 wait_objs[i]->Close(); | ||||
|             } | ||||
|         }); | ||||
|         enum class EventType : s32 { | ||||
|             Exit = 0, | ||||
|             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) { | ||||
|             AddWaiter(&m_event->GetReadableEvent(), EventType::Exit); | ||||
|             // TODO
 | ||||
|             // AddWaiter(gIPmModuleService::GetEvent(), 1);
 | ||||
|             AddWaiter(&m_alarm_worker.GetEvent(), EventType::PowerStateChange); | ||||
|             // TODO: gIPmModuleService::GetEvent() 1
 | ||||
|             index = WaitAny(m_system.Kernel(), | ||||
|                             &m_event->GetReadableEvent(), // 0
 | ||||
|                             &m_alarm_worker.GetEvent()    // 1
 | ||||
|             ); | ||||
|         } else { | ||||
|             AddWaiter(&m_event->GetReadableEvent(), EventType::Exit); | ||||
|             // TODO
 | ||||
|             // AddWaiter(gIPmModuleService::GetEvent(), 1);
 | ||||
|             AddWaiter(&m_alarm_worker.GetEvent(), EventType::PowerStateChange); | ||||
|             AddWaiter(&m_alarm_worker.GetTimerEvent().GetReadableEvent(), EventType::SignalAlarms); | ||||
|             AddWaiter(m_local_clock_event, EventType::UpdateLocalSystemClock); | ||||
|             AddWaiter(m_network_clock_event, EventType::UpdateNetworkSystemClock); | ||||
|             AddWaiter(m_ephemeral_clock_event, EventType::UpdateEphemeralSystemClock); | ||||
|             AddWaiter(&m_timer_steady_clock->GetReadableEvent(), EventType::UpdateSteadyClock); | ||||
|             AddWaiter(&m_timer_file_system->GetReadableEvent(), EventType::UpdateFileTimestamp); | ||||
|             AddWaiter(m_standard_user_auto_correct_clock_event, EventType::AutoCorrect); | ||||
|             // TODO: gIPmModuleService::GetEvent() 1
 | ||||
|             index = WaitAny(m_system.Kernel(), | ||||
|                             &m_event->GetReadableEvent(),                       // 0
 | ||||
|                             &m_alarm_worker.GetEvent(),                         // 1
 | ||||
|                             &m_alarm_worker.GetTimerEvent().GetReadableEvent(), // 2
 | ||||
|                             m_local_clock_event,                                // 3
 | ||||
|                             m_network_clock_event,                              // 4
 | ||||
|                             m_ephemeral_clock_event,                            // 5
 | ||||
|                             &m_timer_steady_clock->GetReadableEvent(),          // 6
 | ||||
|                             &m_timer_file_system->GetReadableEvent(),           // 7
 | ||||
|                             m_standard_user_auto_correct_clock_event            // 8
 | ||||
|             ); | ||||
|         } | ||||
| 
 | ||||
|         s32 out_index{-1}; | ||||
|         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]) { | ||||
|         switch (static_cast<EventType>(index)) { | ||||
|         case EventType::Exit: | ||||
|             return; | ||||
| 
 | ||||
|         case EventType::IpmModuleService_GetEvent: | ||||
|             // TODO
 | ||||
|             // IPmModuleService::GetEvent()
 | ||||
|             // clear the event
 | ||||
|             // Handle power state change event
 | ||||
|             break; | ||||
| 
 | ||||
|         case EventType::PowerStateChange: | ||||
|             m_alarm_worker.GetEvent().Clear(); | ||||
|             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(); | ||||
| 
 | ||||
|             Service::PSC::Time::SystemClockContext context{}; | ||||
|             auto res = m_local_clock->GetSystemClockContext(&context); | ||||
|             ASSERT(res == ResultSuccess); | ||||
|             R_ASSERT(m_local_clock->GetSystemClockContext(&context)); | ||||
| 
 | ||||
|             m_set_sys->SetUserSystemClockContext(context); | ||||
| 
 | ||||
|             m_file_timestamp_worker.SetFilesystemPosixTime(); | ||||
|         } break; | ||||
|             break; | ||||
|         } | ||||
| 
 | ||||
|         case EventType::UpdateNetworkSystemClock: { | ||||
|             m_network_clock_event->Clear(); | ||||
| 
 | ||||
|             Service::PSC::Time::SystemClockContext context{}; | ||||
|             auto res = m_network_clock->GetSystemClockContext(&context); | ||||
|             ASSERT(res == ResultSuccess); | ||||
|             R_ASSERT(m_network_clock->GetSystemClockContext(&context)); | ||||
| 
 | ||||
|             m_set_sys->SetNetworkSystemClockContext(context); | ||||
| 
 | ||||
|             s64 time{}; | ||||
| @ -267,7 +232,8 @@ void TimeWorker::ThreadFunc(std::stop_token stop_token) { | ||||
|             } | ||||
| 
 | ||||
|             m_file_timestamp_worker.SetFilesystemPosixTime(); | ||||
|         } break; | ||||
|             break; | ||||
|         } | ||||
| 
 | ||||
|         case EventType::UpdateEphemeralSystemClock: { | ||||
|             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) { | ||||
|                 g_ig_report_ephemeral_clock_context_set = true; | ||||
|             } | ||||
|         } break; | ||||
|             break; | ||||
|         } | ||||
| 
 | ||||
|         case EventType::UpdateSteadyClock: | ||||
|             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(); | ||||
| 
 | ||||
|             bool automatic_correction{}; | ||||
|             auto res = m_time_sm->IsStandardUserSystemClockAutomaticCorrectionEnabled( | ||||
|                 &automatic_correction); | ||||
|             ASSERT(res == ResultSuccess); | ||||
|             R_ASSERT(m_time_sm->IsStandardUserSystemClockAutomaticCorrectionEnabled( | ||||
|                 &automatic_correction)); | ||||
| 
 | ||||
|             Service::PSC::Time::SteadyClockTimePoint time_point{}; | ||||
|             res = m_time_sm->GetStandardUserSystemClockAutomaticCorrectionUpdatedTime(&time_point); | ||||
|             ASSERT(res == ResultSuccess); | ||||
|             R_ASSERT( | ||||
|                 m_time_sm->GetStandardUserSystemClockAutomaticCorrectionUpdatedTime(&time_point)); | ||||
| 
 | ||||
|             m_set_sys->SetUserSystemClockAutomaticCorrectionEnabled(automatic_correction); | ||||
|             m_set_sys->SetUserSystemClockAutomaticCorrectionUpdatedTime(time_point); | ||||
|         } break; | ||||
|             break; | ||||
|         } | ||||
| 
 | ||||
|         default: | ||||
|             UNREACHABLE(); | ||||
|             break; | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| @ -2,8 +2,8 @@ | ||||
| // SPDX-License-Identifier: GPL-2.0-or-later
 | ||||
| 
 | ||||
| #include "core/hle/kernel/k_event.h" | ||||
| #include "core/hle/service/event.h" | ||||
| #include "core/hle/service/kernel_helpers.h" | ||||
| #include "core/hle/service/os/event.h" | ||||
| 
 | ||||
| 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/hle/kernel/k_event.h" | ||||
| #include "core/hle/kernel/k_synchronization_object.h" | ||||
| #include "core/hle/service/mutex.h" | ||||
| #include "core/hle/service/os/mutex.h" | ||||
| 
 | ||||
| namespace Service { | ||||
| 
 | ||||
| @ -20,50 +20,91 @@ | ||||
| 
 | ||||
| namespace Service { | ||||
| 
 | ||||
| constexpr size_t MaximumWaitObjects = 0x40; | ||||
| 
 | ||||
| enum HandleType { | ||||
| enum class UserDataTag { | ||||
|     Port, | ||||
|     Session, | ||||
|     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.
 | ||||
|     m_event = Kernel::KEvent::Create(system.Kernel()); | ||||
|     m_event->Initialize(nullptr); | ||||
|     m_wakeup_event = Kernel::KEvent::Create(system.Kernel()); | ||||
|     m_wakeup_event->Initialize(nullptr); | ||||
| 
 | ||||
|     // 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() { | ||||
|     // Signal stop.
 | ||||
|     m_stop_source.request_stop(); | ||||
|     m_event->Signal(); | ||||
|     m_wakeup_event->Signal(); | ||||
| 
 | ||||
|     // Wait for processing to stop.
 | ||||
|     m_stopped.Wait(); | ||||
|     m_threads.clear(); | ||||
| 
 | ||||
|     // Clean up server ports.
 | ||||
|     for (const auto& [port, handler] : m_ports) { | ||||
|         port->Close(); | ||||
|     // Clean up ports.
 | ||||
|     for (auto it = m_servers.begin(); it != m_servers.end(); it = m_servers.erase(it)) { | ||||
|         delete std::addressof(*it); | ||||
|     } | ||||
| 
 | ||||
|     // Clean up sessions.
 | ||||
|     for (const auto& [session, manager] : m_sessions) { | ||||
|         session->Close(); | ||||
|     for (auto it = m_sessions.begin(); it != m_sessions.end(); it = m_sessions.erase(it)) { | ||||
|         delete std::addressof(*it); | ||||
|     } | ||||
| 
 | ||||
|     for (const auto& request : m_deferrals) { | ||||
|         request.session->Close(); | ||||
|     } | ||||
| 
 | ||||
|     // Close event.
 | ||||
|     m_event->GetReadableEvent().Close(); | ||||
|     m_event->Close(); | ||||
|     // Close wakeup event.
 | ||||
|     m_wakeup_event->GetReadableEvent().Close(); | ||||
|     m_wakeup_event->Close(); | ||||
| 
 | ||||
|     if (m_deferral_event) { | ||||
|         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)); | ||||
| } | ||||
| 
 | ||||
| Result ServerManager::RegisterSession(Kernel::KServerSession* session, | ||||
| Result ServerManager::RegisterSession(Kernel::KServerSession* server_session, | ||||
|                                       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.
 | ||||
|     auto* session = new Session(server_session, std::move(manager)); | ||||
| 
 | ||||
|     // Begin tracking the server session.
 | ||||
|     { | ||||
|         std::scoped_lock ll{m_list_mutex}; | ||||
|         m_sessions.emplace(session, std::move(manager)); | ||||
|         std::scoped_lock ll{m_deferred_list_mutex}; | ||||
|         m_sessions.push_back(*session); | ||||
|     } | ||||
| 
 | ||||
|     // Signal the wakeup event.
 | ||||
|     m_event->Signal(); | ||||
|     // Register to wait on the session.
 | ||||
|     this->LinkToDeferredList(session); | ||||
| 
 | ||||
|     R_SUCCEED(); | ||||
| } | ||||
| @ -95,21 +136,22 @@ Result ServerManager::RegisterSession(Kernel::KServerSession* session, | ||||
| Result ServerManager::RegisterNamedService(const std::string& service_name, | ||||
|                                            SessionRequestHandlerFactory&& handler_factory, | ||||
|                                            u32 max_sessions) { | ||||
|     ASSERT(m_sessions.size() + m_ports.size() < MaximumWaitObjects); | ||||
| 
 | ||||
|     // Add the new server to sm: and get the moved server port.
 | ||||
|     Kernel::KServerPort* server_port{}; | ||||
|     R_ASSERT(m_system.ServiceManager().RegisterService(std::addressof(server_port), service_name, | ||||
|                                                        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.
 | ||||
|     { | ||||
|         std::scoped_lock ll{m_list_mutex}; | ||||
|         m_ports.emplace(server_port, std::move(handler_factory)); | ||||
|         std::scoped_lock ll{m_deferred_list_mutex}; | ||||
|         m_servers.push_back(*server); | ||||
|     } | ||||
| 
 | ||||
|     // Signal the wakeup event.
 | ||||
|     m_event->Signal(); | ||||
|     // Register to wait on the server port.
 | ||||
|     this->LinkToDeferredList(server); | ||||
| 
 | ||||
|     R_SUCCEED(); | ||||
| } | ||||
| @ -127,8 +169,6 @@ Result ServerManager::RegisterNamedService(const std::string& service_name, | ||||
| Result ServerManager::ManageNamedPort(const std::string& service_name, | ||||
|                                       SessionRequestHandlerFactory&& handler_factory, | ||||
|                                       u32 max_sessions) { | ||||
|     ASSERT(m_sessions.size() + m_ports.size() < MaximumWaitObjects); | ||||
| 
 | ||||
|     // Create a new port.
 | ||||
|     auto* port = Kernel::KPort::Create(m_system.Kernel()); | ||||
|     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.
 | ||||
|     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}; | ||||
|         m_ports.emplace(std::addressof(port->GetServerPort()), std::move(handler_factory)); | ||||
|         std::scoped_lock ll{m_deferred_list_mutex}; | ||||
|         m_servers.push_back(*server); | ||||
|     } | ||||
| 
 | ||||
|     // Register to wait on the port.
 | ||||
|     this->LinkToDeferredList(server); | ||||
| 
 | ||||
|     // We succeeded.
 | ||||
|     R_SUCCEED(); | ||||
| } | ||||
| @ -173,6 +219,11 @@ Result ServerManager::ManageDeferral(Kernel::KEvent** out_event) { | ||||
|     // Set the output.
 | ||||
|     *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.
 | ||||
|     R_SUCCEED(); | ||||
| } | ||||
| @ -191,270 +242,185 @@ Result ServerManager::LoopProcess() { | ||||
|     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() { | ||||
|     while (!m_stop_source.stop_requested()) { | ||||
|         R_TRY(this->WaitAndProcessImpl()); | ||||
|         this->WaitAndProcessImpl(); | ||||
|     } | ||||
| 
 | ||||
|     R_SUCCEED(); | ||||
| } | ||||
| 
 | ||||
| Result ServerManager::WaitAndProcessImpl() { | ||||
|     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) { | ||||
| Result ServerManager::OnPortEvent(Port* server) { | ||||
|     // Accept a new server session.
 | ||||
|     Kernel::KServerSession* session = port->AcceptSession(); | ||||
|     ASSERT(session != nullptr); | ||||
|     auto* server_port = static_cast<Kernel::KServerPort*>(server->GetNativeHandle()); | ||||
|     Kernel::KServerSession* server_session = server_port->AcceptSession(); | ||||
|     ASSERT(server_session != nullptr); | ||||
| 
 | ||||
|     // Create the session manager and install the handler.
 | ||||
|     auto manager = std::make_shared<SessionRequestManager>(m_system.Kernel(), *this); | ||||
|     manager->SetSessionHandler(handler_factory()); | ||||
|     manager->SetSessionHandler(server->CreateHandler()); | ||||
| 
 | ||||
|     // Track the server session.
 | ||||
|     { | ||||
|         std::scoped_lock ll{m_list_mutex}; | ||||
|         m_ports.emplace(port, std::move(handler_factory)); | ||||
|         m_sessions.emplace(session, std::move(manager)); | ||||
|     } | ||||
|     // Create and register the new session.
 | ||||
|     this->RegisterSession(server_session, std::move(manager)); | ||||
| 
 | ||||
|     // Signal the wakeup event.
 | ||||
|     m_event->Signal(); | ||||
|     // Resume tracking the port.
 | ||||
|     this->LinkToDeferredList(server); | ||||
| 
 | ||||
|     // We succeeded.
 | ||||
|     R_SUCCEED(); | ||||
| } | ||||
| 
 | ||||
| Result ServerManager::OnSessionEvent(Kernel::KServerSession* session, | ||||
|                                      std::shared_ptr<SessionRequestManager>&& manager) { | ||||
|     Result rc{ResultSuccess}; | ||||
| Result ServerManager::OnSessionEvent(Session* session) { | ||||
|     Result res = ResultSuccess; | ||||
| 
 | ||||
|     // Try to receive a message.
 | ||||
|     std::shared_ptr<HLERequestContext> context; | ||||
|     rc = session->ReceiveRequestHLE(&context, manager); | ||||
|     auto* server_session = static_cast<Kernel::KServerSession*>(session->GetNativeHandle()); | ||||
|     res = server_session->ReceiveRequestHLE(&session->GetContext(), session->GetManager()); | ||||
| 
 | ||||
|     // If the session has been closed, we're done.
 | ||||
|     if (rc == Kernel::ResultSessionClosed) { | ||||
|         // Close the session.
 | ||||
|         session->Close(); | ||||
| 
 | ||||
|         // Finish.
 | ||||
|     if (res == Kernel::ResultSessionClosed) { | ||||
|         this->DestroySession(session); | ||||
|         R_SUCCEED(); | ||||
|     } | ||||
|     ASSERT(R_SUCCEEDED(rc)); | ||||
| 
 | ||||
|     RequestState request{ | ||||
|         .session = session, | ||||
|         .context = std::move(context), | ||||
|         .manager = std::move(manager), | ||||
|     }; | ||||
|     R_ASSERT(res); | ||||
| 
 | ||||
|     // 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 rc{ResultSuccess}; | ||||
|     Result service_rc{ResultSuccess}; | ||||
| Result ServerManager::CompleteSyncRequest(Session* session) { | ||||
|     Result res = ResultSuccess; | ||||
|     Result service_res = ResultSuccess; | ||||
| 
 | ||||
|     // Mark the request as not deferred.
 | ||||
|     request.context->SetIsDeferred(false); | ||||
|     session->GetContext()->SetIsDeferred(false); | ||||
| 
 | ||||
|     // 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 (request.context->GetIsDeferred()) { | ||||
|         // Insert into deferral list.
 | ||||
|         std::scoped_lock ll{m_list_mutex}; | ||||
|         m_deferrals.emplace_back(std::move(request)); | ||||
|     if (session->GetContext()->GetIsDeferred()) { | ||||
|         // Insert into deferred session list.
 | ||||
|         std::scoped_lock ll{m_deferred_list_mutex}; | ||||
|         m_deferred_sessions.push_back(session); | ||||
| 
 | ||||
|         // Finish.
 | ||||
|         R_SUCCEED(); | ||||
|     } | ||||
| 
 | ||||
|     // Send the reply.
 | ||||
|     rc = request.session->SendReplyHLE(); | ||||
|     res = server_session->SendReplyHLE(); | ||||
| 
 | ||||
|     // If the session has been closed, we're done.
 | ||||
|     if (rc == Kernel::ResultSessionClosed || service_rc == IPC::ResultSessionClosed) { | ||||
|         // Close the session.
 | ||||
|         request.session->Close(); | ||||
| 
 | ||||
|         // Finish.
 | ||||
|     if (res == Kernel::ResultSessionClosed || service_res == IPC::ResultSessionClosed) { | ||||
|         this->DestroySession(session); | ||||
|         R_SUCCEED(); | ||||
|     } | ||||
| 
 | ||||
|     ASSERT(R_SUCCEEDED(rc)); | ||||
|     ASSERT(R_SUCCEEDED(service_rc)); | ||||
|     R_ASSERT(res); | ||||
|     R_ASSERT(service_res); | ||||
| 
 | ||||
|     // Reinsert the session.
 | ||||
|     { | ||||
|         std::scoped_lock ll{m_list_mutex}; | ||||
|         m_sessions.emplace(request.session, std::move(request.manager)); | ||||
|     } | ||||
|     // We succeeded, so we can process future messages on this session.
 | ||||
|     this->LinkToDeferredList(session); | ||||
| 
 | ||||
|     // Signal the wakeup event.
 | ||||
|     m_event->Signal(); | ||||
| 
 | ||||
|     // We succeeded.
 | ||||
|     R_SUCCEED(); | ||||
| } | ||||
| 
 | ||||
| Result ServerManager::OnDeferralEvent(std::list<RequestState>&& deferrals) { | ||||
|     ON_RESULT_FAILURE { | ||||
|         std::scoped_lock ll{m_list_mutex}; | ||||
|         m_deferrals.splice(m_deferrals.end(), deferrals); | ||||
|     }; | ||||
| Result ServerManager::OnDeferralEvent() { | ||||
|     // Clear event before grabbing the list.
 | ||||
|     m_deferral_event->Clear(); | ||||
| 
 | ||||
|     while (!deferrals.empty()) { | ||||
|         RequestState request = deferrals.front(); | ||||
|         deferrals.pop_front(); | ||||
|     // Get and clear list.
 | ||||
|     const auto deferrals = [&] { | ||||
|         std::scoped_lock lk{m_deferred_list_mutex}; | ||||
|         return std::move(m_deferred_sessions); | ||||
|     }(); | ||||
| 
 | ||||
|         // Try again to complete the request.
 | ||||
|         R_TRY(this->CompleteSyncRequest(std::move(request))); | ||||
|     // Relink deferral event.
 | ||||
|     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(); | ||||
| } | ||||
| 
 | ||||
| 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
 | ||||
|  | ||||
| @ -3,18 +3,17 @@ | ||||
| 
 | ||||
| #pragma once | ||||
| 
 | ||||
| #include <functional> | ||||
| #include <list> | ||||
| #include <map> | ||||
| #include <mutex> | ||||
| #include <string_view> | ||||
| #include <optional> | ||||
| #include <vector> | ||||
| 
 | ||||
| #include "common/polyfill_thread.h" | ||||
| #include "common/thread.h" | ||||
| #include "core/hle/result.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 { | ||||
| class System; | ||||
| @ -24,11 +23,13 @@ namespace Kernel { | ||||
| class KEvent; | ||||
| class KServerPort; | ||||
| class KServerSession; | ||||
| class KSynchronizationObject; | ||||
| } // namespace Kernel
 | ||||
| 
 | ||||
| namespace Service { | ||||
| 
 | ||||
| class Port; | ||||
| class Session; | ||||
| 
 | ||||
| class ServerManager { | ||||
| public: | ||||
|     explicit ServerManager(Core::System& system); | ||||
| @ -52,34 +53,40 @@ public: | ||||
|     static void RunServer(std::unique_ptr<ServerManager>&& server); | ||||
| 
 | ||||
| private: | ||||
|     struct RequestState; | ||||
| 
 | ||||
|     void LinkToDeferredList(MultiWaitHolder* holder); | ||||
|     void LinkDeferred(); | ||||
|     MultiWaitHolder* WaitSignaled(); | ||||
|     Result Process(MultiWaitHolder* holder); | ||||
|     bool WaitAndProcessImpl(); | ||||
|     Result LoopProcessImpl(); | ||||
|     Result WaitAndProcessImpl(); | ||||
|     Result OnPortEvent(Kernel::KServerPort* port, SessionRequestHandlerFactory&& handler_factory); | ||||
|     Result OnSessionEvent(Kernel::KServerSession* session, | ||||
|                           std::shared_ptr<SessionRequestManager>&& manager); | ||||
|     Result OnDeferralEvent(std::list<RequestState>&& deferrals); | ||||
|     Result CompleteSyncRequest(RequestState&& state); | ||||
| 
 | ||||
|     Result OnPortEvent(Port* port); | ||||
|     Result OnSessionEvent(Session* session); | ||||
|     Result OnDeferralEvent(); | ||||
|     Result CompleteSyncRequest(Session* session); | ||||
| 
 | ||||
| private: | ||||
|     void DestroySession(Session* session); | ||||
| 
 | ||||
| private: | ||||
|     Core::System& m_system; | ||||
|     Mutex m_serve_mutex; | ||||
|     std::mutex m_list_mutex; | ||||
|     Mutex m_selection_mutex; | ||||
| 
 | ||||
|     // Guest state tracking
 | ||||
|     std::map<Kernel::KServerPort*, SessionRequestHandlerFactory> m_ports{}; | ||||
|     std::map<Kernel::KServerSession*, std::shared_ptr<SessionRequestManager>> m_sessions{}; | ||||
|     Kernel::KEvent* m_event{}; | ||||
|     // Events
 | ||||
|     Kernel::KEvent* m_wakeup_event{}; | ||||
|     Kernel::KEvent* m_deferral_event{}; | ||||
| 
 | ||||
|     // Deferral tracking
 | ||||
|     struct RequestState { | ||||
|         Kernel::KServerSession* session; | ||||
|         std::shared_ptr<HLERequestContext> context; | ||||
|         std::shared_ptr<SessionRequestManager> manager; | ||||
|     }; | ||||
|     std::list<RequestState> m_deferrals{}; | ||||
|     // Deferred wait list
 | ||||
|     std::mutex m_deferred_list_mutex{}; | ||||
|     MultiWait m_deferred_list{}; | ||||
| 
 | ||||
|     // Guest state tracking
 | ||||
|     MultiWait m_multi_wait{}; | ||||
|     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
 | ||||
|     Common::Event m_stopped{}; | ||||
|  | ||||
		Loading…
	
		Reference in New Issue
	
	Block a user