mirror of
https://github.com/yuzu-emu/yuzu.git
synced 2024-07-04 23:31:19 +01:00
sm:: support service registration deferral
This commit is contained in:
parent
a936972614
commit
6e0a33089b
5 changed files with 151 additions and 8 deletions
|
@ -358,6 +358,14 @@ public:
|
||||||
return manager.lock();
|
return manager.lock();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool GetIsDeferred() const {
|
||||||
|
return is_deferred;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SetIsDeferred(bool is_deferred_ = true) {
|
||||||
|
is_deferred = is_deferred_;
|
||||||
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
friend class IPC::ResponseBuilder;
|
friend class IPC::ResponseBuilder;
|
||||||
|
|
||||||
|
@ -392,6 +400,7 @@ private:
|
||||||
u32 domain_offset{};
|
u32 domain_offset{};
|
||||||
|
|
||||||
std::weak_ptr<SessionRequestManager> manager{};
|
std::weak_ptr<SessionRequestManager> manager{};
|
||||||
|
bool is_deferred{false};
|
||||||
|
|
||||||
KernelCore& kernel;
|
KernelCore& kernel;
|
||||||
Core::Memory::Memory& memory;
|
Core::Memory::Memory& memory;
|
||||||
|
|
|
@ -25,6 +25,7 @@ constexpr size_t MaximumWaitObjects = 0x40;
|
||||||
enum HandleType {
|
enum HandleType {
|
||||||
Port,
|
Port,
|
||||||
Session,
|
Session,
|
||||||
|
DeferEvent,
|
||||||
Event,
|
Event,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -53,9 +54,18 @@ ServerManager::~ServerManager() {
|
||||||
session->Close();
|
session->Close();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for (const auto& request : m_deferrals) {
|
||||||
|
request.session->Close();
|
||||||
|
}
|
||||||
|
|
||||||
// Close event.
|
// Close event.
|
||||||
m_event->GetReadableEvent().Close();
|
m_event->GetReadableEvent().Close();
|
||||||
m_event->Close();
|
m_event->Close();
|
||||||
|
|
||||||
|
if (m_deferral_event) {
|
||||||
|
m_deferral_event->GetReadableEvent().Close();
|
||||||
|
// Write event is owned by ServiceManager
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void ServerManager::RunServer(std::unique_ptr<ServerManager>&& server_manager) {
|
void ServerManager::RunServer(std::unique_ptr<ServerManager>&& server_manager) {
|
||||||
|
@ -142,6 +152,21 @@ Result ServerManager::ManageNamedPort(const std::string& service_name,
|
||||||
R_SUCCEED();
|
R_SUCCEED();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Result ServerManager::ManageDeferral(Kernel::KEvent** out_event) {
|
||||||
|
// Create a new event.
|
||||||
|
m_deferral_event = Kernel::KEvent::Create(m_system.Kernel());
|
||||||
|
ASSERT(m_deferral_event != nullptr);
|
||||||
|
|
||||||
|
// Initialize the event.
|
||||||
|
m_deferral_event->Initialize(nullptr);
|
||||||
|
|
||||||
|
// Set the output.
|
||||||
|
*out_event = m_deferral_event;
|
||||||
|
|
||||||
|
// We succeeded.
|
||||||
|
R_SUCCEED();
|
||||||
|
}
|
||||||
|
|
||||||
void ServerManager::StartAdditionalHostThreads(const char* name, size_t num_threads) {
|
void ServerManager::StartAdditionalHostThreads(const char* name, size_t num_threads) {
|
||||||
for (size_t i = 0; i < num_threads; i++) {
|
for (size_t i = 0; i < num_threads; i++) {
|
||||||
auto thread_name = fmt::format("{}:{}", name, i + 1);
|
auto thread_name = fmt::format("{}:{}", name, i + 1);
|
||||||
|
@ -207,6 +232,11 @@ Result ServerManager::WaitAndProcessImpl() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Add the deferral wakeup event.
|
||||||
|
if (m_deferral_event != nullptr) {
|
||||||
|
AddWaiter(std::addressof(m_deferral_event->GetReadableEvent()), HandleType::DeferEvent);
|
||||||
|
}
|
||||||
|
|
||||||
// Add the wakeup event.
|
// Add the wakeup event.
|
||||||
AddWaiter(std::addressof(m_event->GetReadableEvent()), HandleType::Event);
|
AddWaiter(std::addressof(m_event->GetReadableEvent()), HandleType::Event);
|
||||||
|
|
||||||
|
@ -270,6 +300,23 @@ Result ServerManager::WaitAndProcessImpl() {
|
||||||
// Finish.
|
// Finish.
|
||||||
R_RETURN(this->OnSessionEvent(session, std::move(manager)));
|
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: {
|
case HandleType::Event: {
|
||||||
// Clear event and finish.
|
// Clear event and finish.
|
||||||
R_RETURN(m_event->Clear());
|
R_RETURN(m_event->Clear());
|
||||||
|
@ -308,7 +355,6 @@ Result ServerManager::OnPortEvent(Kernel::KServerPort* port,
|
||||||
Result ServerManager::OnSessionEvent(Kernel::KServerSession* session,
|
Result ServerManager::OnSessionEvent(Kernel::KServerSession* session,
|
||||||
std::shared_ptr<Kernel::SessionRequestManager>&& manager) {
|
std::shared_ptr<Kernel::SessionRequestManager>&& manager) {
|
||||||
Result rc{ResultSuccess};
|
Result rc{ResultSuccess};
|
||||||
Result service_rc{ResultSuccess};
|
|
||||||
|
|
||||||
// Try to receive a message.
|
// Try to receive a message.
|
||||||
std::shared_ptr<Kernel::HLERequestContext> context;
|
std::shared_ptr<Kernel::HLERequestContext> context;
|
||||||
|
@ -324,16 +370,43 @@ Result ServerManager::OnSessionEvent(Kernel::KServerSession* session,
|
||||||
}
|
}
|
||||||
ASSERT(R_SUCCEEDED(rc));
|
ASSERT(R_SUCCEEDED(rc));
|
||||||
|
|
||||||
|
RequestState request{
|
||||||
|
.session = session,
|
||||||
|
.context = std::move(context),
|
||||||
|
.manager = std::move(manager),
|
||||||
|
};
|
||||||
|
|
||||||
|
// Complete the sync request with deferral handling.
|
||||||
|
R_RETURN(this->CompleteSyncRequest(std::move(request)));
|
||||||
|
}
|
||||||
|
|
||||||
|
Result ServerManager::CompleteSyncRequest(RequestState&& request) {
|
||||||
|
Result rc{ResultSuccess};
|
||||||
|
Result service_rc{ResultSuccess};
|
||||||
|
|
||||||
|
// Mark the request as not deferred.
|
||||||
|
request.context->SetIsDeferred(false);
|
||||||
|
|
||||||
// Complete the request. We have exclusive access to this session.
|
// Complete the request. We have exclusive access to this session.
|
||||||
service_rc = manager->CompleteSyncRequest(session, *context);
|
service_rc = request.manager->CompleteSyncRequest(request.session, *request.context);
|
||||||
|
|
||||||
|
// 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));
|
||||||
|
|
||||||
|
// Finish.
|
||||||
|
R_SUCCEED();
|
||||||
|
}
|
||||||
|
|
||||||
// Send the reply.
|
// Send the reply.
|
||||||
rc = session->SendReplyHLE();
|
rc = request.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::ERR_REMOTE_PROCESS_DEAD) {
|
if (rc == Kernel::ResultSessionClosed || service_rc == IPC::ERR_REMOTE_PROCESS_DEAD) {
|
||||||
// Close the session.
|
// Close the session.
|
||||||
session->Close();
|
request.session->Close();
|
||||||
|
|
||||||
// Finish.
|
// Finish.
|
||||||
R_SUCCEED();
|
R_SUCCEED();
|
||||||
|
@ -345,7 +418,7 @@ Result ServerManager::OnSessionEvent(Kernel::KServerSession* session,
|
||||||
// Reinsert the session.
|
// Reinsert the session.
|
||||||
{
|
{
|
||||||
std::scoped_lock ll{m_list_mutex};
|
std::scoped_lock ll{m_list_mutex};
|
||||||
m_sessions.emplace(session, std::move(manager));
|
m_sessions.emplace(request.session, std::move(request.manager));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Signal the wakeup event.
|
// Signal the wakeup event.
|
||||||
|
@ -355,4 +428,21 @@ Result ServerManager::OnSessionEvent(Kernel::KServerSession* session,
|
||||||
R_SUCCEED();
|
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);
|
||||||
|
};
|
||||||
|
|
||||||
|
while (!deferrals.empty()) {
|
||||||
|
RequestState request = deferrals.front();
|
||||||
|
deferrals.pop_front();
|
||||||
|
|
||||||
|
// Try again to complete the request.
|
||||||
|
R_TRY(this->CompleteSyncRequest(std::move(request)));
|
||||||
|
}
|
||||||
|
|
||||||
|
R_SUCCEED();
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace Service
|
} // namespace Service
|
||||||
|
|
|
@ -5,6 +5,7 @@
|
||||||
|
|
||||||
#include <atomic>
|
#include <atomic>
|
||||||
#include <functional>
|
#include <functional>
|
||||||
|
#include <list>
|
||||||
#include <map>
|
#include <map>
|
||||||
#include <mutex>
|
#include <mutex>
|
||||||
#include <string_view>
|
#include <string_view>
|
||||||
|
@ -19,6 +20,7 @@ class System;
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace Kernel {
|
namespace Kernel {
|
||||||
|
class HLERequestContext;
|
||||||
class KEvent;
|
class KEvent;
|
||||||
class KServerPort;
|
class KServerPort;
|
||||||
class KServerSession;
|
class KServerSession;
|
||||||
|
@ -42,6 +44,7 @@ public:
|
||||||
Result ManageNamedPort(const std::string& service_name,
|
Result ManageNamedPort(const std::string& service_name,
|
||||||
std::shared_ptr<Kernel::SessionRequestHandler>&& handler,
|
std::shared_ptr<Kernel::SessionRequestHandler>&& handler,
|
||||||
u32 max_sessions = 64);
|
u32 max_sessions = 64);
|
||||||
|
Result ManageDeferral(Kernel::KEvent** out_event);
|
||||||
|
|
||||||
Result LoopProcess();
|
Result LoopProcess();
|
||||||
void StartAdditionalHostThreads(const char* name, size_t num_threads);
|
void StartAdditionalHostThreads(const char* name, size_t num_threads);
|
||||||
|
@ -49,12 +52,16 @@ public:
|
||||||
static void RunServer(std::unique_ptr<ServerManager>&& server);
|
static void RunServer(std::unique_ptr<ServerManager>&& server);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
struct RequestState;
|
||||||
|
|
||||||
Result LoopProcessImpl();
|
Result LoopProcessImpl();
|
||||||
Result WaitAndProcessImpl();
|
Result WaitAndProcessImpl();
|
||||||
Result OnPortEvent(Kernel::KServerPort* port,
|
Result OnPortEvent(Kernel::KServerPort* port,
|
||||||
std::shared_ptr<Kernel::SessionRequestHandler>&& handler);
|
std::shared_ptr<Kernel::SessionRequestHandler>&& handler);
|
||||||
Result OnSessionEvent(Kernel::KServerSession* session,
|
Result OnSessionEvent(Kernel::KServerSession* session,
|
||||||
std::shared_ptr<Kernel::SessionRequestManager>&& manager);
|
std::shared_ptr<Kernel::SessionRequestManager>&& manager);
|
||||||
|
Result OnDeferralEvent(std::list<RequestState>&& deferrals);
|
||||||
|
Result CompleteSyncRequest(RequestState&& state);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Core::System& m_system;
|
Core::System& m_system;
|
||||||
|
@ -65,6 +72,15 @@ private:
|
||||||
std::map<Kernel::KServerPort*, std::shared_ptr<Kernel::SessionRequestHandler>> m_ports{};
|
std::map<Kernel::KServerPort*, std::shared_ptr<Kernel::SessionRequestHandler>> m_ports{};
|
||||||
std::map<Kernel::KServerSession*, std::shared_ptr<Kernel::SessionRequestManager>> m_sessions{};
|
std::map<Kernel::KServerSession*, std::shared_ptr<Kernel::SessionRequestManager>> m_sessions{};
|
||||||
Kernel::KEvent* m_event{};
|
Kernel::KEvent* m_event{};
|
||||||
|
Kernel::KEvent* m_deferral_event{};
|
||||||
|
|
||||||
|
// Deferral tracking
|
||||||
|
struct RequestState {
|
||||||
|
Kernel::KServerSession* session;
|
||||||
|
std::shared_ptr<Kernel::HLERequestContext> context;
|
||||||
|
std::shared_ptr<Kernel::SessionRequestManager> manager;
|
||||||
|
};
|
||||||
|
std::list<RequestState> m_deferrals{};
|
||||||
|
|
||||||
// Host state tracking
|
// Host state tracking
|
||||||
std::atomic<bool> m_stopped{};
|
std::atomic<bool> m_stopped{};
|
||||||
|
|
|
@ -32,6 +32,10 @@ ServiceManager::~ServiceManager() {
|
||||||
port->GetClientPort().Close();
|
port->GetClientPort().Close();
|
||||||
port->GetServerPort().Close();
|
port->GetServerPort().Close();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (deferral_event) {
|
||||||
|
deferral_event->Close();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void ServiceManager::InvokeControlRequest(Kernel::HLERequestContext& context) {
|
void ServiceManager::InvokeControlRequest(Kernel::HLERequestContext& context) {
|
||||||
|
@ -62,6 +66,9 @@ Result ServiceManager::RegisterService(std::string name, u32 max_sessions,
|
||||||
|
|
||||||
service_ports.emplace(name, port);
|
service_ports.emplace(name, port);
|
||||||
registered_services.emplace(name, handler);
|
registered_services.emplace(name, handler);
|
||||||
|
if (deferral_event) {
|
||||||
|
deferral_event->Signal();
|
||||||
|
}
|
||||||
|
|
||||||
return ResultSuccess;
|
return ResultSuccess;
|
||||||
}
|
}
|
||||||
|
@ -88,7 +95,7 @@ ResultVal<Kernel::KPort*> ServiceManager::GetServicePort(const std::string& name
|
||||||
std::scoped_lock lk{lock};
|
std::scoped_lock lk{lock};
|
||||||
auto it = service_ports.find(name);
|
auto it = service_ports.find(name);
|
||||||
if (it == service_ports.end()) {
|
if (it == service_ports.end()) {
|
||||||
LOG_ERROR(Service_SM, "Server is not registered! service={}", name);
|
LOG_WARNING(Service_SM, "Server is not registered! service={}", name);
|
||||||
return ERR_SERVICE_NOT_REGISTERED;
|
return ERR_SERVICE_NOT_REGISTERED;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -113,6 +120,11 @@ void SM::Initialize(Kernel::HLERequestContext& ctx) {
|
||||||
|
|
||||||
void SM::GetService(Kernel::HLERequestContext& ctx) {
|
void SM::GetService(Kernel::HLERequestContext& ctx) {
|
||||||
auto result = GetServiceImpl(ctx);
|
auto result = GetServiceImpl(ctx);
|
||||||
|
if (ctx.GetIsDeferred()) {
|
||||||
|
// Don't overwrite the command buffer.
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (result.Succeeded()) {
|
if (result.Succeeded()) {
|
||||||
IPC::ResponseBuilder rb{ctx, 2, 0, 1, IPC::ResponseBuilder::Flags::AlwaysMoveHandles};
|
IPC::ResponseBuilder rb{ctx, 2, 0, 1, IPC::ResponseBuilder::Flags::AlwaysMoveHandles};
|
||||||
rb.Push(result.Code());
|
rb.Push(result.Code());
|
||||||
|
@ -125,6 +137,11 @@ void SM::GetService(Kernel::HLERequestContext& ctx) {
|
||||||
|
|
||||||
void SM::GetServiceTipc(Kernel::HLERequestContext& ctx) {
|
void SM::GetServiceTipc(Kernel::HLERequestContext& ctx) {
|
||||||
auto result = GetServiceImpl(ctx);
|
auto result = GetServiceImpl(ctx);
|
||||||
|
if (ctx.GetIsDeferred()) {
|
||||||
|
// Don't overwrite the command buffer.
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
IPC::ResponseBuilder rb{ctx, 2, 0, 1, IPC::ResponseBuilder::Flags::AlwaysMoveHandles};
|
IPC::ResponseBuilder rb{ctx, 2, 0, 1, IPC::ResponseBuilder::Flags::AlwaysMoveHandles};
|
||||||
rb.Push(result.Code());
|
rb.Push(result.Code());
|
||||||
rb.PushMoveObjects(result.Succeeded() ? result.Unwrap() : nullptr);
|
rb.PushMoveObjects(result.Succeeded() ? result.Unwrap() : nullptr);
|
||||||
|
@ -152,8 +169,9 @@ ResultVal<Kernel::KClientSession*> SM::GetServiceImpl(Kernel::HLERequestContext&
|
||||||
// Find the named port.
|
// Find the named port.
|
||||||
auto port_result = service_manager.GetServicePort(name);
|
auto port_result = service_manager.GetServicePort(name);
|
||||||
if (port_result.Failed()) {
|
if (port_result.Failed()) {
|
||||||
LOG_ERROR(Service_SM, "called service={} -> error 0x{:08X}", name, port_result.Code().raw);
|
LOG_INFO(Service_SM, "Waiting for service {} to become available", name);
|
||||||
return port_result.Code();
|
ctx.SetIsDeferred();
|
||||||
|
return ERR_SERVICE_NOT_REGISTERED;
|
||||||
}
|
}
|
||||||
auto& port = port_result.Unwrap();
|
auto& port = port_result.Unwrap();
|
||||||
|
|
||||||
|
@ -228,8 +246,13 @@ SM::SM(ServiceManager& service_manager_, Core::System& system_)
|
||||||
SM::~SM() = default;
|
SM::~SM() = default;
|
||||||
|
|
||||||
void LoopProcess(Core::System& system) {
|
void LoopProcess(Core::System& system) {
|
||||||
|
auto& service_manager = system.ServiceManager();
|
||||||
auto server_manager = std::make_unique<ServerManager>(system);
|
auto server_manager = std::make_unique<ServerManager>(system);
|
||||||
|
|
||||||
|
Kernel::KEvent* deferral_event{};
|
||||||
|
server_manager->ManageDeferral(&deferral_event);
|
||||||
|
service_manager.SetDeferralEvent(deferral_event);
|
||||||
|
|
||||||
server_manager->ManageNamedPort("sm:", std::make_shared<SM>(system.ServiceManager(), system));
|
server_manager->ManageNamedPort("sm:", std::make_shared<SM>(system.ServiceManager(), system));
|
||||||
ServerManager::RunServer(std::move(server_manager));
|
ServerManager::RunServer(std::move(server_manager));
|
||||||
}
|
}
|
||||||
|
|
|
@ -71,6 +71,10 @@ public:
|
||||||
|
|
||||||
void InvokeControlRequest(Kernel::HLERequestContext& context);
|
void InvokeControlRequest(Kernel::HLERequestContext& context);
|
||||||
|
|
||||||
|
void SetDeferralEvent(Kernel::KEvent* deferral_event_) {
|
||||||
|
deferral_event = deferral_event_;
|
||||||
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::shared_ptr<SM> sm_interface;
|
std::shared_ptr<SM> sm_interface;
|
||||||
std::unique_ptr<Controller> controller_interface;
|
std::unique_ptr<Controller> controller_interface;
|
||||||
|
@ -82,6 +86,7 @@ private:
|
||||||
|
|
||||||
/// Kernel context
|
/// Kernel context
|
||||||
Kernel::KernelCore& kernel;
|
Kernel::KernelCore& kernel;
|
||||||
|
Kernel::KEvent* deferral_event{};
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Runs SM services.
|
/// Runs SM services.
|
||||||
|
|
Loading…
Reference in a new issue