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/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 {
|
while (!stop_token.stop_requested()) {
|
||||||
|
enum class EventType : s32 {
|
||||||
Exit = 0,
|
Exit = 0,
|
||||||
IpmModuleService_GetEvent = 1,
|
PowerStateChange = 1,
|
||||||
PowerStateChange = 2,
|
SignalAlarms = 2,
|
||||||
SignalAlarms = 3,
|
UpdateLocalSystemClock = 3,
|
||||||
UpdateLocalSystemClock = 4,
|
UpdateNetworkSystemClock = 4,
|
||||||
UpdateNetworkSystemClock = 5,
|
UpdateEphemeralSystemClock = 5,
|
||||||
UpdateEphemeralSystemClock = 6,
|
UpdateSteadyClock = 6,
|
||||||
UpdateSteadyClock = 7,
|
UpdateFileTimestamp = 7,
|
||||||
UpdateFileTimestamp = 8,
|
AutoCorrect = 8,
|
||||||
AutoCorrect = 9,
|
|
||||||
Max = 10,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
s32 num_objs{};
|
s32 index{};
|
||||||
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();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
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 {
|
||||||
|
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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{};
|
||||||
|
|
Loading…
Reference in New Issue