|
@@ -1,12 +1,14 @@
|
|
|
-// Copyright 2016 Citra Emulator Project
|
|
|
|
|
|
|
+// Copyright 2021 Citra Emulator Project
|
|
|
// Licensed under GPLv2 or any later version
|
|
// Licensed under GPLv2 or any later version
|
|
|
// Refer to the license.txt file included.
|
|
// Refer to the license.txt file included.
|
|
|
|
|
|
|
|
|
|
+#include "common/scope_exit.h"
|
|
|
#include "core/hle/kernel/hle_ipc.h"
|
|
#include "core/hle/kernel/hle_ipc.h"
|
|
|
#include "core/hle/kernel/k_client_port.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/k_session.h"
|
|
|
-#include "core/hle/kernel/object.h"
|
|
|
|
|
#include "core/hle/kernel/svc_results.h"
|
|
#include "core/hle/kernel/svc_results.h"
|
|
|
|
|
|
|
|
namespace Kernel {
|
|
namespace Kernel {
|
|
@@ -14,45 +16,110 @@ namespace Kernel {
|
|
|
KClientPort::KClientPort(KernelCore& kernel) : KSynchronizationObject{kernel} {}
|
|
KClientPort::KClientPort(KernelCore& kernel) : KSynchronizationObject{kernel} {}
|
|
|
KClientPort::~KClientPort() = default;
|
|
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_;
|
|
max_sessions = max_sessions_;
|
|
|
name = std::move(name_);
|
|
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 {
|
|
bool KClientPort::IsSignaled() const {
|
|
|
return num_sessions < max_sessions;
|
|
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
|
|
} // namespace Kernel
|