From ddfabf31330c0c8e0558a714b6d7e1f1948b73ed Mon Sep 17 00:00:00 2001
From: Subv <subv2112@gmail.com>
Date: Wed, 4 Jan 2017 23:23:17 -0500
Subject: [PATCH 1/3] Kernel: Use a Session object to keep track of the status
 of a Client/Server session pair.

Reduce the associated port's connection count when a ServerSession is destroyed.
---
 src/core/CMakeLists.txt                |  1 +
 src/core/hle/kernel/client_port.cpp    | 13 +++++-------
 src/core/hle/kernel/client_session.cpp | 24 +++++++++++++--------
 src/core/hle/kernel/client_session.h   | 11 +++++-----
 src/core/hle/kernel/server_session.cpp | 29 +++++++++++++++++++-------
 src/core/hle/kernel/server_session.h   | 12 ++++++++---
 src/core/hle/kernel/session.h          | 27 ++++++++++++++++++++++++
 src/core/hle/result.h                  |  1 +
 8 files changed, 86 insertions(+), 32 deletions(-)
 create mode 100644 src/core/hle/kernel/session.h

diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt
index 61a0b1cc37..b161c05ba3 100644
--- a/src/core/CMakeLists.txt
+++ b/src/core/CMakeLists.txt
@@ -241,6 +241,7 @@ set(HEADERS
             hle/kernel/semaphore.h
             hle/kernel/server_port.h
             hle/kernel/server_session.h
+            hle/kernel/session.h
             hle/kernel/shared_memory.h
             hle/kernel/thread.h
             hle/kernel/timer.h
diff --git a/src/core/hle/kernel/client_port.cpp b/src/core/hle/kernel/client_port.cpp
index 22645f4ecb..ddcf4c916c 100644
--- a/src/core/hle/kernel/client_port.cpp
+++ b/src/core/hle/kernel/client_port.cpp
@@ -19,24 +19,21 @@ ResultVal<SharedPtr<ClientSession>> ClientPort::Connect() {
     // AcceptSession before returning from this call.
 
     if (active_sessions >= max_sessions) {
-        // TODO(Subv): Return an error code in this situation after session disconnection is
-        // implemented.
-        /*return ResultCode(ErrorDescription::MaxConnectionsReached,
-                          ErrorModule::OS, ErrorSummary::WouldBlock,
-                          ErrorLevel::Temporary);*/
+        return ResultCode(ErrorDescription::MaxConnectionsReached, ErrorModule::OS,
+                          ErrorSummary::WouldBlock, ErrorLevel::Temporary);
     }
     active_sessions++;
 
     // Create a new session pair, let the created sessions inherit the parent port's HLE handler.
     auto sessions =
-        ServerSession::CreateSessionPair(server_port->GetName(), server_port->hle_handler);
+        ServerSession::CreateSessionPair(server_port->GetName(), server_port->hle_handler, this);
     auto client_session = std::get<SharedPtr<ClientSession>>(sessions);
     auto server_session = std::get<SharedPtr<ServerSession>>(sessions);
 
     if (server_port->hle_handler)
         server_port->hle_handler->ClientConnected(server_session);
-
-    server_port->pending_sessions.push_back(std::move(server_session));
+    else
+        server_port->pending_sessions.push_back(std::move(server_session));
 
     // Wake the threads waiting on the ServerPort
     server_port->WakeupAllWaitingThreads();
diff --git a/src/core/hle/kernel/client_session.cpp b/src/core/hle/kernel/client_session.cpp
index 0331386ec2..c2f48176e0 100644
--- a/src/core/hle/kernel/client_session.cpp
+++ b/src/core/hle/kernel/client_session.cpp
@@ -14,27 +14,33 @@ ClientSession::~ClientSession() {
     // This destructor will be called automatically when the last ClientSession handle is closed by
     // the emulated application.
 
-    if (server_session->hle_handler)
-        server_session->hle_handler->ClientDisconnected(server_session);
+    if (parent->server) {
+        if (parent->server->hle_handler)
+            parent->server->hle_handler->ClientDisconnected(parent->server);
 
-    // TODO(Subv): If the session is still open, set the connection status to 2 (Closed by client),
-    // wake up all the ServerSession's waiting threads and set the WaitSynchronization result to
-    // 0xC920181A.
+        // TODO(Subv): Force a wake up of all the ServerSession's waiting threads and set
+        // their WaitSynchronization result to 0xC920181A.
+    }
+
+    parent->client = nullptr;
 }
 
-ResultVal<SharedPtr<ClientSession>> ClientSession::Create(ServerSession* server_session,
-                                                          std::string name) {
+ResultVal<SharedPtr<ClientSession>> ClientSession::Create(std::string name) {
     SharedPtr<ClientSession> client_session(new ClientSession);
 
     client_session->name = std::move(name);
-    client_session->server_session = server_session;
+    client_session->parent = nullptr;
     client_session->session_status = SessionStatus::Open;
     return MakeResult<SharedPtr<ClientSession>>(std::move(client_session));
 }
 
 ResultCode ClientSession::SendSyncRequest() {
     // Signal the server session that new data is available
-    return server_session->HandleSyncRequest();
+    if (parent->server)
+        return parent->server->HandleSyncRequest();
+
+    return ResultCode(ErrorDescription::SessionClosedByRemote, ErrorModule::OS,
+                      ErrorSummary::Canceled, ErrorLevel::Status);
 }
 
 } // namespace
diff --git a/src/core/hle/kernel/client_session.h b/src/core/hle/kernel/client_session.h
index ed468dec60..adb2d0b5ff 100644
--- a/src/core/hle/kernel/client_session.h
+++ b/src/core/hle/kernel/client_session.h
@@ -14,6 +14,7 @@
 namespace Kernel {
 
 class ServerSession;
+class Session;
 
 enum class SessionStatus {
     Open = 1,
@@ -44,8 +45,10 @@ public:
      */
     ResultCode SendSyncRequest();
 
-    std::string name;              ///< Name of client port (optional)
-    ServerSession* server_session; ///< The server session associated with this client session.
+    std::string name; ///< Name of client port (optional)
+
+    /// The parent session, which links to the server endpoint.
+    std::shared_ptr<Session> parent;
     SessionStatus session_status;  ///< The session's current status.
 
 private:
@@ -54,12 +57,10 @@ private:
 
     /**
      * Creates a client session.
-     * @param server_session The server session associated with this client session
      * @param name Optional name of client session
      * @return The created client session
      */
-    static ResultVal<SharedPtr<ClientSession>> Create(ServerSession* server_session,
-                                                      std::string name = "Unknown");
+    static ResultVal<SharedPtr<ClientSession>> Create(std::string name = "Unknown");
 };
 
 } // namespace
diff --git a/src/core/hle/kernel/server_session.cpp b/src/core/hle/kernel/server_session.cpp
index 9447ff236a..a93e55c766 100644
--- a/src/core/hle/kernel/server_session.cpp
+++ b/src/core/hle/kernel/server_session.cpp
@@ -14,8 +14,15 @@ ServerSession::ServerSession() = default;
 ServerSession::~ServerSession() {
     // This destructor will be called automatically when the last ServerSession handle is closed by
     // the emulated application.
-    // TODO(Subv): Reduce the ClientPort's connection count,
-    // if the session is still open, set the connection status to 3 (Closed by server),
+
+    // Decrease the port's connection count.
+    if (parent->port)
+        parent->port->active_sessions--;
+
+    // TODO(Subv): Wake up all the ClientSession's waiting threads and set
+    // the SendSyncRequest result to 0xC920181A.
+
+    parent->server = nullptr;
 }
 
 ResultVal<SharedPtr<ServerSession>> ServerSession::Create(
@@ -25,6 +32,7 @@ ResultVal<SharedPtr<ServerSession>> ServerSession::Create(
     server_session->name = std::move(name);
     server_session->signaled = false;
     server_session->hle_handler = std::move(hle_handler);
+    server_session->parent = nullptr;
 
     return MakeResult<SharedPtr<ServerSession>>(std::move(server_session));
 }
@@ -61,13 +69,20 @@ ResultCode ServerSession::HandleSyncRequest() {
 }
 
 ServerSession::SessionPair ServerSession::CreateSessionPair(
-    const std::string& name, std::shared_ptr<Service::SessionRequestHandler> hle_handler) {
+    const std::string& name, std::shared_ptr<Service::SessionRequestHandler> hle_handler,
+    SharedPtr<ClientPort> port) {
+
     auto server_session =
         ServerSession::Create(name + "_Server", std::move(hle_handler)).MoveFrom();
-    // We keep a non-owning pointer to the ServerSession in the ClientSession because we don't want
-    // to prevent the ServerSession's destructor from being called when the emulated
-    // application closes the last ServerSession handle.
-    auto client_session = ClientSession::Create(server_session.get(), name + "_Client").MoveFrom();
+    auto client_session = ClientSession::Create(name + "_Client").MoveFrom();
+
+    std::shared_ptr<Session> parent(new Session);
+    parent->client = client_session.get();
+    parent->server = server_session.get();
+    parent->port = port;
+
+    client_session->parent = parent;
+    server_session->parent = parent;
 
     return std::make_tuple(std::move(server_session), std::move(client_session));
 }
diff --git a/src/core/hle/kernel/server_session.h b/src/core/hle/kernel/server_session.h
index 761fc4781a..c907d487c4 100644
--- a/src/core/hle/kernel/server_session.h
+++ b/src/core/hle/kernel/server_session.h
@@ -9,6 +9,7 @@
 #include "common/assert.h"
 #include "common/common_types.h"
 #include "core/hle/kernel/kernel.h"
+#include "core/hle/kernel/session.h"
 #include "core/hle/kernel/thread.h"
 #include "core/hle/result.h"
 #include "core/hle/service/service.h"
@@ -17,6 +18,8 @@
 namespace Kernel {
 
 class ClientSession;
+class ClientPort;
+class ServerSession;
 
 /**
  * Kernel object representing the server endpoint of an IPC session. Sessions are the basic CTR-OS
@@ -47,11 +50,13 @@ public:
      * Creates a pair of ServerSession and an associated ClientSession.
      * @param name        Optional name of the ports.
      * @param hle_handler Optional HLE handler for this server session.
+     * @param client_port Optional The ClientPort that spawned this session.
      * @return The created session tuple
      */
     static SessionPair CreateSessionPair(
         const std::string& name = "Unknown",
-        std::shared_ptr<Service::SessionRequestHandler> hle_handler = nullptr);
+        std::shared_ptr<Service::SessionRequestHandler> hle_handler = nullptr,
+        SharedPtr<ClientPort> client_port = nullptr);
 
     /**
      * Handle a sync request from the emulated application.
@@ -63,8 +68,9 @@ public:
 
     void Acquire(Thread* thread) override;
 
-    std::string name; ///< The name of this session (optional)
-    bool signaled;    ///< Whether there's new data available to this ServerSession
+    std::string name;                ///< The name of this session (optional)
+    bool signaled;                   ///< Whether there's new data available to this ServerSession
+    std::shared_ptr<Session> parent; ///< The parent session, which links to the client endpoint.
     std::shared_ptr<Service::SessionRequestHandler>
         hle_handler; ///< This session's HLE request handler (optional)
 
diff --git a/src/core/hle/kernel/session.h b/src/core/hle/kernel/session.h
new file mode 100644
index 0000000000..a45e78022b
--- /dev/null
+++ b/src/core/hle/kernel/session.h
@@ -0,0 +1,27 @@
+// Copyright 2017 Citra Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include "core/hle/kernel/kernel.h"
+
+namespace Kernel {
+
+class ClientSession;
+class ClientPort;
+class ServerSession;
+
+/**
+ * Parent structure to link the client and server endpoints of a session with their associated
+ * client port. The client port need not exist, as is the case for portless sessions like the
+ * FS File and Directory sessions. When one of the endpoints of a session is destroyed, its
+ * corresponding field in this structure will be set to nullptr.
+ */
+class Session final {
+public:
+    ClientSession* client = nullptr; ///< The client endpoint of the session.
+    ServerSession* server = nullptr; ///< The server endpoint of the session.
+    SharedPtr<ClientPort> port;      ///< The port that this session is associated with (optional).
+};
+}
diff --git a/src/core/hle/result.h b/src/core/hle/result.h
index cfefbbc64f..13b9488715 100644
--- a/src/core/hle/result.h
+++ b/src/core/hle/result.h
@@ -16,6 +16,7 @@
 /// Detailed description of the error. This listing is likely incomplete.
 enum class ErrorDescription : u32 {
     Success = 0,
+    SessionClosedByRemote = 26,
     WrongPermission = 46,
     OS_InvalidBufferDescriptor = 48,
     MaxConnectionsReached = 52,

From ce5bc477ca0cd59df4922c1c6cd245db365dc7bc Mon Sep 17 00:00:00 2001
From: Subv <subv2112@gmail.com>
Date: Wed, 4 Jan 2017 23:24:06 -0500
Subject: [PATCH 2/3] Kernel: Remove a now unused enum and variable regarding a
 session's status.

---
 src/core/hle/kernel/client_session.cpp | 1 -
 src/core/hle/kernel/client_session.h   | 7 -------
 2 files changed, 8 deletions(-)

diff --git a/src/core/hle/kernel/client_session.cpp b/src/core/hle/kernel/client_session.cpp
index c2f48176e0..6737b204b2 100644
--- a/src/core/hle/kernel/client_session.cpp
+++ b/src/core/hle/kernel/client_session.cpp
@@ -30,7 +30,6 @@ ResultVal<SharedPtr<ClientSession>> ClientSession::Create(std::string name) {
 
     client_session->name = std::move(name);
     client_session->parent = nullptr;
-    client_session->session_status = SessionStatus::Open;
     return MakeResult<SharedPtr<ClientSession>>(std::move(client_session));
 }
 
diff --git a/src/core/hle/kernel/client_session.h b/src/core/hle/kernel/client_session.h
index adb2d0b5ff..5ec4ee9014 100644
--- a/src/core/hle/kernel/client_session.h
+++ b/src/core/hle/kernel/client_session.h
@@ -16,12 +16,6 @@ namespace Kernel {
 class ServerSession;
 class Session;
 
-enum class SessionStatus {
-    Open = 1,
-    ClosedByClient = 2,
-    ClosedBYServer = 3,
-};
-
 class ClientSession final : public Object {
 public:
     friend class ServerSession;
@@ -49,7 +43,6 @@ public:
 
     /// The parent session, which links to the server endpoint.
     std::shared_ptr<Session> parent;
-    SessionStatus session_status;  ///< The session's current status.
 
 private:
     ClientSession();

From 37347bfa380464a1ee1236d2a35f1ec1b697e4b6 Mon Sep 17 00:00:00 2001
From: Subv <subv2112@gmail.com>
Date: Sun, 21 May 2017 18:52:42 -0500
Subject: [PATCH 3/3] Kernel/Sessions: Remove the ClientSession::Create
 function.

It is not meant to be used by anything other than CreateSessionPair.
---
 src/core/hle/kernel/client_session.cpp | 8 --------
 src/core/hle/kernel/client_session.h   | 7 -------
 src/core/hle/kernel/server_session.cpp | 4 +++-
 3 files changed, 3 insertions(+), 16 deletions(-)

diff --git a/src/core/hle/kernel/client_session.cpp b/src/core/hle/kernel/client_session.cpp
index 6737b204b2..e297b7464d 100644
--- a/src/core/hle/kernel/client_session.cpp
+++ b/src/core/hle/kernel/client_session.cpp
@@ -25,14 +25,6 @@ ClientSession::~ClientSession() {
     parent->client = nullptr;
 }
 
-ResultVal<SharedPtr<ClientSession>> ClientSession::Create(std::string name) {
-    SharedPtr<ClientSession> client_session(new ClientSession);
-
-    client_session->name = std::move(name);
-    client_session->parent = nullptr;
-    return MakeResult<SharedPtr<ClientSession>>(std::move(client_session));
-}
-
 ResultCode ClientSession::SendSyncRequest() {
     // Signal the server session that new data is available
     if (parent->server)
diff --git a/src/core/hle/kernel/client_session.h b/src/core/hle/kernel/client_session.h
index 5ec4ee9014..9f3adb72b8 100644
--- a/src/core/hle/kernel/client_session.h
+++ b/src/core/hle/kernel/client_session.h
@@ -47,13 +47,6 @@ public:
 private:
     ClientSession();
     ~ClientSession() override;
-
-    /**
-     * Creates a client session.
-     * @param name Optional name of client session
-     * @return The created client session
-     */
-    static ResultVal<SharedPtr<ClientSession>> Create(std::string name = "Unknown");
 };
 
 } // namespace
diff --git a/src/core/hle/kernel/server_session.cpp b/src/core/hle/kernel/server_session.cpp
index a93e55c766..500b909ab2 100644
--- a/src/core/hle/kernel/server_session.cpp
+++ b/src/core/hle/kernel/server_session.cpp
@@ -74,7 +74,9 @@ ServerSession::SessionPair ServerSession::CreateSessionPair(
 
     auto server_session =
         ServerSession::Create(name + "_Server", std::move(hle_handler)).MoveFrom();
-    auto client_session = ClientSession::Create(name + "_Client").MoveFrom();
+
+    SharedPtr<ClientSession> client_session(new ClientSession);
+    client_session->name = name + "_Client";
 
     std::shared_ptr<Session> parent(new Session);
     parent->client = client_session.get();