summaryrefslogtreecommitdiffstats
path: root/src/core/hle/service/os
diff options
context:
space:
mode:
Diffstat (limited to 'src/core/hle/service/os')
-rw-r--r--src/core/hle/service/os/event.cpp31
-rw-r--r--src/core/hle/service/os/event.h31
-rw-r--r--src/core/hle/service/os/multi_wait.cpp59
-rw-r--r--src/core/hle/service/os/multi_wait.h36
-rw-r--r--src/core/hle/service/os/multi_wait_holder.cpp25
-rw-r--r--src/core/hle/service/os/multi_wait_holder.h44
-rw-r--r--src/core/hle/service/os/multi_wait_utils.h109
-rw-r--r--src/core/hle/service/os/mutex.cpp46
-rw-r--r--src/core/hle/service/os/mutex.h31
9 files changed, 412 insertions, 0 deletions
diff --git a/src/core/hle/service/os/event.cpp b/src/core/hle/service/os/event.cpp
new file mode 100644
index 000000000..ec52c17fd
--- /dev/null
+++ b/src/core/hle/service/os/event.cpp
@@ -0,0 +1,31 @@
+// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#include "core/hle/kernel/k_event.h"
+#include "core/hle/service/kernel_helpers.h"
+#include "core/hle/service/os/event.h"
+
+namespace Service {
+
+Event::Event(KernelHelpers::ServiceContext& ctx) {
+ m_event = ctx.CreateEvent("Event");
+}
+
+Event::~Event() {
+ m_event->GetReadableEvent().Close();
+ m_event->Close();
+}
+
+void Event::Signal() {
+ m_event->Signal();
+}
+
+void Event::Clear() {
+ m_event->Clear();
+}
+
+Kernel::KReadableEvent* Event::GetHandle() {
+ return &m_event->GetReadableEvent();
+}
+
+} // namespace Service
diff --git a/src/core/hle/service/os/event.h b/src/core/hle/service/os/event.h
new file mode 100644
index 000000000..cdbc4635a
--- /dev/null
+++ b/src/core/hle/service/os/event.h
@@ -0,0 +1,31 @@
+// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#pragma once
+
+namespace Kernel {
+class KEvent;
+class KReadableEvent;
+} // namespace Kernel
+
+namespace Service {
+
+namespace KernelHelpers {
+class ServiceContext;
+}
+
+class Event {
+public:
+ explicit Event(KernelHelpers::ServiceContext& ctx);
+ ~Event();
+
+ void Signal();
+ void Clear();
+
+ Kernel::KReadableEvent* GetHandle();
+
+private:
+ Kernel::KEvent* m_event;
+};
+
+} // namespace Service
diff --git a/src/core/hle/service/os/multi_wait.cpp b/src/core/hle/service/os/multi_wait.cpp
new file mode 100644
index 000000000..7b80d28be
--- /dev/null
+++ b/src/core/hle/service/os/multi_wait.cpp
@@ -0,0 +1,59 @@
+// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#include "core/hle/kernel/k_hardware_timer.h"
+#include "core/hle/kernel/k_synchronization_object.h"
+#include "core/hle/kernel/kernel.h"
+#include "core/hle/kernel/svc_common.h"
+#include "core/hle/service/os/multi_wait.h"
+
+namespace Service {
+
+MultiWait::MultiWait() = default;
+MultiWait::~MultiWait() = default;
+
+MultiWaitHolder* MultiWait::WaitAny(Kernel::KernelCore& kernel) {
+ return this->TimedWaitImpl(kernel, -1);
+}
+
+MultiWaitHolder* MultiWait::TryWaitAny(Kernel::KernelCore& kernel) {
+ return this->TimedWaitImpl(kernel, 0);
+}
+
+MultiWaitHolder* MultiWait::TimedWaitAny(Kernel::KernelCore& kernel, s64 timeout_ns) {
+ return this->TimedWaitImpl(kernel, kernel.HardwareTimer().GetTick() + timeout_ns);
+}
+
+MultiWaitHolder* MultiWait::TimedWaitImpl(Kernel::KernelCore& kernel, s64 timeout_tick) {
+ std::array<MultiWaitHolder*, Kernel::Svc::ArgumentHandleCountMax> holders{};
+ std::array<Kernel::KSynchronizationObject*, Kernel::Svc::ArgumentHandleCountMax> objects{};
+
+ s32 out_index = -1;
+ s32 num_objects = 0;
+
+ for (auto it = m_wait_list.begin(); it != m_wait_list.end(); it++) {
+ ASSERT(num_objects < Kernel::Svc::ArgumentHandleCountMax);
+ holders[num_objects] = std::addressof(*it);
+ objects[num_objects] = it->GetNativeHandle();
+ num_objects++;
+ }
+
+ Kernel::KSynchronizationObject::Wait(kernel, std::addressof(out_index), objects.data(),
+ num_objects, timeout_tick);
+
+ if (out_index == -1) {
+ return nullptr;
+ } else {
+ return holders[out_index];
+ }
+}
+
+void MultiWait::MoveAll(MultiWait* other) {
+ while (!other->m_wait_list.empty()) {
+ MultiWaitHolder& holder = other->m_wait_list.front();
+ holder.UnlinkFromMultiWait();
+ holder.LinkToMultiWait(this);
+ }
+}
+
+} // namespace Service
diff --git a/src/core/hle/service/os/multi_wait.h b/src/core/hle/service/os/multi_wait.h
new file mode 100644
index 000000000..340c611b5
--- /dev/null
+++ b/src/core/hle/service/os/multi_wait.h
@@ -0,0 +1,36 @@
+// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#pragma once
+
+#include "core/hle/service/os/multi_wait_holder.h"
+
+namespace Kernel {
+class KernelCore;
+}
+
+namespace Service {
+
+class MultiWait final {
+public:
+ explicit MultiWait();
+ ~MultiWait();
+
+public:
+ MultiWaitHolder* WaitAny(Kernel::KernelCore& kernel);
+ MultiWaitHolder* TryWaitAny(Kernel::KernelCore& kernel);
+ MultiWaitHolder* TimedWaitAny(Kernel::KernelCore& kernel, s64 timeout_ns);
+ // TODO: SdkReplyAndReceive?
+
+ void MoveAll(MultiWait* other);
+
+private:
+ MultiWaitHolder* TimedWaitImpl(Kernel::KernelCore& kernel, s64 timeout_tick);
+
+private:
+ friend class MultiWaitHolder;
+ using ListType = Common::IntrusiveListMemberTraits<&MultiWaitHolder::m_list_node>::ListType;
+ ListType m_wait_list{};
+};
+
+} // namespace Service
diff --git a/src/core/hle/service/os/multi_wait_holder.cpp b/src/core/hle/service/os/multi_wait_holder.cpp
new file mode 100644
index 000000000..01efa045b
--- /dev/null
+++ b/src/core/hle/service/os/multi_wait_holder.cpp
@@ -0,0 +1,25 @@
+// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#include "core/hle/service/os/multi_wait.h"
+#include "core/hle/service/os/multi_wait_holder.h"
+
+namespace Service {
+
+void MultiWaitHolder::LinkToMultiWait(MultiWait* multi_wait) {
+ if (m_multi_wait != nullptr) {
+ UNREACHABLE();
+ }
+
+ m_multi_wait = multi_wait;
+ m_multi_wait->m_wait_list.push_back(*this);
+}
+
+void MultiWaitHolder::UnlinkFromMultiWait() {
+ if (m_multi_wait) {
+ m_multi_wait->m_wait_list.erase(m_multi_wait->m_wait_list.iterator_to(*this));
+ m_multi_wait = nullptr;
+ }
+}
+
+} // namespace Service
diff --git a/src/core/hle/service/os/multi_wait_holder.h b/src/core/hle/service/os/multi_wait_holder.h
new file mode 100644
index 000000000..646395a3f
--- /dev/null
+++ b/src/core/hle/service/os/multi_wait_holder.h
@@ -0,0 +1,44 @@
+// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#pragma once
+
+#include "common/intrusive_list.h"
+
+namespace Kernel {
+class KSynchronizationObject;
+} // namespace Kernel
+
+namespace Service {
+
+class MultiWait;
+
+class MultiWaitHolder {
+public:
+ explicit MultiWaitHolder(Kernel::KSynchronizationObject* native_handle)
+ : m_native_handle(native_handle) {}
+
+ void LinkToMultiWait(MultiWait* multi_wait);
+ void UnlinkFromMultiWait();
+
+ void SetUserData(uintptr_t user_data) {
+ m_user_data = user_data;
+ }
+
+ uintptr_t GetUserData() const {
+ return m_user_data;
+ }
+
+ Kernel::KSynchronizationObject* GetNativeHandle() const {
+ return m_native_handle;
+ }
+
+private:
+ friend class MultiWait;
+ Common::IntrusiveListNode m_list_node{};
+ MultiWait* m_multi_wait{};
+ Kernel::KSynchronizationObject* m_native_handle{};
+ uintptr_t m_user_data{};
+};
+
+} // namespace Service
diff --git a/src/core/hle/service/os/multi_wait_utils.h b/src/core/hle/service/os/multi_wait_utils.h
new file mode 100644
index 000000000..96d3a10f3
--- /dev/null
+++ b/src/core/hle/service/os/multi_wait_utils.h
@@ -0,0 +1,109 @@
+// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#pragma once
+
+#include "core/hle/service/os/multi_wait.h"
+
+namespace Service {
+
+namespace impl {
+
+class AutoMultiWaitHolder {
+private:
+ MultiWaitHolder m_holder;
+
+public:
+ template <typename T>
+ explicit AutoMultiWaitHolder(MultiWait* multi_wait, T&& arg) : m_holder(arg) {
+ m_holder.LinkToMultiWait(multi_wait);
+ }
+
+ ~AutoMultiWaitHolder() {
+ m_holder.UnlinkFromMultiWait();
+ }
+
+ std::pair<MultiWaitHolder*, int> ConvertResult(const std::pair<MultiWaitHolder*, int> result,
+ int index) {
+ if (result.first == std::addressof(m_holder)) {
+ return std::make_pair(static_cast<MultiWaitHolder*>(nullptr), index);
+ } else {
+ return result;
+ }
+ }
+};
+
+using WaitAnyFunction = decltype(&MultiWait::WaitAny);
+
+inline std::pair<MultiWaitHolder*, int> WaitAnyImpl(Kernel::KernelCore& kernel,
+ MultiWait* multi_wait, WaitAnyFunction func,
+ int) {
+ return std::pair<MultiWaitHolder*, int>((multi_wait->*func)(kernel), -1);
+}
+
+template <typename T, typename... Args>
+inline std::pair<MultiWaitHolder*, int> WaitAnyImpl(Kernel::KernelCore& kernel,
+ MultiWait* multi_wait, WaitAnyFunction func,
+ int index, T&& x, Args&&... args) {
+ AutoMultiWaitHolder holder(multi_wait, std::forward<T>(x));
+ return holder.ConvertResult(
+ WaitAnyImpl(kernel, multi_wait, func, index + 1, std::forward<Args>(args)...), index);
+}
+
+template <typename... Args>
+inline std::pair<MultiWaitHolder*, int> WaitAnyImpl(Kernel::KernelCore& kernel,
+ MultiWait* multi_wait, WaitAnyFunction func,
+ Args&&... args) {
+ return WaitAnyImpl(kernel, multi_wait, func, 0, std::forward<Args>(args)...);
+}
+
+template <typename... Args>
+inline std::pair<MultiWaitHolder*, int> WaitAnyImpl(Kernel::KernelCore& kernel,
+ WaitAnyFunction func, Args&&... args) {
+ MultiWait temp_multi_wait;
+ return WaitAnyImpl(kernel, std::addressof(temp_multi_wait), func, 0,
+ std::forward<Args>(args)...);
+}
+
+class NotBoolButInt {
+public:
+ constexpr NotBoolButInt(int v) : m_value(v) {}
+ constexpr operator int() const {
+ return m_value;
+ }
+ explicit operator bool() const = delete;
+
+private:
+ int m_value;
+};
+
+} // namespace impl
+
+template <typename... Args>
+ requires(sizeof...(Args) > 0)
+inline std::pair<MultiWaitHolder*, int> WaitAny(Kernel::KernelCore& kernel, MultiWait* multi_wait,
+ Args&&... args) {
+ return impl::WaitAnyImpl(kernel, &MultiWait::WaitAny, multi_wait, std::forward<Args>(args)...);
+}
+
+template <typename... Args>
+ requires(sizeof...(Args) > 0)
+inline int WaitAny(Kernel::KernelCore& kernel, Args&&... args) {
+ return impl::WaitAnyImpl(kernel, &MultiWait::WaitAny, std::forward<Args>(args)...).second;
+}
+
+template <typename... Args>
+ requires(sizeof...(Args) > 0)
+inline std::pair<MultiWaitHolder*, int> TryWaitAny(Kernel::KernelCore& kernel,
+ MultiWait* multi_wait, Args&&... args) {
+ return impl::WaitAnyImpl(kernel, &MultiWait::TryWaitAny, multi_wait,
+ std::forward<Args>(args)...);
+}
+
+template <typename... Args>
+ requires(sizeof...(Args) > 0)
+inline impl::NotBoolButInt TryWaitAny(Kernel::KernelCore& kernel, Args&&... args) {
+ return impl::WaitAnyImpl(kernel, &MultiWait::TryWaitAny, std::forward<Args>(args)...).second;
+}
+
+} // namespace Service
diff --git a/src/core/hle/service/os/mutex.cpp b/src/core/hle/service/os/mutex.cpp
new file mode 100644
index 000000000..6009f4866
--- /dev/null
+++ b/src/core/hle/service/os/mutex.cpp
@@ -0,0 +1,46 @@
+// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#include "core/core.h"
+#include "core/hle/kernel/k_event.h"
+#include "core/hle/kernel/k_synchronization_object.h"
+#include "core/hle/service/os/mutex.h"
+
+namespace Service {
+
+Mutex::Mutex(Core::System& system) : m_system(system) {
+ m_event = Kernel::KEvent::Create(system.Kernel());
+ m_event->Initialize(nullptr);
+
+ // Register the event.
+ Kernel::KEvent::Register(system.Kernel(), m_event);
+
+ ASSERT(R_SUCCEEDED(m_event->Signal()));
+}
+
+Mutex::~Mutex() {
+ m_event->GetReadableEvent().Close();
+ m_event->Close();
+}
+
+void Mutex::lock() {
+ // Infinitely retry until we successfully clear the event.
+ while (R_FAILED(m_event->GetReadableEvent().Reset())) {
+ s32 index;
+ Kernel::KSynchronizationObject* obj = &m_event->GetReadableEvent();
+
+ // The event was already cleared!
+ // Wait for it to become signaled again.
+ ASSERT(R_SUCCEEDED(
+ Kernel::KSynchronizationObject::Wait(m_system.Kernel(), &index, &obj, 1, -1)));
+ }
+
+ // We successfully cleared the event, and now have exclusive ownership.
+}
+
+void Mutex::unlock() {
+ // Unlock.
+ ASSERT(R_SUCCEEDED(m_event->Signal()));
+}
+
+} // namespace Service
diff --git a/src/core/hle/service/os/mutex.h b/src/core/hle/service/os/mutex.h
new file mode 100644
index 000000000..95ac9b117
--- /dev/null
+++ b/src/core/hle/service/os/mutex.h
@@ -0,0 +1,31 @@
+// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#pragma once
+
+#include "common/common_types.h"
+
+namespace Core {
+class System;
+}
+
+namespace Kernel {
+class KEvent;
+}
+
+namespace Service {
+
+class Mutex {
+public:
+ explicit Mutex(Core::System& system);
+ ~Mutex();
+
+ void lock();
+ void unlock();
+
+private:
+ Core::System& m_system;
+ Kernel::KEvent* m_event{};
+};
+
+} // namespace Service