diff options
Diffstat (limited to 'src/core/hle/kernel/k_client_port.cpp')
-rw-r--r-- | src/core/hle/kernel/k_client_port.cpp | 125 |
1 files changed, 125 insertions, 0 deletions
diff --git a/src/core/hle/kernel/k_client_port.cpp b/src/core/hle/kernel/k_client_port.cpp new file mode 100644 index 000000000..b6f1d713f --- /dev/null +++ b/src/core/hle/kernel/k_client_port.cpp @@ -0,0 +1,125 @@ +// 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_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/svc_results.h" + +namespace Kernel { + +KClientPort::KClientPort(KernelCore& kernel) : KSynchronizationObject{kernel} {} +KClientPort::~KClientPort() = default; + +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_); +} + +void KClientPort::OnSessionFinalized() { + KScopedSchedulerLock sl{kernel}; + + const auto prev = num_sessions--; + if (prev == max_sessions) { + this->NotifyAvailable(); + } +} + +void KClientPort::OnServerClosed() {} + +bool KClientPort::IsLight() const { + return this->GetParent()->IsLight(); +} + +bool KClientPort::IsServerClosed() const { + return this->GetParent()->IsServerClosed(); +} + +void KClientPort::Destroy() { + // Note with our parent that we're closed. + parent->OnClientClosed(); + + // Close our reference to our parent. + parent->Close(); +} + +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 |