diff options
Diffstat (limited to 'src/core/hle/kernel/k_auto_object.h')
-rw-r--r-- | src/core/hle/kernel/k_auto_object.h | 290 |
1 files changed, 290 insertions, 0 deletions
diff --git a/src/core/hle/kernel/k_auto_object.h b/src/core/hle/kernel/k_auto_object.h new file mode 100644 index 000000000..567dad204 --- /dev/null +++ b/src/core/hle/kernel/k_auto_object.h @@ -0,0 +1,290 @@ +// Copyright 2021 yuzu Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include <atomic> + +#include "common/assert.h" +#include "common/common_funcs.h" +#include "common/common_types.h" +#include "common/intrusive_red_black_tree.h" +#include "core/hle/kernel/k_class_token.h" + +namespace Kernel { + +class Process; + +#define KERNEL_AUTOOBJECT_TRAITS(CLASS, BASE_CLASS) \ + NON_COPYABLE(CLASS); \ + NON_MOVEABLE(CLASS); \ + \ +private: \ + friend class ::Kernel::KClassTokenGenerator; \ + static constexpr inline auto ObjectType = ::Kernel::KClassTokenGenerator::ObjectType::CLASS; \ + static constexpr inline const char* const TypeName = #CLASS; \ + static constexpr inline ClassTokenType ClassToken() { \ + return ::Kernel::ClassToken<CLASS>; \ + } \ + \ +public: \ + using BaseClass = BASE_CLASS; \ + static constexpr TypeObj GetStaticTypeObj() { \ + constexpr ClassTokenType Token = ClassToken(); \ + return TypeObj(TypeName, Token); \ + } \ + static constexpr const char* GetStaticTypeName() { \ + return TypeName; \ + } \ + virtual TypeObj GetTypeObj() const { \ + return GetStaticTypeObj(); \ + } \ + virtual const char* GetTypeName() { \ + return GetStaticTypeName(); \ + } \ + \ +private: + +class KAutoObject { +protected: + class TypeObj { + private: + const char* m_name; + ClassTokenType m_class_token; + + public: + constexpr explicit TypeObj(const char* n, ClassTokenType tok) + : m_name(n), m_class_token(tok) { // ... + } + + constexpr const char* GetName() const { + return m_name; + } + constexpr ClassTokenType GetClassToken() const { + return m_class_token; + } + + constexpr bool operator==(const TypeObj& rhs) { + return this->GetClassToken() == rhs.GetClassToken(); + } + + constexpr bool operator!=(const TypeObj& rhs) { + return this->GetClassToken() != rhs.GetClassToken(); + } + + constexpr bool IsDerivedFrom(const TypeObj& rhs) { + return (this->GetClassToken() | rhs.GetClassToken()) == this->GetClassToken(); + } + }; + +private: + KERNEL_AUTOOBJECT_TRAITS(KAutoObject, KAutoObject); + +private: + std::atomic<u32> m_ref_count; + +public: + static KAutoObject* Create(KAutoObject* ptr); + +public: + constexpr explicit KAutoObject() : m_ref_count(0) {} + virtual ~KAutoObject() {} + + // Destroy is responsible for destroying the auto object's resources when ref_count hits zero. + virtual void Destroy() { + UNIMPLEMENTED(); + } + + // Finalize is responsible for cleaning up resource, but does not destroy the object. + virtual void Finalize() { + UNIMPLEMENTED(); + } + + virtual Process* GetOwner() const { + return nullptr; + } + + u32 GetReferenceCount() const { + return m_ref_count.load(); + } + + bool IsDerivedFrom(const TypeObj& rhs) const { + return this->GetTypeObj().IsDerivedFrom(rhs); + } + + bool IsDerivedFrom(const KAutoObject& rhs) const { + return this->IsDerivedFrom(rhs.GetTypeObj()); + } + + template <typename Derived> + Derived DynamicCast() { + static_assert(std::is_pointer<Derived>::value); + using DerivedType = typename std::remove_pointer<Derived>::type; + + if (this->IsDerivedFrom(DerivedType::GetStaticTypeObj())) { + return static_cast<Derived>(this); + } else { + return nullptr; + } + } + + template <typename Derived> + const Derived DynamicCast() const { + static_assert(std::is_pointer<Derived>::value); + using DerivedType = typename std::remove_pointer<Derived>::type; + + if (this->IsDerivedFrom(DerivedType::GetStaticTypeObj())) { + return static_cast<Derived>(this); + } else { + return nullptr; + } + } + + bool Open() { + // Atomically increment the reference count, only if it's positive. + u32 cur_ref_count = m_ref_count.load(std::memory_order_acquire); + do { + if (cur_ref_count == 0) { + return false; + } + ASSERT(cur_ref_count < cur_ref_count + 1); + } while (!m_ref_count.compare_exchange_weak(cur_ref_count, cur_ref_count + 1, + std::memory_order_relaxed)); + + return true; + } + + void Close() { + // Atomically decrement the reference count, not allowing it to become negative. + u32 cur_ref_count = m_ref_count.load(std::memory_order_acquire); + do { + ASSERT(cur_ref_count > 0); + } while (!m_ref_count.compare_exchange_weak(cur_ref_count, cur_ref_count - 1, + std::memory_order_relaxed)); + + // If ref count hits zero, destroy the object. + if (cur_ref_count - 1 == 0) { + this->Destroy(); + } + } +}; + +class KAutoObjectWithListContainer; + +class KAutoObjectWithList : public KAutoObject { +private: + friend class KAutoObjectWithListContainer; + +private: + Common::IntrusiveRedBlackTreeNode list_node; + +public: + static int Compare(const KAutoObjectWithList& lhs, const KAutoObjectWithList& rhs) { + const u64 lid = lhs.GetId(); + const u64 rid = rhs.GetId(); + + if (lid < rid) { + return -1; + } else if (lid > rid) { + return 1; + } else { + return 0; + } + } + +public: + virtual u64 GetId() const { + return reinterpret_cast<u64>(this); + } +}; + +template <typename T> +class KScopedAutoObject { + NON_COPYABLE(KScopedAutoObject); + +private: + template <typename U> + friend class KScopedAutoObject; + +private: + T* m_obj; + +private: + constexpr void Swap(KScopedAutoObject& rhs) { + std::swap(m_obj, rhs.m_obj); + } + +public: + constexpr KScopedAutoObject() : m_obj(nullptr) { // ... + } + constexpr KScopedAutoObject(T* o) : m_obj(o) { + if (m_obj != nullptr) { + m_obj->Open(); + } + } + + ~KScopedAutoObject() { + if (m_obj != nullptr) { + m_obj->Close(); + } + m_obj = nullptr; + } + + template <typename U> + requires(std::derived_from<T, U> || + std::derived_from<U, T>) constexpr KScopedAutoObject(KScopedAutoObject<U>&& rhs) { + if constexpr (std::derived_from<U, T>) { + // Upcast. + m_obj = rhs.m_obj; + rhs.m_obj = nullptr; + } else { + // Downcast. + T* derived = nullptr; + if (rhs.m_obj != nullptr) { + derived = rhs.m_obj->template DynamicCast<T*>(); + if (derived == nullptr) { + rhs.m_obj->Close(); + } + } + + m_obj = derived; + rhs.m_obj = nullptr; + } + } + + constexpr KScopedAutoObject<T>& operator=(KScopedAutoObject<T>&& rhs) { + rhs.Swap(*this); + return *this; + } + + constexpr T* operator->() { + return m_obj; + } + constexpr T& operator*() { + return *m_obj; + } + + constexpr void Reset(T* o) { + KScopedAutoObject(o).Swap(*this); + } + + constexpr T* GetPointerUnsafe() { + return m_obj; + } + + constexpr T* ReleasePointerUnsafe() { + T* ret = m_obj; + m_obj = nullptr; + return ret; + } + + constexpr bool IsNull() const { + return m_obj == nullptr; + } + constexpr bool IsNotNull() const { + return m_obj != nullptr; + } +}; + +} // namespace Kernel |