summaryrefslogtreecommitdiffstats
path: root/src/core/hle/kernel/k_client_port.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/core/hle/kernel/k_client_port.cpp')
-rw-r--r--src/core/hle/kernel/k_client_port.cpp119
1 files changed, 93 insertions, 26 deletions
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