summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/core/CMakeLists.txt2
-rw-r--r--src/core/hle/ipc_helpers.h4
-rw-r--r--src/core/hle/kernel/hle_ipc.cpp10
-rw-r--r--src/core/hle/kernel/hle_ipc.h10
-rw-r--r--src/core/hle/kernel/init/init_slab_setup.cpp4
-rw-r--r--src/core/hle/kernel/k_client_port.cpp119
-rw-r--r--src/core/hle/kernel/k_client_port.h46
-rw-r--r--src/core/hle/kernel/k_event.h2
-rw-r--r--src/core/hle/kernel/k_port.cpp68
-rw-r--r--src/core/hle/kernel/k_port.h87
-rw-r--r--src/core/hle/kernel/k_server_port.cpp94
-rw-r--r--src/core/hle/kernel/k_server_port.h55
-rw-r--r--src/core/hle/kernel/k_server_session.cpp2
-rw-r--r--src/core/hle/kernel/k_server_session.h5
-rw-r--r--src/core/hle/kernel/k_session.cpp18
-rw-r--r--src/core/hle/kernel/k_session.h2
-rw-r--r--src/core/hle/kernel/kernel.h6
-rw-r--r--src/core/hle/kernel/svc.cpp22
-rw-r--r--src/core/hle/kernel/svc_results.h1
-rw-r--r--src/core/hle/service/service.cpp9
-rw-r--r--src/core/hle/service/sm/sm.cpp31
-rw-r--r--src/core/hle/service/sm/sm.h13
22 files changed, 444 insertions, 166 deletions
diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt
index 487e6f720..cee6d30f6 100644
--- a/src/core/CMakeLists.txt
+++ b/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
diff --git a/src/core/hle/ipc_helpers.h b/src/core/hle/ipc_helpers.h
index 18aebf6ea..8128445fd 100644
--- a/src/core/hle/ipc_helpers.h
+++ b/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());
}
}
diff --git a/src/core/hle/kernel/hle_ipc.cpp b/src/core/hle/kernel/hle_ipc.cpp
index 9e1e63204..ddff9ce99 100644
--- a/src/core/hle/kernel/hle_ipc.cpp
+++ b/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_,
diff --git a/src/core/hle/kernel/hle_ipc.h b/src/core/hle/kernel/hle_ipc.h
index b7484c445..d63c730ac 100644
--- a/src/core/hle/kernel/hle_ipc.h
+++ b/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);
};
/**
diff --git a/src/core/hle/kernel/init/init_slab_setup.cpp b/src/core/hle/kernel/init/init_slab_setup.cpp
index a5ddd7344..f8c255732 100644
--- a/src/core/hle/kernel/init/init_slab_setup.cpp
+++ b/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__)
diff --git a/src/core/hle/kernel/k_client_port.cpp b/src/core/hle/kernel/k_client_port.cpp
index 00e1bbc59..b6f1d713f 100644
--- a/src/core/hle/kernel/k_client_port.cpp
+++ b/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
diff --git a/src/core/hle/kernel/k_client_port.h b/src/core/hle/kernel/k_client_port.h
index 60dea4763..43a17f4a4 100644
--- a/src/core/hle/kernel/k_client_port.h
+++ b/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
diff --git a/src/core/hle/kernel/k_event.h b/src/core/hle/kernel/k_event.h
index 2c48a0499..45634e401 100644
--- a/src/core/hle/kernel/k_event.h
+++ b/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);
diff --git a/src/core/hle/kernel/k_port.cpp b/src/core/hle/kernel/k_port.cpp
new file mode 100644
index 000000000..734aa2a8c
--- /dev/null
+++ b/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
diff --git a/src/core/hle/kernel/k_port.h b/src/core/hle/kernel/k_port.h
new file mode 100644
index 000000000..68c8ed8df
--- /dev/null
+++ b/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
diff --git a/src/core/hle/kernel/k_server_port.cpp b/src/core/hle/kernel/k_server_port.cpp
index 2c51d66db..fcc04abaa 100644
--- a/src/core/hle/kernel/k_server_port.cpp
+++ b/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
diff --git a/src/core/hle/kernel/k_server_port.h b/src/core/hle/kernel/k_server_port.h
index 13fa54e5e..9f45ca3f4 100644
--- a/src/core/hle/kernel/k_server_port.h
+++ b/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;
};
diff --git a/src/core/hle/kernel/k_server_session.cpp b/src/core/hle/kernel/k_server_session.cpp
index 46ba7081b..863f9aa5f 100644
--- a/src/core/hle/kernel/k_server_session.cpp
+++ b/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);
}
}
diff --git a/src/core/hle/kernel/k_server_session.h b/src/core/hle/kernel/k_server_session.h
index ef81c4e30..d748754d0 100644
--- a/src/core/hle/kernel/k_server_session.h
+++ b/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;
diff --git a/src/core/hle/kernel/k_session.cpp b/src/core/hle/kernel/k_session.cpp
index ca1cf18cd..6f4276189 100644
--- a/src/core/hle/kernel/k_session.cpp
+++ b/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) {
diff --git a/src/core/hle/kernel/k_session.h b/src/core/hle/kernel/k_session.h
index 6a6fcb588..1597cc608 100644
--- a/src/core/hle/kernel/k_session.h
+++ b/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;
diff --git a/src/core/hle/kernel/kernel.h b/src/core/hle/kernel/kernel.h
index 19b3530b4..723be6b51 100644
--- a/src/core/hle/kernel/kernel.h
+++ b/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;
diff --git a/src/core/hle/kernel/svc.cpp b/src/core/hle/kernel/svc.cpp
index 8d8d3dd5a..ef8fa98a9 100644
--- a/src/core/hle/kernel/svc.cpp
+++ b/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();
{
diff --git a/src/core/hle/kernel/svc_results.h b/src/core/hle/kernel/svc_results.h
index cd32acd99..53a940723 100644
--- a/src/core/hle/kernel/svc_results.h
+++ b/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};
diff --git a/src/core/hle/service/service.cpp b/src/core/hle/service/service.cpp
index a882b3b4e..42e464024 100644
--- a/src/core/hle/service/service.cpp
+++ b/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;
}
diff --git a/src/core/hle/service/sm/sm.cpp b/src/core/hle/service/sm/sm.cpp
index 51274bfb1..71ab4b6f5 100644
--- a/src/core/hle/service/sm/sm.cpp
+++ b/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());
diff --git a/src/core/hle/service/sm/sm.h b/src/core/hle/service/sm/sm.h
index b0204c4bb..af5010c3b 100644
--- a/src/core/hle/service/sm/sm.h
+++ b/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;