summaryrefslogtreecommitdiffstats
path: root/src/core/hle/kernel/k_client_port.cpp
blob: 700ae71e33ea642267dab7acc8e9ca28f8d5b664 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
// SPDX-FileCopyrightText: 2021 Citra Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later

#include "common/scope_exit.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_port_, s32 max_sessions_, std::string&& name_) {
    // Set member variables.
    num_sessions = 0;
    peak_sessions = 0;
    parent = parent_port_;
    max_sessions = max_sessions_;
    name = std::move(name_);
}

void KClientPort::OnSessionFinalized() {
    KScopedSchedulerLock sl{kernel};

    // This might happen if a session was improperly used with this port.
    ASSERT_MSG(num_sessions > 0, "num_sessions is invalid");

    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;
}

Result KClientPort::CreateSession(KClientSession** out) {
    // Reserve a new session from the resource limit.
    //! FIXME: we are reserving this from the wrong resource limit!
    KScopedResourceReservation session_reservation(kernel.ApplicationProcess()->GetResourceLimit(),
                                                   LimitableResource::SessionCountMax);
    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 ResultSuccess;
}

} // namespace Kernel