mirror of
				https://git.suyu.dev/suyu/suyu
				synced 2025-11-04 00:49:02 -06:00 
			
		
		
		
	sm:: support service registration deferral
This commit is contained in:
		@@ -358,6 +358,14 @@ public:
 | 
			
		||||
        return manager.lock();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    bool GetIsDeferred() const {
 | 
			
		||||
        return is_deferred;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void SetIsDeferred(bool is_deferred_ = true) {
 | 
			
		||||
        is_deferred = is_deferred_;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
    friend class IPC::ResponseBuilder;
 | 
			
		||||
 | 
			
		||||
@@ -392,6 +400,7 @@ private:
 | 
			
		||||
    u32 domain_offset{};
 | 
			
		||||
 | 
			
		||||
    std::weak_ptr<SessionRequestManager> manager{};
 | 
			
		||||
    bool is_deferred{false};
 | 
			
		||||
 | 
			
		||||
    KernelCore& kernel;
 | 
			
		||||
    Core::Memory::Memory& memory;
 | 
			
		||||
 
 | 
			
		||||
@@ -25,6 +25,7 @@ constexpr size_t MaximumWaitObjects = 0x40;
 | 
			
		||||
enum HandleType {
 | 
			
		||||
    Port,
 | 
			
		||||
    Session,
 | 
			
		||||
    DeferEvent,
 | 
			
		||||
    Event,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
@@ -53,9 +54,18 @@ ServerManager::~ServerManager() {
 | 
			
		||||
        session->Close();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    for (const auto& request : m_deferrals) {
 | 
			
		||||
        request.session->Close();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Close event.
 | 
			
		||||
    m_event->GetReadableEvent().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) {
 | 
			
		||||
@@ -142,6 +152,21 @@ Result ServerManager::ManageNamedPort(const std::string& service_name,
 | 
			
		||||
    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) {
 | 
			
		||||
    for (size_t i = 0; i < num_threads; i++) {
 | 
			
		||||
        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.
 | 
			
		||||
        AddWaiter(std::addressof(m_event->GetReadableEvent()), HandleType::Event);
 | 
			
		||||
 | 
			
		||||
@@ -270,6 +300,23 @@ Result ServerManager::WaitAndProcessImpl() {
 | 
			
		||||
            // 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());
 | 
			
		||||
@@ -308,7 +355,6 @@ Result ServerManager::OnPortEvent(Kernel::KServerPort* port,
 | 
			
		||||
Result ServerManager::OnSessionEvent(Kernel::KServerSession* session,
 | 
			
		||||
                                     std::shared_ptr<Kernel::SessionRequestManager>&& manager) {
 | 
			
		||||
    Result rc{ResultSuccess};
 | 
			
		||||
    Result service_rc{ResultSuccess};
 | 
			
		||||
 | 
			
		||||
    // Try to receive a message.
 | 
			
		||||
    std::shared_ptr<Kernel::HLERequestContext> context;
 | 
			
		||||
@@ -324,16 +370,43 @@ Result ServerManager::OnSessionEvent(Kernel::KServerSession* session,
 | 
			
		||||
    }
 | 
			
		||||
    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.
 | 
			
		||||
    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.
 | 
			
		||||
    rc = session->SendReplyHLE();
 | 
			
		||||
    rc = request.session->SendReplyHLE();
 | 
			
		||||
 | 
			
		||||
    // If the session has been closed, we're done.
 | 
			
		||||
    if (rc == Kernel::ResultSessionClosed || service_rc == IPC::ERR_REMOTE_PROCESS_DEAD) {
 | 
			
		||||
        // Close the session.
 | 
			
		||||
        session->Close();
 | 
			
		||||
        request.session->Close();
 | 
			
		||||
 | 
			
		||||
        // Finish.
 | 
			
		||||
        R_SUCCEED();
 | 
			
		||||
@@ -345,7 +418,7 @@ Result ServerManager::OnSessionEvent(Kernel::KServerSession* session,
 | 
			
		||||
    // Reinsert the session.
 | 
			
		||||
    {
 | 
			
		||||
        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.
 | 
			
		||||
@@ -355,4 +428,21 @@ Result ServerManager::OnSessionEvent(Kernel::KServerSession* session,
 | 
			
		||||
    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
 | 
			
		||||
 
 | 
			
		||||
@@ -5,6 +5,7 @@
 | 
			
		||||
 | 
			
		||||
#include <atomic>
 | 
			
		||||
#include <functional>
 | 
			
		||||
#include <list>
 | 
			
		||||
#include <map>
 | 
			
		||||
#include <mutex>
 | 
			
		||||
#include <string_view>
 | 
			
		||||
@@ -19,6 +20,7 @@ class System;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
namespace Kernel {
 | 
			
		||||
class HLERequestContext;
 | 
			
		||||
class KEvent;
 | 
			
		||||
class KServerPort;
 | 
			
		||||
class KServerSession;
 | 
			
		||||
@@ -42,6 +44,7 @@ public:
 | 
			
		||||
    Result ManageNamedPort(const std::string& service_name,
 | 
			
		||||
                           std::shared_ptr<Kernel::SessionRequestHandler>&& handler,
 | 
			
		||||
                           u32 max_sessions = 64);
 | 
			
		||||
    Result ManageDeferral(Kernel::KEvent** out_event);
 | 
			
		||||
 | 
			
		||||
    Result LoopProcess();
 | 
			
		||||
    void StartAdditionalHostThreads(const char* name, size_t num_threads);
 | 
			
		||||
@@ -49,12 +52,16 @@ public:
 | 
			
		||||
    static void RunServer(std::unique_ptr<ServerManager>&& server);
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
    struct RequestState;
 | 
			
		||||
 | 
			
		||||
    Result LoopProcessImpl();
 | 
			
		||||
    Result WaitAndProcessImpl();
 | 
			
		||||
    Result OnPortEvent(Kernel::KServerPort* port,
 | 
			
		||||
                       std::shared_ptr<Kernel::SessionRequestHandler>&& handler);
 | 
			
		||||
    Result OnSessionEvent(Kernel::KServerSession* session,
 | 
			
		||||
                          std::shared_ptr<Kernel::SessionRequestManager>&& manager);
 | 
			
		||||
    Result OnDeferralEvent(std::list<RequestState>&& deferrals);
 | 
			
		||||
    Result CompleteSyncRequest(RequestState&& state);
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
    Core::System& m_system;
 | 
			
		||||
@@ -65,6 +72,15 @@ private:
 | 
			
		||||
    std::map<Kernel::KServerPort*, std::shared_ptr<Kernel::SessionRequestHandler>> m_ports{};
 | 
			
		||||
    std::map<Kernel::KServerSession*, std::shared_ptr<Kernel::SessionRequestManager>> m_sessions{};
 | 
			
		||||
    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
 | 
			
		||||
    std::atomic<bool> m_stopped{};
 | 
			
		||||
 
 | 
			
		||||
@@ -32,6 +32,10 @@ ServiceManager::~ServiceManager() {
 | 
			
		||||
        port->GetClientPort().Close();
 | 
			
		||||
        port->GetServerPort().Close();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (deferral_event) {
 | 
			
		||||
        deferral_event->Close();
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void ServiceManager::InvokeControlRequest(Kernel::HLERequestContext& context) {
 | 
			
		||||
@@ -62,6 +66,9 @@ Result ServiceManager::RegisterService(std::string name, u32 max_sessions,
 | 
			
		||||
 | 
			
		||||
    service_ports.emplace(name, port);
 | 
			
		||||
    registered_services.emplace(name, handler);
 | 
			
		||||
    if (deferral_event) {
 | 
			
		||||
        deferral_event->Signal();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return ResultSuccess;
 | 
			
		||||
}
 | 
			
		||||
@@ -88,7 +95,7 @@ ResultVal<Kernel::KPort*> ServiceManager::GetServicePort(const std::string& name
 | 
			
		||||
    std::scoped_lock lk{lock};
 | 
			
		||||
    auto it = service_ports.find(name);
 | 
			
		||||
    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;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@@ -113,6 +120,11 @@ void SM::Initialize(Kernel::HLERequestContext& ctx) {
 | 
			
		||||
 | 
			
		||||
void SM::GetService(Kernel::HLERequestContext& ctx) {
 | 
			
		||||
    auto result = GetServiceImpl(ctx);
 | 
			
		||||
    if (ctx.GetIsDeferred()) {
 | 
			
		||||
        // Don't overwrite the command buffer.
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (result.Succeeded()) {
 | 
			
		||||
        IPC::ResponseBuilder rb{ctx, 2, 0, 1, IPC::ResponseBuilder::Flags::AlwaysMoveHandles};
 | 
			
		||||
        rb.Push(result.Code());
 | 
			
		||||
@@ -125,6 +137,11 @@ void SM::GetService(Kernel::HLERequestContext& ctx) {
 | 
			
		||||
 | 
			
		||||
void SM::GetServiceTipc(Kernel::HLERequestContext& 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};
 | 
			
		||||
    rb.Push(result.Code());
 | 
			
		||||
    rb.PushMoveObjects(result.Succeeded() ? result.Unwrap() : nullptr);
 | 
			
		||||
@@ -152,8 +169,9 @@ ResultVal<Kernel::KClientSession*> SM::GetServiceImpl(Kernel::HLERequestContext&
 | 
			
		||||
    // Find the named port.
 | 
			
		||||
    auto port_result = service_manager.GetServicePort(name);
 | 
			
		||||
    if (port_result.Failed()) {
 | 
			
		||||
        LOG_ERROR(Service_SM, "called service={} -> error 0x{:08X}", name, port_result.Code().raw);
 | 
			
		||||
        return port_result.Code();
 | 
			
		||||
        LOG_INFO(Service_SM, "Waiting for service {} to become available", name);
 | 
			
		||||
        ctx.SetIsDeferred();
 | 
			
		||||
        return ERR_SERVICE_NOT_REGISTERED;
 | 
			
		||||
    }
 | 
			
		||||
    auto& port = port_result.Unwrap();
 | 
			
		||||
 | 
			
		||||
@@ -228,8 +246,13 @@ SM::SM(ServiceManager& service_manager_, Core::System& system_)
 | 
			
		||||
SM::~SM() = default;
 | 
			
		||||
 | 
			
		||||
void LoopProcess(Core::System& system) {
 | 
			
		||||
    auto& service_manager = system.ServiceManager();
 | 
			
		||||
    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));
 | 
			
		||||
    ServerManager::RunServer(std::move(server_manager));
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -71,6 +71,10 @@ public:
 | 
			
		||||
 | 
			
		||||
    void InvokeControlRequest(Kernel::HLERequestContext& context);
 | 
			
		||||
 | 
			
		||||
    void SetDeferralEvent(Kernel::KEvent* deferral_event_) {
 | 
			
		||||
        deferral_event = deferral_event_;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
    std::shared_ptr<SM> sm_interface;
 | 
			
		||||
    std::unique_ptr<Controller> controller_interface;
 | 
			
		||||
@@ -82,6 +86,7 @@ private:
 | 
			
		||||
 | 
			
		||||
    /// Kernel context
 | 
			
		||||
    Kernel::KernelCore& kernel;
 | 
			
		||||
    Kernel::KEvent* deferral_event{};
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/// Runs SM services.
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user