3
0
Fork 0
forked from suyu/suyu

hle: kernel: Refactor to allocate a ServiceThread per service handler.

- Previously, we would allocate a thread per session, which adds new threads on CloneCurrentObject.
- This results in race conditions with N sessions queuing requests to the same service interface.
- Fixes Pokken Tournament DX crashes/softlocks, which were regressed by #6347.
This commit is contained in:
bunnei 2021-06-04 19:26:48 -07:00
parent c8b3d92836
commit 27ce97fd42
13 changed files with 75 additions and 67 deletions

View file

@ -30,9 +30,16 @@
namespace Kernel { namespace Kernel {
SessionRequestHandler::SessionRequestHandler() = default; SessionRequestHandler::SessionRequestHandler(KernelCore& kernel_, const char* service_name_)
: kernel{kernel_}, service_thread{kernel.CreateServiceThread(service_name_)} {}
SessionRequestHandler::~SessionRequestHandler() = default; SessionRequestHandler::~SessionRequestHandler() {
kernel.ReleaseServiceThread(service_thread);
}
SessionRequestManager::SessionRequestManager(KernelCore& kernel_) : kernel{kernel_} {}
SessionRequestManager::~SessionRequestManager() {}
void SessionRequestHandler::ClientConnected(KServerSession* session) { void SessionRequestHandler::ClientConnected(KServerSession* session) {
session->SetSessionHandler(shared_from_this()); session->SetSessionHandler(shared_from_this());

View file

@ -46,6 +46,7 @@ class KThread;
class KReadableEvent; class KReadableEvent;
class KSession; class KSession;
class KWritableEvent; class KWritableEvent;
class ServiceThread;
enum class ThreadWakeupReason; enum class ThreadWakeupReason;
@ -56,7 +57,7 @@ enum class ThreadWakeupReason;
*/ */
class SessionRequestHandler : public std::enable_shared_from_this<SessionRequestHandler> { class SessionRequestHandler : public std::enable_shared_from_this<SessionRequestHandler> {
public: public:
SessionRequestHandler(); SessionRequestHandler(KernelCore& kernel, const char* service_name_);
virtual ~SessionRequestHandler(); virtual ~SessionRequestHandler();
/** /**
@ -83,6 +84,14 @@ public:
* @param server_session ServerSession associated with the connection. * @param server_session ServerSession associated with the connection.
*/ */
void ClientDisconnected(KServerSession* session); void ClientDisconnected(KServerSession* session);
std::weak_ptr<ServiceThread> GetServiceThread() const {
return service_thread;
}
protected:
KernelCore& kernel;
std::weak_ptr<ServiceThread> service_thread;
}; };
using SessionRequestHandlerPtr = std::shared_ptr<SessionRequestHandler>; using SessionRequestHandlerPtr = std::shared_ptr<SessionRequestHandler>;
@ -94,7 +103,8 @@ using SessionRequestHandlerPtr = std::shared_ptr<SessionRequestHandler>;
*/ */
class SessionRequestManager final { class SessionRequestManager final {
public: public:
SessionRequestManager() = default; explicit SessionRequestManager(KernelCore& kernel);
~SessionRequestManager();
bool IsDomain() const { bool IsDomain() const {
return is_domain; return is_domain;
@ -142,10 +152,18 @@ public:
session_handler = std::move(handler); session_handler = std::move(handler);
} }
std::weak_ptr<ServiceThread> GetServiceThread() const {
return session_handler->GetServiceThread();
}
private: private:
bool is_domain{}; bool is_domain{};
SessionRequestHandlerPtr session_handler; SessionRequestHandlerPtr session_handler;
std::vector<SessionRequestHandlerPtr> domain_handlers; std::vector<SessionRequestHandlerPtr> domain_handlers;
private:
KernelCore& kernel;
std::weak_ptr<ServiceThread> service_thread;
}; };
/** /**

View file

@ -56,7 +56,8 @@ bool KClientPort::IsSignaled() const {
return num_sessions < max_sessions; return num_sessions < max_sessions;
} }
ResultCode KClientPort::CreateSession(KClientSession** out) { ResultCode KClientPort::CreateSession(KClientSession** out,
std::shared_ptr<SessionRequestManager> session_manager) {
// Reserve a new session from the resource limit. // Reserve a new session from the resource limit.
KScopedResourceReservation session_reservation(kernel.CurrentProcess()->GetResourceLimit(), KScopedResourceReservation session_reservation(kernel.CurrentProcess()->GetResourceLimit(),
LimitableResource::Sessions); LimitableResource::Sessions);
@ -101,7 +102,7 @@ ResultCode KClientPort::CreateSession(KClientSession** out) {
} }
// Initialize the session. // Initialize the session.
session->Initialize(this, parent->GetName()); session->Initialize(this, parent->GetName(), session_manager);
// Commit the session reservation. // Commit the session reservation.
session_reservation.Commit(); session_reservation.Commit();

View file

@ -16,6 +16,7 @@ namespace Kernel {
class KClientSession; class KClientSession;
class KernelCore; class KernelCore;
class KPort; class KPort;
class SessionRequestManager;
class KClientPort final : public KSynchronizationObject { class KClientPort final : public KSynchronizationObject {
KERNEL_AUTOOBJECT_TRAITS(KClientPort, KSynchronizationObject); KERNEL_AUTOOBJECT_TRAITS(KClientPort, KSynchronizationObject);
@ -52,7 +53,8 @@ public:
void Destroy() override; void Destroy() override;
bool IsSignaled() const override; bool IsSignaled() const override;
ResultCode CreateSession(KClientSession** out); ResultCode CreateSession(KClientSession** out,
std::shared_ptr<SessionRequestManager> session_manager = nullptr);
private: private:
std::atomic<s32> num_sessions{}; std::atomic<s32> num_sessions{};

View file

@ -13,8 +13,10 @@
#include "core/hle/kernel/hle_ipc.h" #include "core/hle/kernel/hle_ipc.h"
#include "core/hle/kernel/k_client_port.h" #include "core/hle/kernel/k_client_port.h"
#include "core/hle/kernel/k_handle_table.h" #include "core/hle/kernel/k_handle_table.h"
#include "core/hle/kernel/k_port.h"
#include "core/hle/kernel/k_process.h" #include "core/hle/kernel/k_process.h"
#include "core/hle/kernel/k_scheduler.h" #include "core/hle/kernel/k_scheduler.h"
#include "core/hle/kernel/k_server_port.h"
#include "core/hle/kernel/k_server_session.h" #include "core/hle/kernel/k_server_session.h"
#include "core/hle/kernel/k_session.h" #include "core/hle/kernel/k_session.h"
#include "core/hle/kernel/k_thread.h" #include "core/hle/kernel/k_thread.h"
@ -23,18 +25,21 @@
namespace Kernel { namespace Kernel {
KServerSession::KServerSession(KernelCore& kernel_) KServerSession::KServerSession(KernelCore& kernel_) : KSynchronizationObject{kernel_} {}
: KSynchronizationObject{kernel_}, manager{std::make_shared<SessionRequestManager>()} {}
KServerSession::~KServerSession() { KServerSession::~KServerSession() {}
kernel.ReleaseServiceThread(service_thread);
}
void KServerSession::Initialize(KSession* parent_, std::string&& name_) { void KServerSession::Initialize(KSession* parent_, std::string&& name_,
std::shared_ptr<SessionRequestManager> manager_) {
// Set member variables. // Set member variables.
parent = parent_; parent = parent_;
name = std::move(name_); name = std::move(name_);
service_thread = kernel.CreateServiceThread(name);
if (manager_) {
manager = manager_;
} else {
manager = std::make_shared<SessionRequestManager>(kernel);
}
} }
void KServerSession::Destroy() { void KServerSession::Destroy() {
@ -114,7 +119,7 @@ ResultCode KServerSession::QueueSyncRequest(KThread* thread, Core::Memory::Memor
context->PopulateFromIncomingCommandBuffer(kernel.CurrentProcess()->GetHandleTable(), cmd_buf); context->PopulateFromIncomingCommandBuffer(kernel.CurrentProcess()->GetHandleTable(), cmd_buf);
if (auto strong_ptr = service_thread.lock()) { if (auto strong_ptr = manager->GetServiceThread().lock()) {
strong_ptr->QueueSyncRequest(*parent, std::move(context)); strong_ptr->QueueSyncRequest(*parent, std::move(context));
return ResultSuccess; return ResultSuccess;
} }

View file

@ -32,6 +32,7 @@ class HLERequestContext;
class KernelCore; class KernelCore;
class KSession; class KSession;
class SessionRequestHandler; class SessionRequestHandler;
class SessionRequestManager;
class KThread; class KThread;
class KServerSession final : public KSynchronizationObject, class KServerSession final : public KSynchronizationObject,
@ -46,7 +47,8 @@ public:
void Destroy() override; void Destroy() override;
void Initialize(KSession* parent_, std::string&& name_); void Initialize(KSession* parent_, std::string&& name_,
std::shared_ptr<SessionRequestManager> manager_);
KSession* GetParent() { KSession* GetParent() {
return parent; return parent;
@ -104,16 +106,6 @@ public:
return manager; return manager;
} }
/// Gets the session request manager, which forwards requests to the underlying service
const std::shared_ptr<SessionRequestManager>& GetSessionRequestManager() const {
return manager;
}
/// Sets the session request manager, which forwards requests to the underlying service
void SetSessionRequestManager(std::shared_ptr<SessionRequestManager> manager_) {
manager = std::move(manager_);
}
private: private:
/// Queues a sync request from the emulated application. /// Queues a sync request from the emulated application.
ResultCode QueueSyncRequest(KThread* thread, Core::Memory::Memory& memory); ResultCode QueueSyncRequest(KThread* thread, Core::Memory::Memory& memory);
@ -131,9 +123,6 @@ private:
/// When set to True, converts the session to a domain at the end of the command /// When set to True, converts the session to a domain at the end of the command
bool convert_to_domain{}; bool convert_to_domain{};
/// Thread to dispatch service requests
std::weak_ptr<ServiceThread> service_thread;
/// KSession that owns this KServerSession /// KSession that owns this KServerSession
KSession* parent{}; KSession* parent{};
}; };

View file

@ -15,7 +15,8 @@ KSession::KSession(KernelCore& kernel_)
: KAutoObjectWithSlabHeapAndContainer{kernel_}, server{kernel_}, client{kernel_} {} : KAutoObjectWithSlabHeapAndContainer{kernel_}, server{kernel_}, client{kernel_} {}
KSession::~KSession() = default; KSession::~KSession() = default;
void KSession::Initialize(KClientPort* port_, const std::string& name_) { void KSession::Initialize(KClientPort* port_, const std::string& name_,
std::shared_ptr<SessionRequestManager> manager_) {
// Increment reference count. // Increment reference count.
// Because reference count is one on creation, this will result // Because reference count is one on creation, this will result
// in a reference count of two. Thus, when both server and client are closed // in a reference count of two. Thus, when both server and client are closed
@ -27,7 +28,7 @@ void KSession::Initialize(KClientPort* port_, const std::string& name_) {
KAutoObject::Create(std::addressof(client)); KAutoObject::Create(std::addressof(client));
// Initialize our sub sessions. // Initialize our sub sessions.
server.Initialize(this, name_ + ":Server"); server.Initialize(this, name_ + ":Server", manager_);
client.Initialize(this, name_ + ":Client"); client.Initialize(this, name_ + ":Client");
// Set state and name. // Set state and name.

View file

@ -13,6 +13,8 @@
namespace Kernel { namespace Kernel {
class SessionRequestManager;
class KSession final : public KAutoObjectWithSlabHeapAndContainer<KSession, KAutoObjectWithList> { class KSession final : public KAutoObjectWithSlabHeapAndContainer<KSession, KAutoObjectWithList> {
KERNEL_AUTOOBJECT_TRAITS(KSession, KAutoObject); KERNEL_AUTOOBJECT_TRAITS(KSession, KAutoObject);
@ -20,7 +22,8 @@ public:
explicit KSession(KernelCore& kernel_); explicit KSession(KernelCore& kernel_);
~KSession() override; ~KSession() override;
void Initialize(KClientPort* port_, const std::string& name_); void Initialize(KClientPort* port_, const std::string& name_,
std::shared_ptr<SessionRequestManager> manager_ = nullptr);
void Finalize() override; void Finalize() override;

View file

@ -254,8 +254,6 @@ void PL_U::GetSharedMemoryNativeHandle(Kernel::HLERequestContext& ctx) {
LOG_DEBUG(Service_NS, "called"); LOG_DEBUG(Service_NS, "called");
// Create shared font memory object // Create shared font memory object
auto& kernel = system.Kernel();
std::memcpy(kernel.GetFontSharedMem().GetPointer(), impl->shared_font->data(), std::memcpy(kernel.GetFontSharedMem().GetPointer(), impl->shared_font->data(),
impl->shared_font->size()); impl->shared_font->size());

View file

@ -93,8 +93,8 @@ namespace Service {
ServiceFrameworkBase::ServiceFrameworkBase(Core::System& system_, const char* service_name_, ServiceFrameworkBase::ServiceFrameworkBase(Core::System& system_, const char* service_name_,
u32 max_sessions_, InvokerFn* handler_invoker_) u32 max_sessions_, InvokerFn* handler_invoker_)
: system{system_}, service_name{service_name_}, max_sessions{max_sessions_}, : SessionRequestHandler(system_.Kernel(), service_name_), system{system_},
handler_invoker{handler_invoker_} {} service_name{service_name_}, max_sessions{max_sessions_}, handler_invoker{handler_invoker_} {}
ServiceFrameworkBase::~ServiceFrameworkBase() { ServiceFrameworkBase::~ServiceFrameworkBase() {
// Wait for other threads to release access before destroying // Wait for other threads to release access before destroying
@ -111,7 +111,7 @@ void ServiceFrameworkBase::InstallAsService(SM::ServiceManager& service_manager)
port_installed = true; port_installed = true;
} }
Kernel::KClientPort& ServiceFrameworkBase::CreatePort(Kernel::KernelCore& kernel) { Kernel::KClientPort& ServiceFrameworkBase::CreatePort() {
const auto guard = LockService(); const auto guard = LockService();
ASSERT(!port_installed); ASSERT(!port_installed);

View file

@ -23,6 +23,7 @@ namespace Kernel {
class HLERequestContext; class HLERequestContext;
class KClientPort; class KClientPort;
class KServerSession; class KServerSession;
class ServiceThread;
} // namespace Kernel } // namespace Kernel
namespace Service { namespace Service {
@ -41,7 +42,7 @@ class ServiceManager;
static const int kMaxPortSize = 8; ///< Maximum size of a port name (8 characters) static const int kMaxPortSize = 8; ///< Maximum size of a port name (8 characters)
/// Arbitrary default number of maximum connections to an HLE service. /// Arbitrary default number of maximum connections to an HLE service.
static const u32 DefaultMaxSessions = 10; static const u32 DefaultMaxSessions = 64;
/** /**
* This is an non-templated base of ServiceFramework to reduce code bloat and compilation times, it * This is an non-templated base of ServiceFramework to reduce code bloat and compilation times, it
@ -74,7 +75,7 @@ public:
void InvokeRequestTipc(Kernel::HLERequestContext& ctx); void InvokeRequestTipc(Kernel::HLERequestContext& ctx);
/// Creates a port pair and registers it on the kernel's global port registry. /// Creates a port pair and registers it on the kernel's global port registry.
Kernel::KClientPort& CreatePort(Kernel::KernelCore& kernel); Kernel::KClientPort& CreatePort();
/// Handles a synchronization request for the service. /// Handles a synchronization request for the service.
ResultCode HandleSyncRequest(Kernel::KServerSession& session, ResultCode HandleSyncRequest(Kernel::KServerSession& session,

View file

@ -28,42 +28,25 @@ void Controller::ConvertCurrentObjectToDomain(Kernel::HLERequestContext& ctx) {
} }
void Controller::CloneCurrentObject(Kernel::HLERequestContext& ctx) { void Controller::CloneCurrentObject(Kernel::HLERequestContext& ctx) {
// TODO(bunnei): This is just creating a new handle to the same Session. I assume this is wrong
// and that we probably want to actually make an entirely new Session, but we still need to
// verify this on hardware.
LOG_DEBUG(Service, "called"); LOG_DEBUG(Service, "called");
auto& kernel = system.Kernel(); auto& parent_session = *ctx.Session()->GetParent();
auto* session = ctx.Session()->GetParent(); auto& parent_port = parent_session.GetParent()->GetParent()->GetClientPort();
auto* port = session->GetParent()->GetParent(); auto& session_manager = parent_session.GetServerSession().GetSessionRequestManager();
// Reserve a new session from the process resource limit. // Create a session.
Kernel::KScopedResourceReservation session_reservation( Kernel::KClientSession* session{};
kernel.CurrentProcess()->GetResourceLimit(), Kernel::LimitableResource::Sessions); const ResultCode result = parent_port.CreateSession(std::addressof(session), session_manager);
if (!session_reservation.Succeeded()) { if (result.IsError()) {
LOG_CRITICAL(Service, "CreateSession failed with error 0x{:08X}", result.raw);
IPC::ResponseBuilder rb{ctx, 2}; IPC::ResponseBuilder rb{ctx, 2};
rb.Push(Kernel::ResultLimitReached); rb.Push(result);
} }
// Create a new session.
auto* clone = Kernel::KSession::Create(kernel);
clone->Initialize(&port->GetClientPort(), session->GetName());
// Commit the session reservation.
session_reservation.Commit();
// Enqueue the session with the named port.
port->EnqueueSession(&clone->GetServerSession());
// Set the session request manager.
clone->GetServerSession().SetSessionRequestManager(
session->GetServerSession().GetSessionRequestManager());
// We succeeded. // We 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(ResultSuccess); rb.Push(ResultSuccess);
rb.PushMoveObjects(clone->GetClientSession()); rb.PushMoveObjects(session);
} }
void Controller::CloneCurrentObjectEx(Kernel::HLERequestContext& ctx) { void Controller::CloneCurrentObjectEx(Kernel::HLERequestContext& ctx) {

View file

@ -46,7 +46,7 @@ Kernel::KClientPort& ServiceManager::InterfaceFactory(ServiceManager& self, Core
self.sm_interface = sm; self.sm_interface = sm;
self.controller_interface = std::make_unique<Controller>(system); self.controller_interface = std::make_unique<Controller>(system);
return sm->CreatePort(system.Kernel()); return sm->CreatePort();
} }
ResultVal<Kernel::KServerPort*> ServiceManager::RegisterService(std::string name, ResultVal<Kernel::KServerPort*> ServiceManager::RegisterService(std::string name,