diff options
Diffstat (limited to 'src/core/hle/service/os')
-rw-r--r-- | src/core/hle/service/os/event.cpp | 31 | ||||
-rw-r--r-- | src/core/hle/service/os/event.h | 31 | ||||
-rw-r--r-- | src/core/hle/service/os/multi_wait.cpp | 59 | ||||
-rw-r--r-- | src/core/hle/service/os/multi_wait.h | 36 | ||||
-rw-r--r-- | src/core/hle/service/os/multi_wait_holder.cpp | 25 | ||||
-rw-r--r-- | src/core/hle/service/os/multi_wait_holder.h | 44 | ||||
-rw-r--r-- | src/core/hle/service/os/multi_wait_utils.h | 109 | ||||
-rw-r--r-- | src/core/hle/service/os/mutex.cpp | 46 | ||||
-rw-r--r-- | src/core/hle/service/os/mutex.h | 31 |
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 |