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:
parent
c8b3d92836
commit
27ce97fd42
13 changed files with 75 additions and 67 deletions
|
@ -30,9 +30,16 @@
|
|||
|
||||
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) {
|
||||
session->SetSessionHandler(shared_from_this());
|
||||
|
|
|
@ -46,6 +46,7 @@ class KThread;
|
|||
class KReadableEvent;
|
||||
class KSession;
|
||||
class KWritableEvent;
|
||||
class ServiceThread;
|
||||
|
||||
enum class ThreadWakeupReason;
|
||||
|
||||
|
@ -56,7 +57,7 @@ enum class ThreadWakeupReason;
|
|||
*/
|
||||
class SessionRequestHandler : public std::enable_shared_from_this<SessionRequestHandler> {
|
||||
public:
|
||||
SessionRequestHandler();
|
||||
SessionRequestHandler(KernelCore& kernel, const char* service_name_);
|
||||
virtual ~SessionRequestHandler();
|
||||
|
||||
/**
|
||||
|
@ -83,6 +84,14 @@ public:
|
|||
* @param server_session ServerSession associated with the connection.
|
||||
*/
|
||||
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>;
|
||||
|
@ -94,7 +103,8 @@ using SessionRequestHandlerPtr = std::shared_ptr<SessionRequestHandler>;
|
|||
*/
|
||||
class SessionRequestManager final {
|
||||
public:
|
||||
SessionRequestManager() = default;
|
||||
explicit SessionRequestManager(KernelCore& kernel);
|
||||
~SessionRequestManager();
|
||||
|
||||
bool IsDomain() const {
|
||||
return is_domain;
|
||||
|
@ -142,10 +152,18 @@ public:
|
|||
session_handler = std::move(handler);
|
||||
}
|
||||
|
||||
std::weak_ptr<ServiceThread> GetServiceThread() const {
|
||||
return session_handler->GetServiceThread();
|
||||
}
|
||||
|
||||
private:
|
||||
bool is_domain{};
|
||||
SessionRequestHandlerPtr session_handler;
|
||||
std::vector<SessionRequestHandlerPtr> domain_handlers;
|
||||
|
||||
private:
|
||||
KernelCore& kernel;
|
||||
std::weak_ptr<ServiceThread> service_thread;
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
|
@ -56,7 +56,8 @@ bool KClientPort::IsSignaled() const {
|
|||
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.
|
||||
KScopedResourceReservation session_reservation(kernel.CurrentProcess()->GetResourceLimit(),
|
||||
LimitableResource::Sessions);
|
||||
|
@ -101,7 +102,7 @@ ResultCode KClientPort::CreateSession(KClientSession** out) {
|
|||
}
|
||||
|
||||
// Initialize the session.
|
||||
session->Initialize(this, parent->GetName());
|
||||
session->Initialize(this, parent->GetName(), session_manager);
|
||||
|
||||
// Commit the session reservation.
|
||||
session_reservation.Commit();
|
||||
|
|
|
@ -16,6 +16,7 @@ namespace Kernel {
|
|||
class KClientSession;
|
||||
class KernelCore;
|
||||
class KPort;
|
||||
class SessionRequestManager;
|
||||
|
||||
class KClientPort final : public KSynchronizationObject {
|
||||
KERNEL_AUTOOBJECT_TRAITS(KClientPort, KSynchronizationObject);
|
||||
|
@ -52,7 +53,8 @@ public:
|
|||
void Destroy() override;
|
||||
bool IsSignaled() const override;
|
||||
|
||||
ResultCode CreateSession(KClientSession** out);
|
||||
ResultCode CreateSession(KClientSession** out,
|
||||
std::shared_ptr<SessionRequestManager> session_manager = nullptr);
|
||||
|
||||
private:
|
||||
std::atomic<s32> num_sessions{};
|
||||
|
|
|
@ -13,8 +13,10 @@
|
|||
#include "core/hle/kernel/hle_ipc.h"
|
||||
#include "core/hle/kernel/k_client_port.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_scheduler.h"
|
||||
#include "core/hle/kernel/k_server_port.h"
|
||||
#include "core/hle/kernel/k_server_session.h"
|
||||
#include "core/hle/kernel/k_session.h"
|
||||
#include "core/hle/kernel/k_thread.h"
|
||||
|
@ -23,18 +25,21 @@
|
|||
|
||||
namespace Kernel {
|
||||
|
||||
KServerSession::KServerSession(KernelCore& kernel_)
|
||||
: KSynchronizationObject{kernel_}, manager{std::make_shared<SessionRequestManager>()} {}
|
||||
KServerSession::KServerSession(KernelCore& kernel_) : KSynchronizationObject{kernel_} {}
|
||||
|
||||
KServerSession::~KServerSession() {
|
||||
kernel.ReleaseServiceThread(service_thread);
|
||||
}
|
||||
KServerSession::~KServerSession() {}
|
||||
|
||||
void KServerSession::Initialize(KSession* parent_, std::string&& name_) {
|
||||
void KServerSession::Initialize(KSession* parent_, std::string&& name_,
|
||||
std::shared_ptr<SessionRequestManager> manager_) {
|
||||
// Set member variables.
|
||||
parent = parent_;
|
||||
name = std::move(name_);
|
||||
service_thread = kernel.CreateServiceThread(name);
|
||||
|
||||
if (manager_) {
|
||||
manager = manager_;
|
||||
} else {
|
||||
manager = std::make_shared<SessionRequestManager>(kernel);
|
||||
}
|
||||
}
|
||||
|
||||
void KServerSession::Destroy() {
|
||||
|
@ -114,7 +119,7 @@ ResultCode KServerSession::QueueSyncRequest(KThread* thread, Core::Memory::Memor
|
|||
|
||||
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));
|
||||
return ResultSuccess;
|
||||
}
|
||||
|
|
|
@ -32,6 +32,7 @@ class HLERequestContext;
|
|||
class KernelCore;
|
||||
class KSession;
|
||||
class SessionRequestHandler;
|
||||
class SessionRequestManager;
|
||||
class KThread;
|
||||
|
||||
class KServerSession final : public KSynchronizationObject,
|
||||
|
@ -46,7 +47,8 @@ public:
|
|||
|
||||
void Destroy() override;
|
||||
|
||||
void Initialize(KSession* parent_, std::string&& name_);
|
||||
void Initialize(KSession* parent_, std::string&& name_,
|
||||
std::shared_ptr<SessionRequestManager> manager_);
|
||||
|
||||
KSession* GetParent() {
|
||||
return parent;
|
||||
|
@ -104,16 +106,6 @@ public:
|
|||
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:
|
||||
/// Queues a sync request from the emulated application.
|
||||
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
|
||||
bool convert_to_domain{};
|
||||
|
||||
/// Thread to dispatch service requests
|
||||
std::weak_ptr<ServiceThread> service_thread;
|
||||
|
||||
/// KSession that owns this KServerSession
|
||||
KSession* parent{};
|
||||
};
|
||||
|
|
|
@ -15,7 +15,8 @@ KSession::KSession(KernelCore& kernel_)
|
|||
: KAutoObjectWithSlabHeapAndContainer{kernel_}, server{kernel_}, client{kernel_} {}
|
||||
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.
|
||||
// Because reference count is one on creation, this will result
|
||||
// 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));
|
||||
|
||||
// Initialize our sub sessions.
|
||||
server.Initialize(this, name_ + ":Server");
|
||||
server.Initialize(this, name_ + ":Server", manager_);
|
||||
client.Initialize(this, name_ + ":Client");
|
||||
|
||||
// Set state and name.
|
||||
|
|
|
@ -13,6 +13,8 @@
|
|||
|
||||
namespace Kernel {
|
||||
|
||||
class SessionRequestManager;
|
||||
|
||||
class KSession final : public KAutoObjectWithSlabHeapAndContainer<KSession, KAutoObjectWithList> {
|
||||
KERNEL_AUTOOBJECT_TRAITS(KSession, KAutoObject);
|
||||
|
||||
|
@ -20,7 +22,8 @@ public:
|
|||
explicit KSession(KernelCore& kernel_);
|
||||
~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;
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue