diff options
Diffstat (limited to '')
-rw-r--r-- | src/core/hle/kernel/k_client_port.cpp | 119 |
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 |