Explorar o código

hle: kernel: Migrate KPort, KClientPort, and KServerPort to KAutoObject.

bunnei %!s(int64=5) %!d(string=hai) anos
pai
achega
626f746971

+ 2 - 0
src/core/CMakeLists.txt

@@ -194,6 +194,8 @@ add_library(core STATIC
     hle/kernel/k_page_linked_list.h
     hle/kernel/k_page_table.cpp
     hle/kernel/k_page_table.h
+    hle/kernel/k_port.cpp
+    hle/kernel/k_port.h
     hle/kernel/k_priority_queue.h
     hle/kernel/k_readable_event.cpp
     hle/kernel/k_readable_event.h

+ 2 - 2
src/core/hle/ipc_helpers.h

@@ -136,10 +136,10 @@ public:
             context->AddDomainObject(std::move(iface));
         } else {
             auto* session = Kernel::KSession::Create(kernel);
-            session->Initialize(iface->GetServiceName());
+            session->Initialize(nullptr, iface->GetServiceName());
 
             context->AddMoveObject(&session->GetClientSession());
-            iface->ClientConnected(session);
+            iface->ClientConnected(&session->GetServerSession());
         }
     }
 

+ 4 - 6
src/core/hle/kernel/hle_ipc.cpp

@@ -35,14 +35,12 @@ SessionRequestHandler::SessionRequestHandler() = default;
 
 SessionRequestHandler::~SessionRequestHandler() = default;
 
-void SessionRequestHandler::ClientConnected(KSession* session) {
-    session->GetServerSession().SetHleHandler(shared_from_this());
-    sessions.push_back(session);
+void SessionRequestHandler::ClientConnected(KServerSession* session) {
+    session->SetHleHandler(shared_from_this());
 }
 
-void SessionRequestHandler::ClientDisconnected(KSession* session) {
-    session->GetServerSession().SetHleHandler(nullptr);
-    boost::range::remove_erase(sessions, session);
+void SessionRequestHandler::ClientDisconnected(KServerSession* session) {
+    session->SetHleHandler(nullptr);
 }
 
 HLERequestContext::HLERequestContext(KernelCore& kernel_, Core::Memory::Memory& memory_,

+ 2 - 8
src/core/hle/kernel/hle_ipc.h

@@ -72,20 +72,14 @@ public:
      * associated ServerSession alive for the duration of the connection.
      * @param server_session Owning pointer to the ServerSession associated with the connection.
      */
-    void ClientConnected(KSession* session);
+    void ClientConnected(KServerSession* session);
 
     /**
      * Signals that a client has just disconnected from this HLE handler and releases the
      * associated ServerSession.
      * @param server_session ServerSession associated with the connection.
      */
-    void ClientDisconnected(KSession* session);
-
-protected:
-    /// List of sessions that are connected to this handler.
-    /// A ServerSession whose server endpoint is an HLE implementation is kept alive by this list
-    /// for the duration of the connection.
-    std::vector<KSession*> sessions;
+    void ClientDisconnected(KServerSession* session);
 };
 
 /**

+ 3 - 1
src/core/hle/kernel/init/init_slab_setup.cpp

@@ -12,6 +12,7 @@
 #include "core/hle/kernel/k_event.h"
 #include "core/hle/kernel/k_memory_layout.h"
 #include "core/hle/kernel/k_memory_manager.h"
+#include "core/hle/kernel/k_port.h"
 #include "core/hle/kernel/k_resource_limit.h"
 #include "core/hle/kernel/k_session.h"
 #include "core/hle/kernel/k_shared_memory.h"
@@ -30,8 +31,9 @@ namespace Kernel::Init {
     HANDLER(Process, (SLAB_COUNT(Process)), ##__VA_ARGS__)                                         \
     HANDLER(KThread, (SLAB_COUNT(KThread)), ##__VA_ARGS__)                                         \
     HANDLER(KEvent, (SLAB_COUNT(KEvent)), ##__VA_ARGS__)                                           \
-    HANDLER(KTransferMemory, (SLAB_COUNT(KTransferMemory)), ##__VA_ARGS__)                         \
+    HANDLER(KPort, (SLAB_COUNT(KPort)), ##__VA_ARGS__)                                             \
     HANDLER(KSharedMemory, (SLAB_COUNT(KSharedMemory)), ##__VA_ARGS__)                             \
+    HANDLER(KTransferMemory, (SLAB_COUNT(KTransferMemory)), ##__VA_ARGS__)                         \
     HANDLER(KSession, (SLAB_COUNT(KSession)), ##__VA_ARGS__)                                       \
     HANDLER(KResourceLimit, (SLAB_COUNT(KResourceLimit)), ##__VA_ARGS__)
 

+ 93 - 26
src/core/hle/kernel/k_client_port.cpp

@@ -1,12 +1,14 @@
-// Copyright 2016 Citra Emulator Project
+// Copyright 2021 Citra Emulator Project
 // Licensed under GPLv2 or any later version
 // Refer to the license.txt file included.
 
+#include "common/scope_exit.h"
 #include "core/hle/kernel/hle_ipc.h"
 #include "core/hle/kernel/k_client_port.h"
-#include "core/hle/kernel/k_server_port.h"
+#include "core/hle/kernel/k_port.h"
+#include "core/hle/kernel/k_scheduler.h"
+#include "core/hle/kernel/k_scoped_resource_reservation.h"
 #include "core/hle/kernel/k_session.h"
-#include "core/hle/kernel/object.h"
 #include "core/hle/kernel/svc_results.h"
 
 namespace Kernel {
@@ -14,45 +16,110 @@ namespace Kernel {
 KClientPort::KClientPort(KernelCore& kernel) : KSynchronizationObject{kernel} {}
 KClientPort::~KClientPort() = default;
 
-void KClientPort::Initialize(s32 max_sessions_, std::string&& name_) {
+void KClientPort::Initialize(KPort* parent_, s32 max_sessions_, std::string&& name_) {
+    // Set member variables.
+    num_sessions = 0;
+    peak_sessions = 0;
+    parent = parent_;
     max_sessions = max_sessions_;
     name = std::move(name_);
 }
 
-KServerPort* KClientPort::GetServerPort() const {
-    return server_port;
-}
+void KClientPort::OnSessionFinalized() {
+    KScopedSchedulerLock sl{kernel};
 
-ResultVal<KClientSession*> KClientPort::Connect() {
-    if (num_sessions >= max_sessions) {
-        return ResultOutOfSessions;
+    const auto prev = num_sessions--;
+    if (prev == max_sessions) {
+        this->NotifyAvailable();
     }
-    num_sessions++;
+}
 
-    auto* session = Kernel::KSession::Create(kernel);
-    session->Initialize(name + ":ClientPort");
+void KClientPort::OnServerClosed() {}
 
-    if (server_port->HasHLEHandler()) {
-        server_port->GetHLEHandler()->ClientConnected(session);
-    } else {
-        server_port->AppendPendingSession(std::addressof(session->GetServerSession()));
-    }
+bool KClientPort::IsLight() const {
+    return this->GetParent()->IsLight();
+}
 
-    return MakeResult(std::addressof(session->GetClientSession()));
+bool KClientPort::IsServerClosed() const {
+    return this->GetParent()->IsServerClosed();
 }
 
-void KClientPort::ConnectionClosed() {
-    if (num_sessions == 0) {
-        return;
-    }
+void KClientPort::Destroy() {
+    // Note with our parent that we're closed.
+    parent->OnClientClosed();
 
-    --num_sessions;
+    // Close our reference to our parent.
+    parent->Close();
 }
 
-void KClientPort::Destroy() {}
-
 bool KClientPort::IsSignaled() const {
     return num_sessions < max_sessions;
 }
 
+ResultCode KClientPort::CreateSession(KClientSession** out) {
+    // Reserve a new session from the resource limit.
+    KScopedResourceReservation session_reservation(kernel.CurrentProcess()->GetResourceLimit(),
+                                                   LimitableResource::Sessions);
+    R_UNLESS(session_reservation.Succeeded(), ResultLimitReached);
+
+    // Update the session counts.
+    {
+        // Atomically increment the number of sessions.
+        s32 new_sessions;
+        {
+            const auto max = max_sessions;
+            auto cur_sessions = num_sessions.load(std::memory_order_acquire);
+            do {
+                R_UNLESS(cur_sessions < max, ResultOutOfSessions);
+                new_sessions = cur_sessions + 1;
+            } while (!num_sessions.compare_exchange_weak(cur_sessions, new_sessions,
+                                                         std::memory_order_relaxed));
+        }
+
+        // Atomically update the peak session tracking.
+        {
+            auto peak = peak_sessions.load(std::memory_order_acquire);
+            do {
+                if (peak >= new_sessions) {
+                    break;
+                }
+            } while (!peak_sessions.compare_exchange_weak(peak, new_sessions,
+                                                          std::memory_order_relaxed));
+        }
+    }
+
+    // Create a new session.
+    KSession* session = KSession::Create(kernel);
+    if (session == nullptr) {
+        /* Decrement the session count. */
+        const auto prev = num_sessions--;
+        if (prev == max_sessions) {
+            this->NotifyAvailable();
+        }
+
+        return ResultOutOfResource;
+    }
+
+    // Initialize the session.
+    session->Initialize(this, parent->GetName());
+
+    // Commit the session reservation.
+    session_reservation.Commit();
+
+    // Register the session.
+    KSession::Register(kernel, session);
+    auto session_guard = SCOPE_GUARD({
+        session->GetClientSession().Close();
+        session->GetServerSession().Close();
+    });
+
+    // Enqueue the session with our parent.
+    R_TRY(parent->EnqueueSession(std::addressof(session->GetServerSession())));
+
+    // We succeeded, so set the output.
+    session_guard.Cancel();
+    *out = std::addressof(session->GetClientSession());
+    return RESULT_SUCCESS;
+}
+
 } // namespace Kernel

+ 25 - 21
src/core/hle/kernel/k_client_port.h

@@ -15,7 +15,7 @@ namespace Kernel {
 
 class KClientSession;
 class KernelCore;
-class KServerPort;
+class KPort;
 
 class KClientPort final : public KSynchronizationObject {
     KERNEL_AUTOOBJECT_TRAITS(KClientPort, KSynchronizationObject);
@@ -24,30 +24,33 @@ public:
     explicit KClientPort(KernelCore& kernel);
     virtual ~KClientPort() override;
 
-    friend class KServerPort;
+    void Initialize(KPort* parent_, s32 max_sessions_, std::string&& name_);
+    void OnSessionFinalized();
+    void OnServerClosed();
 
-    void Initialize(s32 max_sessions_, std::string&& name_);
-
-    KServerPort* GetServerPort() const;
+    constexpr const KPort* GetParent() const {
+        return parent;
+    }
 
-    /**
-     * Creates a new Session pair, adds the created ServerSession to the associated ServerPort's
-     * list of pending sessions, and signals the ServerPort, causing any threads
-     * waiting on it to awake.
-     * @returns ClientSession The client endpoint of the created Session pair, or error code.
-     */
-    ResultVal<KClientSession*> Connect();
+    s32 GetNumSessions() const {
+        return num_sessions;
+    }
+    s32 GetPeakSessions() const {
+        return peak_sessions;
+    }
+    s32 GetMaxSessions() const {
+        return max_sessions;
+    }
 
-    /**
-     * Signifies that a previously active connection has been closed,
-     * decreasing the total number of active connections to this port.
-     */
-    void ConnectionClosed();
+    bool IsLight() const;
+    bool IsServerClosed() const;
 
     // Overridden virtual functions.
     virtual void Destroy() override;
     virtual bool IsSignaled() const override;
 
+    ResultCode CreateSession(KClientSession** out);
+
     // DEPRECATED
 
     std::string GetTypeName() const override {
@@ -63,10 +66,11 @@ public:
     }
 
 private:
-    KServerPort* server_port{};      ///< ServerPort associated with this client port.
-    s32 max_sessions{};              ///< Maximum number of simultaneous sessions the port can have
-    std::atomic<s32> num_sessions{}; ///< Number of currently open sessions to this port
-    std::string name;                ///< Name of client port (optional)
+    std::atomic<s32> num_sessions{};
+    std::atomic<s32> peak_sessions{};
+    s32 max_sessions{};
+    KPort* parent{};
+    std::string name;
 };
 
 } // namespace Kernel

+ 1 - 1
src/core/hle/kernel/k_event.h

@@ -20,7 +20,7 @@ class KEvent final : public KAutoObjectWithSlabHeapAndContainer<KEvent, KAutoObj
 
 public:
     explicit KEvent(KernelCore& kernel);
-    ~KEvent() override;
+    virtual ~KEvent();
 
     void Initialize(std::string&& name);
 

+ 68 - 0
src/core/hle/kernel/k_port.cpp

@@ -0,0 +1,68 @@
+// Copyright 2021 yuzu Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#include "core/hle/kernel/hle_ipc.h"
+#include "core/hle/kernel/k_port.h"
+#include "core/hle/kernel/k_scheduler.h"
+#include "core/hle/kernel/svc_results.h"
+
+namespace Kernel {
+
+KPort::KPort(KernelCore& kernel)
+    : KAutoObjectWithSlabHeapAndContainer{kernel}, server{kernel}, client{kernel} {}
+
+KPort::~KPort() = default;
+
+void KPort::Initialize(s32 max_sessions_, bool is_light_, const std::string& name_) {
+    // Open a new reference count to the initialized port.
+    Open();
+
+    // Create and initialize our server/client pair.
+    KAutoObject::Create(std::addressof(server));
+    KAutoObject::Create(std::addressof(client));
+    server.Initialize(this, name_ + ":Server");
+    client.Initialize(this, max_sessions_, name_ + ":Client");
+
+    // Set our member variables.
+    is_light = is_light_;
+    name = name_;
+    state = State::Normal;
+}
+
+void KPort::OnClientClosed() {
+    KScopedSchedulerLock sl{kernel};
+
+    if (state == State::Normal) {
+        state = State::ClientClosed;
+    }
+}
+
+void KPort::OnServerClosed() {
+    KScopedSchedulerLock sl{kernel};
+
+    if (state == State::Normal) {
+        state = State::ServerClosed;
+    }
+}
+
+bool KPort::IsServerClosed() const {
+    KScopedSchedulerLock sl{kernel};
+    return state == State::ServerClosed;
+}
+
+ResultCode KPort::EnqueueSession(KServerSession* session) {
+    KScopedSchedulerLock sl{kernel};
+
+    R_UNLESS(state == State::Normal, ResultPortClosed);
+
+    if (server.HasHLEHandler()) {
+        server.GetHLEHandler()->ClientConnected(session);
+    } else {
+        server.EnqueueSession(session);
+    }
+
+    return RESULT_SUCCESS;
+}
+
+} // namespace Kernel

+ 87 - 0
src/core/hle/kernel/k_port.h

@@ -0,0 +1,87 @@
+// Copyright 2021 yuzu Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include <memory>
+#include <string>
+
+#include "common/common_types.h"
+#include "core/hle/kernel/k_client_port.h"
+#include "core/hle/kernel/k_server_port.h"
+#include "core/hle/kernel/slab_helpers.h"
+#include "core/hle/result.h"
+
+namespace Kernel {
+
+class KServerSession;
+
+class KPort final : public KAutoObjectWithSlabHeapAndContainer<KPort, KAutoObjectWithList> {
+    KERNEL_AUTOOBJECT_TRAITS(KPort, KAutoObject);
+
+public:
+    explicit KPort(KernelCore& kernel);
+    virtual ~KPort();
+
+    static void PostDestroy([[maybe_unused]] uintptr_t arg) {}
+
+    void Initialize(s32 max_sessions_, bool is_light_, const std::string& name_);
+    void OnClientClosed();
+    void OnServerClosed();
+
+    bool IsLight() const {
+        return is_light;
+    }
+
+    bool IsServerClosed() const;
+
+    ResultCode EnqueueSession(KServerSession* session);
+
+    KClientPort& GetClientPort() {
+        return client;
+    }
+    KServerPort& GetServerPort() {
+        return server;
+    }
+    const KClientPort& GetClientPort() const {
+        return client;
+    }
+    const KServerPort& GetServerPort() const {
+        return server;
+    }
+
+    // DEPRECATED
+
+    friend class ServerPort;
+    std::string GetTypeName() const override {
+        return "Port";
+    }
+    std::string GetName() const override {
+        return name;
+    }
+
+    HandleType GetHandleType() const override {
+        return {};
+    }
+
+    void Finalize() override {}
+
+private:
+    enum class State : u8 {
+        Invalid = 0,
+        Normal = 1,
+        ClientClosed = 2,
+        ServerClosed = 3,
+    };
+
+private:
+    KServerPort server;
+    KClientPort client;
+    State state{State::Invalid};
+    bool is_light{};
+
+    std::string name; ///< Name of client port (optional)
+};
+
+} // namespace Kernel

+ 67 - 27
src/core/hle/kernel/k_server_port.cpp

@@ -1,10 +1,12 @@
-// Copyright 2016 Citra Emulator Project
+// Copyright 2021 yuzu emulator team
 // Licensed under GPLv2 or any later version
 // Refer to the license.txt file included.
 
 #include <tuple>
 #include "common/assert.h"
 #include "core/hle/kernel/k_client_port.h"
+#include "core/hle/kernel/k_port.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_thread.h"
@@ -16,50 +18,88 @@ namespace Kernel {
 KServerPort::KServerPort(KernelCore& kernel) : KSynchronizationObject{kernel} {}
 KServerPort::~KServerPort() = default;
 
-void KServerPort::Initialize(std::string&& name_) {
+void KServerPort::Initialize(KPort* parent_, std::string&& name_) {
     // Set member variables.
+    parent = parent_;
     name = std::move(name_);
 }
 
-ResultVal<KServerSession*> KServerPort::Accept() {
-    if (pending_sessions.empty()) {
-        return ResultNotFound;
-    }
-
-    auto* session = pending_sessions.back();
-    pending_sessions.pop_back();
-    return MakeResult(session);
+bool KServerPort::IsLight() const {
+    return this->GetParent()->IsLight();
 }
 
-void KServerPort::AppendPendingSession(KServerSession* pending_session) {
-    pending_sessions.push_back(std::move(pending_session));
-    if (pending_sessions.size() == 1) {
-        NotifyAvailable();
+void KServerPort::CleanupSessions() {
+    // Ensure our preconditions are met.
+    if (this->IsLight()) {
+        UNIMPLEMENTED();
+    }
+
+    // Cleanup the session list.
+    while (true) {
+        // Get the last session in the list
+        KServerSession* session = nullptr;
+        {
+            KScopedSchedulerLock sl{kernel};
+            if (!session_list.empty()) {
+                session = std::addressof(session_list.front());
+                session_list.pop_front();
+            }
+        }
+
+        // Close the session.
+        if (session != nullptr) {
+            session->Close();
+        } else {
+            break;
+        }
     }
 }
 
-void KServerPort::Destroy() {}
+void KServerPort::Destroy() {
+    // Note with our parent that we're closed.
+    parent->OnServerClosed();
+
+    // Perform necessary cleanup of our session lists.
+    this->CleanupSessions();
+
+    // Close our reference to our parent.
+    parent->Close();
+}
 
 bool KServerPort::IsSignaled() const {
-    return !pending_sessions.empty();
+    if (this->IsLight()) {
+        UNIMPLEMENTED();
+        return false;
+    } else {
+        return !session_list.empty();
+    }
 }
 
-KServerPort::PortPair KServerPort::CreatePortPair(KernelCore& kernel, u32 max_sessions,
-                                                  std::string name) {
-    KServerPort* server_port = new KServerPort(kernel);
-    KClientPort* client_port = new KClientPort(kernel);
+void KServerPort::EnqueueSession(KServerSession* session) {
+    ASSERT(!this->IsLight());
+
+    KScopedSchedulerLock sl{kernel};
 
-    KAutoObject::Create(server_port);
-    KAutoObject::Create(client_port);
+    // Add the session to our queue.
+    session_list.push_back(*session);
+    if (session_list.size() == 1) {
+        this->NotifyAvailable();
+    }
+}
 
-    server_port->Initialize(name + "_Server");
-    client_port->Initialize(max_sessions, name + "_Client");
+KServerSession* KServerPort::AcceptSession() {
+    ASSERT(!this->IsLight());
 
-    client_port->server_port = server_port;
+    KScopedSchedulerLock sl{kernel};
 
-    server_port->name = name + "_Server";
+    // Return the first session in the list.
+    if (session_list.empty()) {
+        return nullptr;
+    }
 
-    return std::make_pair(server_port, client_port);
+    KServerSession* session = std::addressof(session_list.front());
+    session_list.pop_front();
+    return session;
 }
 
 } // namespace Kernel

+ 23 - 32
src/core/hle/kernel/k_server_port.h

@@ -1,4 +1,4 @@
-// Copyright 2016 Citra Emulator Project
+// Copyright 2021 yuzu emulator team
 // Licensed under GPLv2 or any later version
 // Refer to the license.txt file included.
 
@@ -8,46 +8,33 @@
 #include <string>
 #include <utility>
 #include <vector>
+
+#include <boost/intrusive/list.hpp>
+
 #include "common/common_types.h"
+#include "core/hle/kernel/k_server_session.h"
 #include "core/hle/kernel/k_synchronization_object.h"
-#include "core/hle/kernel/object.h"
 #include "core/hle/result.h"
 
 namespace Kernel {
 
-class KClientPort;
 class KernelCore;
-class KServerSession;
+class KPort;
 class SessionRequestHandler;
 
 class KServerPort final : public KSynchronizationObject {
     KERNEL_AUTOOBJECT_TRAITS(KServerPort, KSynchronizationObject);
 
+private:
+    using SessionList = boost::intrusive::list<KServerSession>;
+
 public:
     explicit KServerPort(KernelCore& kernel);
     virtual ~KServerPort() override;
 
     using HLEHandler = std::shared_ptr<SessionRequestHandler>;
-    using PortPair = std::pair<KServerPort*, KClientPort*>;
-
-    void Initialize(std::string&& name_);
-
-    /**
-     * Creates a pair of ServerPort and an associated ClientPort.
-     *
-     * @param kernel The kernel instance to create the port pair under.
-     * @param max_sessions Maximum number of sessions to the port
-     * @param name Optional name of the ports
-     * @return The created port tuple
-     */
-    static PortPair CreatePortPair(KernelCore& kernel, u32 max_sessions,
-                                   std::string name = "UnknownPort");
 
-    /**
-     * Accepts a pending incoming connection on this port. If there are no pending sessions, will
-     * return ERR_NO_PENDING_SESSIONS.
-     */
-    ResultVal<KServerSession*> Accept();
+    void Initialize(KPort* parent_, std::string&& name_);
 
     /// Whether or not this server port has an HLE handler available.
     bool HasHLEHandler() const {
@@ -67,9 +54,15 @@ public:
         hle_handler = std::move(hle_handler_);
     }
 
-    /// Appends a ServerSession to the collection of ServerSessions
-    /// waiting to be accepted by this port.
-    void AppendPendingSession(KServerSession* pending_session);
+    void EnqueueSession(KServerSession* pending_session);
+
+    KServerSession* AcceptSession();
+
+    constexpr const KPort* GetParent() const {
+        return parent;
+    }
+
+    bool IsLight() const;
 
     // Overridden virtual functions.
     virtual void Destroy() override;
@@ -90,14 +83,12 @@ public:
     }
 
 private:
-    /// ServerSessions waiting to be accepted by the port
-    std::vector<KServerSession*> pending_sessions;
+    void CleanupSessions();
 
-    /// This session's HLE request handler template (optional)
-    /// ServerSessions created from this port inherit a reference to this handler.
+private:
+    SessionList session_list;
     HLEHandler hle_handler;
-
-    /// Name of the port (optional)
+    KPort* parent{};
     std::string name;
 };
 

+ 1 - 1
src/core/hle/kernel/k_server_session.cpp

@@ -50,7 +50,7 @@ void KServerSession::OnClientClosed() {
     if (handler) {
         // Note that after this returns, this server session's hle_handler is
         // invalidated (set to null).
-        handler->ClientDisconnected(parent);
+        handler->ClientDisconnected(this);
     }
 }
 

+ 4 - 1
src/core/hle/kernel/k_server_session.h

@@ -9,6 +9,8 @@
 #include <utility>
 #include <vector>
 
+#include <boost/intrusive/list.hpp>
+
 #include "common/threadsafe_queue.h"
 #include "core/hle/kernel/k_synchronization_object.h"
 #include "core/hle/kernel/service_thread.h"
@@ -31,7 +33,8 @@ class KSession;
 class SessionRequestHandler;
 class KThread;
 
-class KServerSession final : public KSynchronizationObject {
+class KServerSession final : public KSynchronizationObject,
+                             public boost::intrusive::list_base_hook<> {
     KERNEL_AUTOOBJECT_TRAITS(KServerSession, KSynchronizationObject);
 
     friend class ServiceThread;

+ 15 - 3
src/core/hle/kernel/k_session.cpp

@@ -1,8 +1,9 @@
-// Copyright 2019 yuzu emulator team
+// Copyright 2021 yuzu emulator team
 // Licensed under GPLv2 or any later version
 // Refer to the license.txt file included.
 
 #include "common/assert.h"
+#include "core/hle/kernel/k_client_port.h"
 #include "core/hle/kernel/k_client_session.h"
 #include "core/hle/kernel/k_scoped_resource_reservation.h"
 #include "core/hle/kernel/k_server_session.h"
@@ -14,7 +15,7 @@ KSession::KSession(KernelCore& kernel)
     : KAutoObjectWithSlabHeapAndContainer{kernel}, server{kernel}, client{kernel} {}
 KSession::~KSession() = default;
 
-void KSession::Initialize(std::string&& name_) {
+void KSession::Initialize(KClientPort* port_, std::string&& name_) {
     // 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
@@ -37,11 +38,22 @@ void KSession::Initialize(std::string&& name_) {
     process = kernel.CurrentProcess();
     process->Open();
 
+    // Set our port.
+    port = port_;
+    if (port != nullptr) {
+        port->Open();
+    }
+
     // Mark initialized.
     initialized = true;
 }
 
-void KSession::Finalize() {}
+void KSession::Finalize() {
+    if (port != nullptr) {
+        port->OnSessionFinalized();
+        port->Close();
+    }
+}
 
 void KSession::OnServerClosed() {
     if (GetState() == State::Normal) {

+ 1 - 1
src/core/hle/kernel/k_session.h

@@ -28,7 +28,7 @@ public:
     explicit KSession(KernelCore& kernel);
     virtual ~KSession() override;
 
-    void Initialize(std::string&& name_);
+    void Initialize(KClientPort* port_, std::string&& name_);
 
     virtual void Finalize() override;
 

+ 5 - 1
src/core/hle/kernel/kernel.h

@@ -37,6 +37,8 @@ class KClientSession;
 class KEvent;
 class KLinkedListNode;
 class KMemoryManager;
+class KPort;
+class Process;
 class KResourceLimit;
 class KScheduler;
 class KSession;
@@ -45,7 +47,6 @@ class KThread;
 class KTransferMemory;
 class KWritableEvent;
 class PhysicalCore;
-class Process;
 class ServiceThread;
 class Synchronization;
 class TimeManager;
@@ -272,6 +273,8 @@ public:
             return slab_heap_container->event;
         } else if constexpr (std::is_same_v<T, KLinkedListNode>) {
             return slab_heap_container->linked_list_node;
+        } else if constexpr (std::is_same_v<T, KPort>) {
+            return slab_heap_container->port;
         } else if constexpr (std::is_same_v<T, Process>) {
             return slab_heap_container->process;
         } else if constexpr (std::is_same_v<T, KResourceLimit>) {
@@ -323,6 +326,7 @@ private:
         KSlabHeap<KClientSession> client_session;
         KSlabHeap<KEvent> event;
         KSlabHeap<KLinkedListNode> linked_list_node;
+        KSlabHeap<KPort> port;
         KSlabHeap<Process> process;
         KSlabHeap<KResourceLimit> resource_limit;
         KSlabHeap<KSession> session;

+ 13 - 9
src/core/hle/kernel/svc.cpp

@@ -293,9 +293,7 @@ static ResultCode UnmapMemory32(Core::System& system, u32 dst_addr, u32 src_addr
 /// Connect to an OS service given the port name, returns the handle to the port to out
 static ResultCode ConnectToNamedPort(Core::System& system, Handle* out_handle,
                                      VAddr port_name_address) {
-    std::lock_guard lock{HLE::g_hle_lock};
     auto& memory = system.Memory();
-
     if (!memory.IsValidVirtualAddress(port_name_address)) {
         LOG_ERROR(Kernel_SVC,
                   "Port Name Address is not a valid virtual address, port_name_address=0x{:016X}",
@@ -314,21 +312,27 @@ static ResultCode ConnectToNamedPort(Core::System& system, Handle* out_handle,
 
     LOG_TRACE(Kernel_SVC, "called port_name={}", port_name);
 
+    // Get the current handle table.
     auto& kernel = system.Kernel();
+    auto& handle_table = kernel.CurrentProcess()->GetHandleTable();
+
+    // Find the client port.
     const auto it = kernel.FindNamedPort(port_name);
     if (!kernel.IsValidNamedPort(it)) {
         LOG_WARNING(Kernel_SVC, "tried to connect to unknown port: {}", port_name);
         return ResultNotFound;
     }
+    auto port = it->second;
 
-    auto client_port = it->second;
+    // Create a session.
+    KClientSession* session{};
+    R_TRY(port->CreateSession(std::addressof(session)));
 
-    KClientSession* client_session{};
-    CASCADE_RESULT(client_session, client_port->Connect());
+    // Register the session in the table, close the extra reference.
+    handle_table.Add(out_handle, session);
+    session->Close();
 
-    // Return the client session
-    auto& handle_table = kernel.CurrentProcess()->GetHandleTable();
-    handle_table.Add(out_handle, client_session);
+    // We succeeded.
     return RESULT_SUCCESS;
 }
 
@@ -340,13 +344,13 @@ static ResultCode ConnectToNamedPort32(Core::System& system, Handle* out_handle,
 
 /// Makes a blocking IPC call to an OS service.
 static ResultCode SendSyncRequest(Core::System& system, Handle handle) {
-    LOG_TRACE(Kernel_SVC, "called handle=0x{:08X}({})", handle, session->GetName());
 
     auto& kernel = system.Kernel();
 
     KScopedAutoObject session =
         kernel.CurrentProcess()->GetHandleTable().GetObject<KClientSession>(handle);
     R_UNLESS(session.IsNotNull(), ResultInvalidHandle);
+    LOG_TRACE(Kernel_SVC, "called handle=0x{:08X}({})", handle, session->GetName());
 
     auto thread = kernel.CurrentScheduler()->GetCurrentThread();
     {

+ 1 - 0
src/core/hle/kernel/svc_results.h

@@ -36,6 +36,7 @@ constexpr ResultCode ResultBusy{ErrorModule::Kernel, 122};
 constexpr ResultCode ResultSessionClosed{ErrorModule::Kernel, 123};
 constexpr ResultCode ResultInvalidState{ErrorModule::Kernel, 125};
 constexpr ResultCode ResultReservedUsed{ErrorModule::Kernel, 126};
+constexpr ResultCode ResultPortClosed{ErrorModule::Kernel, 131};
 constexpr ResultCode ResultLimitReached{ErrorModule::Kernel, 132};
 constexpr ResultCode ResultInvalidId{ErrorModule::Kernel, 519};
 

+ 5 - 4
src/core/hle/service/service.cpp

@@ -116,10 +116,11 @@ void ServiceFrameworkBase::InstallAsNamedPort(Kernel::KernelCore& kernel) {
 
     ASSERT(!port_installed);
 
-    auto [server_port, client_port] =
-        Kernel::KServerPort::CreatePortPair(kernel, max_sessions, service_name);
-    server_port->SetHleHandler(shared_from_this());
-    kernel.AddNamedPort(service_name, client_port);
+    auto* port = Kernel::KPort::Create(kernel);
+    port->Initialize(max_sessions, false, service_name);
+    port->GetServerPort().SetHleHandler(shared_from_this());
+    kernel.AddNamedPort(service_name, &port->GetClientPort());
+
     port_installed = true;
 }
 

+ 16 - 15
src/core/hle/service/sm/sm.cpp

@@ -8,6 +8,7 @@
 #include "core/hle/ipc_helpers.h"
 #include "core/hle/kernel/k_client_port.h"
 #include "core/hle/kernel/k_client_session.h"
+#include "core/hle/kernel/k_port.h"
 #include "core/hle/kernel/k_server_port.h"
 #include "core/hle/kernel/k_server_session.h"
 #include "core/hle/kernel/k_session.h"
@@ -59,13 +60,12 @@ ResultVal<Kernel::KServerPort*> ServiceManager::RegisterService(std::string name
         return ERR_ALREADY_REGISTERED;
     }
 
-    auto [server_port, client_port] =
-        Kernel::KServerPort::CreatePortPair(kernel, max_sessions, name);
+    auto* port = Kernel::KPort::Create(kernel);
+    port->Initialize(max_sessions, false, name);
 
-    client_port->Open();
+    registered_services.emplace(std::move(name), port);
 
-    registered_services.emplace(std::move(name), client_port);
-    return MakeResult(server_port);
+    return MakeResult(&port->GetServerPort());
 }
 
 ResultCode ServiceManager::UnregisterService(const std::string& name) {
@@ -83,7 +83,7 @@ ResultCode ServiceManager::UnregisterService(const std::string& name) {
     return RESULT_SUCCESS;
 }
 
-ResultVal<Kernel::KClientPort*> ServiceManager::GetServicePort(const std::string& name) {
+ResultVal<Kernel::KPort*> ServiceManager::GetServicePort(const std::string& name) {
 
     CASCADE_CODE(ValidateServiceName(name));
     auto it = registered_services.find(name);
@@ -118,25 +118,26 @@ void SM::GetService(Kernel::HLERequestContext& ctx) {
 
     std::string name(name_buf.begin(), end);
 
-    auto client_port = service_manager->GetServicePort(name);
-    if (client_port.Failed()) {
+    auto result = service_manager->GetServicePort(name);
+    if (result.Failed()) {
         IPC::ResponseBuilder rb{ctx, 2};
-        rb.Push(client_port.Code());
-        LOG_ERROR(Service_SM, "called service={} -> error 0x{:08X}", name, client_port.Code().raw);
+        rb.Push(result.Code());
+        LOG_ERROR(Service_SM, "called service={} -> error 0x{:08X}", name, result.Code().raw);
         if (name.length() == 0)
             return; // LibNX Fix
         UNIMPLEMENTED();
         return;
     }
 
+    auto* port = result.Unwrap();
+
     auto* session = Kernel::KSession::Create(kernel);
-    session->Initialize(std::move(name));
+    session->Initialize(&port->GetClientPort(), std::move(name));
 
-    const auto& server_port = client_port.Unwrap()->GetServerPort();
-    if (server_port->GetHLEHandler()) {
-        server_port->GetHLEHandler()->ClientConnected(session);
+    if (port->GetServerPort().GetHLEHandler()) {
+        port->GetServerPort().GetHLEHandler()->ClientConnected(&session->GetServerSession());
     } else {
-        server_port->AppendPendingSession(&session->GetServerSession());
+        port->EnqueueSession(&session->GetServerSession());
     }
 
     LOG_DEBUG(Service_SM, "called service={} -> session={}", name, session->GetObjectId());

+ 6 - 7
src/core/hle/service/sm/sm.h

@@ -10,9 +10,7 @@
 #include <unordered_map>
 
 #include "common/concepts.h"
-#include "core/hle/kernel/k_client_port.h"
-#include "core/hle/kernel/k_server_port.h"
-#include "core/hle/kernel/object.h"
+#include "core/hle/kernel/k_port.h"
 #include "core/hle/result.h"
 #include "core/hle/service/service.h"
 
@@ -24,6 +22,7 @@ namespace Kernel {
 class KClientPort;
 class KClientSession;
 class KernelCore;
+class KPort;
 class KServerPort;
 class SessionRequestHandler;
 } // namespace Kernel
@@ -57,7 +56,7 @@ public:
 
     ResultVal<Kernel::KServerPort*> RegisterService(std::string name, u32 max_sessions);
     ResultCode UnregisterService(const std::string& name);
-    ResultVal<Kernel::KClientPort*> GetServicePort(const std::string& name);
+    ResultVal<Kernel::KPort*> GetServicePort(const std::string& name);
 
     template <Common::DerivedFrom<Kernel::SessionRequestHandler> T>
     std::shared_ptr<T> GetService(const std::string& service_name) const {
@@ -66,11 +65,11 @@ public:
             LOG_DEBUG(Service, "Can't find service: {}", service_name);
             return nullptr;
         }
-        auto port = service->second->GetServerPort();
+        auto* port = service->second;
         if (port == nullptr) {
             return nullptr;
         }
-        return std::static_pointer_cast<T>(port->GetHLEHandler());
+        return std::static_pointer_cast<T>(port->GetServerPort().GetHLEHandler());
     }
 
     void InvokeControlRequest(Kernel::HLERequestContext& context);
@@ -80,7 +79,7 @@ private:
     std::unique_ptr<Controller> controller_interface;
 
     /// Map of registered services, retrieved using GetServicePort.
-    std::unordered_map<std::string, Kernel::KClientPort*> registered_services;
+    std::unordered_map<std::string, Kernel::KPort*> registered_services;
 
     /// Kernel context
     Kernel::KernelCore& kernel;