summaryrefslogtreecommitdiffstats
path: root/src/core/hle/kernel/k_light_server_session.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/core/hle/kernel/k_light_server_session.cpp')
-rw-r--r--src/core/hle/kernel/k_light_server_session.cpp247
1 files changed, 247 insertions, 0 deletions
diff --git a/src/core/hle/kernel/k_light_server_session.cpp b/src/core/hle/kernel/k_light_server_session.cpp
new file mode 100644
index 000000000..e5ceb01f2
--- /dev/null
+++ b/src/core/hle/kernel/k_light_server_session.cpp
@@ -0,0 +1,247 @@
+// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#include "core/hle/kernel/k_light_server_session.h"
+#include "core/hle/kernel/k_light_session.h"
+#include "core/hle/kernel/k_thread.h"
+#include "core/hle/kernel/k_thread_queue.h"
+#include "core/hle/kernel/svc_results.h"
+
+namespace Kernel {
+
+namespace {
+
+constexpr u64 InvalidThreadId = std::numeric_limits<u64>::max();
+
+class ThreadQueueImplForKLightServerSessionRequest final : public KThreadQueue {
+private:
+ KThread::WaiterList* m_wait_list;
+
+public:
+ ThreadQueueImplForKLightServerSessionRequest(KernelCore& kernel, KThread::WaiterList* wl)
+ : KThreadQueue(kernel), m_wait_list(wl) {}
+
+ virtual void EndWait(KThread* waiting_thread, Result wait_result) override {
+ // Remove the thread from our wait list.
+ m_wait_list->erase(m_wait_list->iterator_to(*waiting_thread));
+
+ // Invoke the base end wait handler.
+ KThreadQueue::EndWait(waiting_thread, wait_result);
+ }
+
+ virtual void CancelWait(KThread* waiting_thread, Result wait_result,
+ bool cancel_timer_task) override {
+ // Remove the thread from our wait list.
+ m_wait_list->erase(m_wait_list->iterator_to(*waiting_thread));
+
+ // Invoke the base cancel wait handler.
+ KThreadQueue::CancelWait(waiting_thread, wait_result, cancel_timer_task);
+ }
+};
+
+class ThreadQueueImplForKLightServerSessionReceive final : public KThreadQueue {
+private:
+ KThread** m_server_thread;
+
+public:
+ ThreadQueueImplForKLightServerSessionReceive(KernelCore& kernel, KThread** st)
+ : KThreadQueue(kernel), m_server_thread(st) {}
+
+ virtual void EndWait(KThread* waiting_thread, Result wait_result) override {
+ // Clear the server thread.
+ *m_server_thread = nullptr;
+
+ // Set the waiting thread as not cancelable.
+ waiting_thread->ClearCancellable();
+
+ // Invoke the base end wait handler.
+ KThreadQueue::EndWait(waiting_thread, wait_result);
+ }
+
+ virtual void CancelWait(KThread* waiting_thread, Result wait_result,
+ bool cancel_timer_task) override {
+ // Clear the server thread.
+ *m_server_thread = nullptr;
+
+ // Set the waiting thread as not cancelable.
+ waiting_thread->ClearCancellable();
+
+ // Invoke the base cancel wait handler.
+ KThreadQueue::CancelWait(waiting_thread, wait_result, cancel_timer_task);
+ }
+};
+
+} // namespace
+
+KLightServerSession::KLightServerSession(KernelCore& kernel) : KAutoObject(kernel) {}
+KLightServerSession::~KLightServerSession() = default;
+
+void KLightServerSession::Destroy() {
+ this->CleanupRequests();
+
+ m_parent->OnServerClosed();
+}
+
+void KLightServerSession::OnClientClosed() {
+ this->CleanupRequests();
+}
+
+Result KLightServerSession::OnRequest(KThread* request_thread) {
+ ThreadQueueImplForKLightServerSessionRequest wait_queue(m_kernel,
+ std::addressof(m_request_list));
+
+ // Send the request.
+ {
+ // Lock the scheduler.
+ KScopedSchedulerLock sl(m_kernel);
+
+ // Check that the server isn't closed.
+ R_UNLESS(!m_parent->IsServerClosed(), ResultSessionClosed);
+
+ // Check that the request thread isn't terminating.
+ R_UNLESS(!request_thread->IsTerminationRequested(), ResultTerminationRequested);
+
+ // Add the request thread to our list.
+ m_request_list.push_back(*request_thread);
+
+ // Begin waiting on the request.
+ request_thread->SetWaitReasonForDebugging(ThreadWaitReasonForDebugging::IPC);
+ request_thread->BeginWait(std::addressof(wait_queue));
+
+ // If we have a server thread, end its wait.
+ if (m_server_thread != nullptr) {
+ m_server_thread->EndWait(ResultSuccess);
+ }
+ }
+
+ // NOTE: Nintendo returns GetCurrentThread().GetWaitResult() here.
+ // This is technically incorrect, although it doesn't cause problems in practice
+ // because this is only ever called with request_thread = GetCurrentThreadPointer().
+ R_RETURN(request_thread->GetWaitResult());
+}
+
+Result KLightServerSession::ReplyAndReceive(u32* data) {
+ // Set the server context.
+ GetCurrentThread(m_kernel).SetLightSessionData(data);
+
+ // Reply, if we need to.
+ if (data[0] & KLightSession::ReplyFlag) {
+ KScopedSchedulerLock sl(m_kernel);
+
+ // Check that we're open.
+ R_UNLESS(!m_parent->IsClientClosed(), ResultSessionClosed);
+ R_UNLESS(!m_parent->IsServerClosed(), ResultSessionClosed);
+
+ // Check that we have a request to reply to.
+ R_UNLESS(m_current_request != nullptr, ResultInvalidState);
+
+ // Check that the server thread id is correct.
+ R_UNLESS(m_server_thread_id == GetCurrentThread(m_kernel).GetId(), ResultInvalidState);
+
+ // If we can reply, do so.
+ if (!m_current_request->IsTerminationRequested()) {
+ std::memcpy(m_current_request->GetLightSessionData(),
+ GetCurrentThread(m_kernel).GetLightSessionData(), KLightSession::DataSize);
+ m_current_request->EndWait(ResultSuccess);
+ }
+
+ // Close our current request.
+ m_current_request->Close();
+
+ // Clear our current request.
+ m_current_request = nullptr;
+ m_server_thread_id = InvalidThreadId;
+ }
+
+ // Create the wait queue for our receive.
+ ThreadQueueImplForKLightServerSessionReceive wait_queue(m_kernel,
+ std::addressof(m_server_thread));
+
+ // Receive.
+ while (true) {
+ // Try to receive a request.
+ {
+ KScopedSchedulerLock sl(m_kernel);
+
+ // Check that we aren't already receiving.
+ R_UNLESS(m_server_thread == nullptr, ResultInvalidState);
+ R_UNLESS(m_server_thread_id == InvalidThreadId, ResultInvalidState);
+
+ // Check that we're open.
+ R_UNLESS(!m_parent->IsClientClosed(), ResultSessionClosed);
+ R_UNLESS(!m_parent->IsServerClosed(), ResultSessionClosed);
+
+ // Check that we're not terminating.
+ R_UNLESS(!GetCurrentThread(m_kernel).IsTerminationRequested(),
+ ResultTerminationRequested);
+
+ // If we have a request available, use it.
+ if (auto head = m_request_list.begin(); head != m_request_list.end()) {
+ // Set our current request.
+ m_current_request = std::addressof(*head);
+ m_current_request->Open();
+
+ // Set our server thread id.
+ m_server_thread_id = GetCurrentThread(m_kernel).GetId();
+
+ // Copy the client request data.
+ std::memcpy(GetCurrentThread(m_kernel).GetLightSessionData(),
+ m_current_request->GetLightSessionData(), KLightSession::DataSize);
+
+ // We successfully received.
+ R_SUCCEED();
+ }
+
+ // We need to wait for a request to come in.
+
+ // Check if we were cancelled.
+ if (GetCurrentThread(m_kernel).IsWaitCancelled()) {
+ GetCurrentThread(m_kernel).ClearWaitCancelled();
+ R_THROW(ResultCancelled);
+ }
+
+ // Mark ourselves as cancellable.
+ GetCurrentThread(m_kernel).SetCancellable();
+
+ // Wait for a request to come in.
+ m_server_thread = GetCurrentThreadPointer(m_kernel);
+ GetCurrentThread(m_kernel).SetWaitReasonForDebugging(ThreadWaitReasonForDebugging::IPC);
+ GetCurrentThread(m_kernel).BeginWait(std::addressof(wait_queue));
+ }
+
+ // We waited to receive a request; if our wait failed, return the failing result.
+ R_TRY(GetCurrentThread(m_kernel).GetWaitResult());
+ }
+}
+
+void KLightServerSession::CleanupRequests() {
+ // Cleanup all pending requests.
+ {
+ KScopedSchedulerLock sl(m_kernel);
+
+ // Handle the current request.
+ if (m_current_request != nullptr) {
+ // Reply to the current request.
+ if (!m_current_request->IsTerminationRequested()) {
+ m_current_request->EndWait(ResultSessionClosed);
+ }
+
+ // Clear our current request.
+ m_current_request->Close();
+ m_current_request = nullptr;
+ m_server_thread_id = InvalidThreadId;
+ }
+
+ // Reply to all other requests.
+ for (auto& thread : m_request_list) {
+ thread.EndWait(ResultSessionClosed);
+ }
+
+ // Wait up our server thread, if we have one.
+ if (m_server_thread != nullptr) {
+ m_server_thread->EndWait(ResultSessionClosed);
+ }
+ }
+}
+
+} // namespace Kernel