diff options
78 files changed, 3696 insertions, 1983 deletions
| diff --git a/CMakeLists.txt b/CMakeLists.txt index 5793c5d57..aaf3a90cf 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -26,6 +26,10 @@ option(ENABLE_CUBEB "Enables the cubeb audio backend" ON)  option(USE_DISCORD_PRESENCE "Enables Discord Rich Presence" OFF) +if (NOT ENABLE_WEB_SERVICE) +    set(YUZU_ENABLE_BOXCAT OFF) +endif() +  # Default to a Release build  get_property(IS_MULTI_CONFIG GLOBAL PROPERTY GENERATOR_IS_MULTI_CONFIG)  if (NOT IS_MULTI_CONFIG AND NOT CMAKE_BUILD_TYPE) @@ -239,7 +243,7 @@ if(ENABLE_QT)      if (YUZU_USE_QT_WEB_ENGINE)          find_package(Qt5 COMPONENTS WebEngineCore WebEngineWidgets)      endif() -	 +      if (ENABLE_QT_TRANSLATION)          find_package(Qt5 REQUIRED COMPONENTS LinguistTools ${QT_PREFIX_HINT})      endif() @@ -322,7 +326,7 @@ if (CONAN_REQUIRED_LIBS)              list(APPEND Boost_LIBRARIES Boost::context)          endif()      endif() -     +      # Due to issues with variable scopes in functions, we need to also find_package(qt5) outside of the function      if(ENABLE_QT)          list(APPEND CMAKE_MODULE_PATH "${CONAN_QT_ROOT_RELEASE}") @@ -30,7 +30,6 @@ If you want to contribute to the user interface translation, please check out th  * __Windows__: [Windows Build](https://github.com/yuzu-emu/yuzu/wiki/Building-For-Windows)  * __Linux__: [Linux Build](https://github.com/yuzu-emu/yuzu/wiki/Building-For-Linux) -* __macOS__: [macOS Build](https://github.com/yuzu-emu/yuzu/wiki/Building-for-macOS)  ### Support diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 8777df751..61adbef28 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -45,10 +45,15 @@ if (MSVC)          # Warnings          /W3 +        /we4062 # enumerator 'identifier' in a switch of enum 'enumeration' is not handled +        /we4101 # 'identifier': unreferenced local variable +        /we4265 # 'class': class has virtual functions, but destructor is not virtual +        /we4388 # signed/unsigned mismatch          /we4547 # 'operator' : operator before comma has no effect; expected operator with side-effect          /we4549 # 'operator1': operator before comma has no effect; did you intend 'operator2'?          /we4555 # Expression has no effect; expected expression with side-effect          /we4834 # Discarding return value of function with 'nodiscard' attribute +        /we5038 # data member 'member1' will be initialized after data member 'member2'      )      # /GS- - No stack buffer overflow checks diff --git a/src/common/CMakeLists.txt b/src/common/CMakeLists.txt index 2c2bd2ee8..5d781cd77 100644 --- a/src/common/CMakeLists.txt +++ b/src/common/CMakeLists.txt @@ -123,6 +123,7 @@ add_library(common STATIC      hash.h      hex_util.cpp      hex_util.h +    intrusive_red_black_tree.h      logging/backend.cpp      logging/backend.h      logging/filter.cpp @@ -143,6 +144,7 @@ add_library(common STATIC      page_table.h      param_package.cpp      param_package.h +    parent_of_member.h      quaternion.h      ring_buffer.h      scm_rev.cpp @@ -167,6 +169,7 @@ add_library(common STATIC      time_zone.h      timer.cpp      timer.h +    tree.h      uint128.cpp      uint128.h      uuid.cpp diff --git a/src/common/common_funcs.h b/src/common/common_funcs.h index 367b6bf6e..c90978f9c 100644 --- a/src/common/common_funcs.h +++ b/src/common/common_funcs.h @@ -93,6 +93,14 @@ __declspec(dllimport) void __stdcall DebugBreak(void);          return static_cast<T>(key) == 0;                                                           \      } +/// Evaluates a boolean expression, and returns a result unless that expression is true. +#define R_UNLESS(expr, res)                                                                        \ +    {                                                                                              \ +        if (!(expr)) {                                                                             \ +            return res;                                                                            \ +        }                                                                                          \ +    } +  namespace Common {  [[nodiscard]] constexpr u32 MakeMagic(char a, char b, char c, char d) { diff --git a/src/common/div_ceil.h b/src/common/div_ceil.h index 6b2c48f91..95e1489a9 100644 --- a/src/common/div_ceil.h +++ b/src/common/div_ceil.h @@ -11,16 +11,16 @@ namespace Common {  /// Ceiled integer division.  template <typename N, typename D> -requires std::is_integral_v<N>&& std::is_unsigned_v<D>[[nodiscard]] constexpr auto DivCeil( -    N number, D divisor) { -    return (static_cast<D>(number) + divisor - 1) / divisor; +requires std::is_integral_v<N>&& std::is_unsigned_v<D>[[nodiscard]] constexpr N DivCeil(N number, +                                                                                        D divisor) { +    return static_cast<N>((static_cast<D>(number) + divisor - 1) / divisor);  }  /// Ceiled integer division with logarithmic divisor in base 2  template <typename N, typename D> -requires std::is_integral_v<N>&& std::is_unsigned_v<D>[[nodiscard]] constexpr auto DivCeilLog2( +requires std::is_integral_v<N>&& std::is_unsigned_v<D>[[nodiscard]] constexpr N DivCeilLog2(      N value, D alignment_log2) { -    return (static_cast<D>(value) + (D(1) << alignment_log2) - 1) >> alignment_log2; +    return static_cast<N>((static_cast<D>(value) + (D(1) << alignment_log2) - 1) >> alignment_log2);  }  } // namespace Common diff --git a/src/common/intrusive_red_black_tree.h b/src/common/intrusive_red_black_tree.h new file mode 100644 index 000000000..fb55de94e --- /dev/null +++ b/src/common/intrusive_red_black_tree.h @@ -0,0 +1,627 @@ +// Copyright 2021 yuzu Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include "common/parent_of_member.h" +#include "common/tree.h" + +namespace Common { + +namespace impl { + +class IntrusiveRedBlackTreeImpl; + +} + +struct IntrusiveRedBlackTreeNode { + +private: +    RB_ENTRY(IntrusiveRedBlackTreeNode) entry{}; + +    friend class impl::IntrusiveRedBlackTreeImpl; + +    template <class, class, class> +    friend class IntrusiveRedBlackTree; + +public: +    constexpr IntrusiveRedBlackTreeNode() = default; +}; + +template <class T, class Traits, class Comparator> +class IntrusiveRedBlackTree; + +namespace impl { + +class IntrusiveRedBlackTreeImpl { + +private: +    template <class, class, class> +    friend class ::Common::IntrusiveRedBlackTree; + +private: +    RB_HEAD(IntrusiveRedBlackTreeRoot, IntrusiveRedBlackTreeNode); +    using RootType = IntrusiveRedBlackTreeRoot; + +private: +    IntrusiveRedBlackTreeRoot root; + +public: +    template <bool Const> +    class Iterator; + +    using value_type = IntrusiveRedBlackTreeNode; +    using size_type = size_t; +    using difference_type = ptrdiff_t; +    using pointer = value_type*; +    using const_pointer = const value_type*; +    using reference = value_type&; +    using const_reference = const value_type&; +    using iterator = Iterator<false>; +    using const_iterator = Iterator<true>; + +    template <bool Const> +    class Iterator { +    public: +        using iterator_category = std::bidirectional_iterator_tag; +        using value_type = typename IntrusiveRedBlackTreeImpl::value_type; +        using difference_type = typename IntrusiveRedBlackTreeImpl::difference_type; +        using pointer = std::conditional_t<Const, IntrusiveRedBlackTreeImpl::const_pointer, +                                           IntrusiveRedBlackTreeImpl::pointer>; +        using reference = std::conditional_t<Const, IntrusiveRedBlackTreeImpl::const_reference, +                                             IntrusiveRedBlackTreeImpl::reference>; + +    private: +        pointer node; + +    public: +        explicit Iterator(pointer n) : node(n) {} + +        bool operator==(const Iterator& rhs) const { +            return this->node == rhs.node; +        } + +        bool operator!=(const Iterator& rhs) const { +            return !(*this == rhs); +        } + +        pointer operator->() const { +            return this->node; +        } + +        reference operator*() const { +            return *this->node; +        } + +        Iterator& operator++() { +            this->node = GetNext(this->node); +            return *this; +        } + +        Iterator& operator--() { +            this->node = GetPrev(this->node); +            return *this; +        } + +        Iterator operator++(int) { +            const Iterator it{*this}; +            ++(*this); +            return it; +        } + +        Iterator operator--(int) { +            const Iterator it{*this}; +            --(*this); +            return it; +        } + +        operator Iterator<true>() const { +            return Iterator<true>(this->node); +        } +    }; + +protected: +    // Generate static implementations for non-comparison operations for IntrusiveRedBlackTreeRoot. +    RB_GENERATE_WITHOUT_COMPARE_STATIC(IntrusiveRedBlackTreeRoot, IntrusiveRedBlackTreeNode, entry); + +private: +    // Define accessors using RB_* functions. +    constexpr void InitializeImpl() { +        RB_INIT(&this->root); +    } + +    bool EmptyImpl() const { +        return RB_EMPTY(&this->root); +    } + +    IntrusiveRedBlackTreeNode* GetMinImpl() const { +        return RB_MIN(IntrusiveRedBlackTreeRoot, +                      const_cast<IntrusiveRedBlackTreeRoot*>(&this->root)); +    } + +    IntrusiveRedBlackTreeNode* GetMaxImpl() const { +        return RB_MAX(IntrusiveRedBlackTreeRoot, +                      const_cast<IntrusiveRedBlackTreeRoot*>(&this->root)); +    } + +    IntrusiveRedBlackTreeNode* RemoveImpl(IntrusiveRedBlackTreeNode* node) { +        return RB_REMOVE(IntrusiveRedBlackTreeRoot, &this->root, node); +    } + +public: +    static IntrusiveRedBlackTreeNode* GetNext(IntrusiveRedBlackTreeNode* node) { +        return RB_NEXT(IntrusiveRedBlackTreeRoot, nullptr, node); +    } + +    static IntrusiveRedBlackTreeNode* GetPrev(IntrusiveRedBlackTreeNode* node) { +        return RB_PREV(IntrusiveRedBlackTreeRoot, nullptr, node); +    } + +    static IntrusiveRedBlackTreeNode const* GetNext(const IntrusiveRedBlackTreeNode* node) { +        return static_cast<const IntrusiveRedBlackTreeNode*>( +            GetNext(const_cast<IntrusiveRedBlackTreeNode*>(node))); +    } + +    static IntrusiveRedBlackTreeNode const* GetPrev(const IntrusiveRedBlackTreeNode* node) { +        return static_cast<const IntrusiveRedBlackTreeNode*>( +            GetPrev(const_cast<IntrusiveRedBlackTreeNode*>(node))); +    } + +public: +    constexpr IntrusiveRedBlackTreeImpl() : root() { +        this->InitializeImpl(); +    } + +    // Iterator accessors. +    iterator begin() { +        return iterator(this->GetMinImpl()); +    } + +    const_iterator begin() const { +        return const_iterator(this->GetMinImpl()); +    } + +    iterator end() { +        return iterator(static_cast<IntrusiveRedBlackTreeNode*>(nullptr)); +    } + +    const_iterator end() const { +        return const_iterator(static_cast<const IntrusiveRedBlackTreeNode*>(nullptr)); +    } + +    const_iterator cbegin() const { +        return this->begin(); +    } + +    const_iterator cend() const { +        return this->end(); +    } + +    iterator iterator_to(reference ref) { +        return iterator(&ref); +    } + +    const_iterator iterator_to(const_reference ref) const { +        return const_iterator(&ref); +    } + +    // Content management. +    bool empty() const { +        return this->EmptyImpl(); +    } + +    reference back() { +        return *this->GetMaxImpl(); +    } + +    const_reference back() const { +        return *this->GetMaxImpl(); +    } + +    reference front() { +        return *this->GetMinImpl(); +    } + +    const_reference front() const { +        return *this->GetMinImpl(); +    } + +    iterator erase(iterator it) { +        auto cur = std::addressof(*it); +        auto next = GetNext(cur); +        this->RemoveImpl(cur); +        return iterator(next); +    } +}; + +} // namespace impl + +template <typename T> +concept HasLightCompareType = requires { +    { std::is_same<typename T::LightCompareType, void>::value } +    ->std::convertible_to<bool>; +}; + +namespace impl { + +template <typename T, typename Default> +consteval auto* GetLightCompareType() { +    if constexpr (HasLightCompareType<T>) { +        return static_cast<typename T::LightCompareType*>(nullptr); +    } else { +        return static_cast<Default*>(nullptr); +    } +} + +} // namespace impl + +template <typename T, typename Default> +using LightCompareType = std::remove_pointer_t<decltype(impl::GetLightCompareType<T, Default>())>; + +template <class T, class Traits, class Comparator> +class IntrusiveRedBlackTree { + +public: +    using ImplType = impl::IntrusiveRedBlackTreeImpl; + +private: +    ImplType impl{}; + +public: +    struct IntrusiveRedBlackTreeRootWithCompare : ImplType::IntrusiveRedBlackTreeRoot {}; + +    template <bool Const> +    class Iterator; + +    using value_type = T; +    using size_type = size_t; +    using difference_type = ptrdiff_t; +    using pointer = T*; +    using const_pointer = const T*; +    using reference = T&; +    using const_reference = const T&; +    using iterator = Iterator<false>; +    using const_iterator = Iterator<true>; + +    using light_value_type = LightCompareType<Comparator, value_type>; +    using const_light_pointer = const light_value_type*; +    using const_light_reference = const light_value_type&; + +    template <bool Const> +    class Iterator { +    public: +        friend class IntrusiveRedBlackTree<T, Traits, Comparator>; + +        using ImplIterator = +            std::conditional_t<Const, ImplType::const_iterator, ImplType::iterator>; + +        using iterator_category = std::bidirectional_iterator_tag; +        using value_type = typename IntrusiveRedBlackTree::value_type; +        using difference_type = typename IntrusiveRedBlackTree::difference_type; +        using pointer = std::conditional_t<Const, IntrusiveRedBlackTree::const_pointer, +                                           IntrusiveRedBlackTree::pointer>; +        using reference = std::conditional_t<Const, IntrusiveRedBlackTree::const_reference, +                                             IntrusiveRedBlackTree::reference>; + +    private: +        ImplIterator iterator; + +    private: +        explicit Iterator(ImplIterator it) : iterator(it) {} + +        explicit Iterator(typename std::conditional<Const, ImplType::const_iterator, +                                                    ImplType::iterator>::type::pointer ptr) +            : iterator(ptr) {} + +        ImplIterator GetImplIterator() const { +            return this->iterator; +        } + +    public: +        bool operator==(const Iterator& rhs) const { +            return this->iterator == rhs.iterator; +        } + +        bool operator!=(const Iterator& rhs) const { +            return !(*this == rhs); +        } + +        pointer operator->() const { +            return Traits::GetParent(std::addressof(*this->iterator)); +        } + +        reference operator*() const { +            return *Traits::GetParent(std::addressof(*this->iterator)); +        } + +        Iterator& operator++() { +            ++this->iterator; +            return *this; +        } + +        Iterator& operator--() { +            --this->iterator; +            return *this; +        } + +        Iterator operator++(int) { +            const Iterator it{*this}; +            ++this->iterator; +            return it; +        } + +        Iterator operator--(int) { +            const Iterator it{*this}; +            --this->iterator; +            return it; +        } + +        operator Iterator<true>() const { +            return Iterator<true>(this->iterator); +        } +    }; + +private: +    // Generate static implementations for comparison operations for IntrusiveRedBlackTreeRoot. +    RB_GENERATE_WITH_COMPARE_STATIC(IntrusiveRedBlackTreeRootWithCompare, IntrusiveRedBlackTreeNode, +                                    entry, CompareImpl, LightCompareImpl); + +private: +    static int CompareImpl(const IntrusiveRedBlackTreeNode* lhs, +                           const IntrusiveRedBlackTreeNode* rhs) { +        return Comparator::Compare(*Traits::GetParent(lhs), *Traits::GetParent(rhs)); +    } + +    static int LightCompareImpl(const void* elm, const IntrusiveRedBlackTreeNode* rhs) { +        return Comparator::Compare(*static_cast<const_light_pointer>(elm), *Traits::GetParent(rhs)); +    } + +    // Define accessors using RB_* functions. +    IntrusiveRedBlackTreeNode* InsertImpl(IntrusiveRedBlackTreeNode* node) { +        return RB_INSERT(IntrusiveRedBlackTreeRootWithCompare, +                         static_cast<IntrusiveRedBlackTreeRootWithCompare*>(&this->impl.root), +                         node); +    } + +    IntrusiveRedBlackTreeNode* FindImpl(const IntrusiveRedBlackTreeNode* node) const { +        return RB_FIND( +            IntrusiveRedBlackTreeRootWithCompare, +            const_cast<IntrusiveRedBlackTreeRootWithCompare*>( +                static_cast<const IntrusiveRedBlackTreeRootWithCompare*>(&this->impl.root)), +            const_cast<IntrusiveRedBlackTreeNode*>(node)); +    } + +    IntrusiveRedBlackTreeNode* NFindImpl(const IntrusiveRedBlackTreeNode* node) const { +        return RB_NFIND( +            IntrusiveRedBlackTreeRootWithCompare, +            const_cast<IntrusiveRedBlackTreeRootWithCompare*>( +                static_cast<const IntrusiveRedBlackTreeRootWithCompare*>(&this->impl.root)), +            const_cast<IntrusiveRedBlackTreeNode*>(node)); +    } + +    IntrusiveRedBlackTreeNode* FindLightImpl(const_light_pointer lelm) const { +        return RB_FIND_LIGHT( +            IntrusiveRedBlackTreeRootWithCompare, +            const_cast<IntrusiveRedBlackTreeRootWithCompare*>( +                static_cast<const IntrusiveRedBlackTreeRootWithCompare*>(&this->impl.root)), +            static_cast<const void*>(lelm)); +    } + +    IntrusiveRedBlackTreeNode* NFindLightImpl(const_light_pointer lelm) const { +        return RB_NFIND_LIGHT( +            IntrusiveRedBlackTreeRootWithCompare, +            const_cast<IntrusiveRedBlackTreeRootWithCompare*>( +                static_cast<const IntrusiveRedBlackTreeRootWithCompare*>(&this->impl.root)), +            static_cast<const void*>(lelm)); +    } + +public: +    constexpr IntrusiveRedBlackTree() = default; + +    // Iterator accessors. +    iterator begin() { +        return iterator(this->impl.begin()); +    } + +    const_iterator begin() const { +        return const_iterator(this->impl.begin()); +    } + +    iterator end() { +        return iterator(this->impl.end()); +    } + +    const_iterator end() const { +        return const_iterator(this->impl.end()); +    } + +    const_iterator cbegin() const { +        return this->begin(); +    } + +    const_iterator cend() const { +        return this->end(); +    } + +    iterator iterator_to(reference ref) { +        return iterator(this->impl.iterator_to(*Traits::GetNode(std::addressof(ref)))); +    } + +    const_iterator iterator_to(const_reference ref) const { +        return const_iterator(this->impl.iterator_to(*Traits::GetNode(std::addressof(ref)))); +    } + +    // Content management. +    bool empty() const { +        return this->impl.empty(); +    } + +    reference back() { +        return *Traits::GetParent(std::addressof(this->impl.back())); +    } + +    const_reference back() const { +        return *Traits::GetParent(std::addressof(this->impl.back())); +    } + +    reference front() { +        return *Traits::GetParent(std::addressof(this->impl.front())); +    } + +    const_reference front() const { +        return *Traits::GetParent(std::addressof(this->impl.front())); +    } + +    iterator erase(iterator it) { +        return iterator(this->impl.erase(it.GetImplIterator())); +    } + +    iterator insert(reference ref) { +        ImplType::pointer node = Traits::GetNode(std::addressof(ref)); +        this->InsertImpl(node); +        return iterator(node); +    } + +    iterator find(const_reference ref) const { +        return iterator(this->FindImpl(Traits::GetNode(std::addressof(ref)))); +    } + +    iterator nfind(const_reference ref) const { +        return iterator(this->NFindImpl(Traits::GetNode(std::addressof(ref)))); +    } + +    iterator find_light(const_light_reference ref) const { +        return iterator(this->FindLightImpl(std::addressof(ref))); +    } + +    iterator nfind_light(const_light_reference ref) const { +        return iterator(this->NFindLightImpl(std::addressof(ref))); +    } +}; + +template <auto T, class Derived = impl::GetParentType<T>> +class IntrusiveRedBlackTreeMemberTraits; + +template <class Parent, IntrusiveRedBlackTreeNode Parent::*Member, class Derived> +class IntrusiveRedBlackTreeMemberTraits<Member, Derived> { +public: +    template <class Comparator> +    using TreeType = IntrusiveRedBlackTree<Derived, IntrusiveRedBlackTreeMemberTraits, Comparator>; +    using TreeTypeImpl = impl::IntrusiveRedBlackTreeImpl; + +private: +    template <class, class, class> +    friend class IntrusiveRedBlackTree; + +    friend class impl::IntrusiveRedBlackTreeImpl; + +    static constexpr IntrusiveRedBlackTreeNode* GetNode(Derived* parent) { +        return std::addressof(parent->*Member); +    } + +    static constexpr IntrusiveRedBlackTreeNode const* GetNode(Derived const* parent) { +        return std::addressof(parent->*Member); +    } + +    static constexpr Derived* GetParent(IntrusiveRedBlackTreeNode* node) { +        return GetParentPointer<Member, Derived>(node); +    } + +    static constexpr Derived const* GetParent(const IntrusiveRedBlackTreeNode* node) { +        return GetParentPointer<Member, Derived>(node); +    } + +private: +    static constexpr TypedStorage<Derived> DerivedStorage = {}; +    static_assert(GetParent(GetNode(GetPointer(DerivedStorage))) == GetPointer(DerivedStorage)); +}; + +template <auto T, class Derived = impl::GetParentType<T>> +class IntrusiveRedBlackTreeMemberTraitsDeferredAssert; + +template <class Parent, IntrusiveRedBlackTreeNode Parent::*Member, class Derived> +class IntrusiveRedBlackTreeMemberTraitsDeferredAssert<Member, Derived> { +public: +    template <class Comparator> +    using TreeType = +        IntrusiveRedBlackTree<Derived, IntrusiveRedBlackTreeMemberTraitsDeferredAssert, Comparator>; +    using TreeTypeImpl = impl::IntrusiveRedBlackTreeImpl; + +    static constexpr bool IsValid() { +        TypedStorage<Derived> DerivedStorage = {}; +        return GetParent(GetNode(GetPointer(DerivedStorage))) == GetPointer(DerivedStorage); +    } + +private: +    template <class, class, class> +    friend class IntrusiveRedBlackTree; + +    friend class impl::IntrusiveRedBlackTreeImpl; + +    static constexpr IntrusiveRedBlackTreeNode* GetNode(Derived* parent) { +        return std::addressof(parent->*Member); +    } + +    static constexpr IntrusiveRedBlackTreeNode const* GetNode(Derived const* parent) { +        return std::addressof(parent->*Member); +    } + +    static constexpr Derived* GetParent(IntrusiveRedBlackTreeNode* node) { +        return GetParentPointer<Member, Derived>(node); +    } + +    static constexpr Derived const* GetParent(const IntrusiveRedBlackTreeNode* node) { +        return GetParentPointer<Member, Derived>(node); +    } +}; + +template <class Derived> +class IntrusiveRedBlackTreeBaseNode : public IntrusiveRedBlackTreeNode { +public: +    constexpr Derived* GetPrev() { +        return static_cast<Derived*>(impl::IntrusiveRedBlackTreeImpl::GetPrev(this)); +    } +    constexpr const Derived* GetPrev() const { +        return static_cast<const Derived*>(impl::IntrusiveRedBlackTreeImpl::GetPrev(this)); +    } + +    constexpr Derived* GetNext() { +        return static_cast<Derived*>(impl::IntrusiveRedBlackTreeImpl::GetNext(this)); +    } +    constexpr const Derived* GetNext() const { +        return static_cast<const Derived*>(impl::IntrusiveRedBlackTreeImpl::GetNext(this)); +    } +}; + +template <class Derived> +class IntrusiveRedBlackTreeBaseTraits { +public: +    template <class Comparator> +    using TreeType = IntrusiveRedBlackTree<Derived, IntrusiveRedBlackTreeBaseTraits, Comparator>; +    using TreeTypeImpl = impl::IntrusiveRedBlackTreeImpl; + +private: +    template <class, class, class> +    friend class IntrusiveRedBlackTree; + +    friend class impl::IntrusiveRedBlackTreeImpl; + +    static constexpr IntrusiveRedBlackTreeNode* GetNode(Derived* parent) { +        return static_cast<IntrusiveRedBlackTreeNode*>(parent); +    } + +    static constexpr IntrusiveRedBlackTreeNode const* GetNode(Derived const* parent) { +        return static_cast<const IntrusiveRedBlackTreeNode*>(parent); +    } + +    static constexpr Derived* GetParent(IntrusiveRedBlackTreeNode* node) { +        return static_cast<Derived*>(node); +    } + +    static constexpr Derived const* GetParent(const IntrusiveRedBlackTreeNode* node) { +        return static_cast<const Derived*>(node); +    } +}; + +} // namespace Common diff --git a/src/common/parent_of_member.h b/src/common/parent_of_member.h new file mode 100644 index 000000000..d9a14529d --- /dev/null +++ b/src/common/parent_of_member.h @@ -0,0 +1,191 @@ +// Copyright 2021 yuzu Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include <type_traits> + +#include "common/assert.h" +#include "common/common_types.h" + +namespace Common { +namespace detail { +template <typename T, size_t Size, size_t Align> +struct TypedStorageImpl { +    std::aligned_storage_t<Size, Align> storage_; +}; +} // namespace detail + +template <typename T> +using TypedStorage = detail::TypedStorageImpl<T, sizeof(T), alignof(T)>; + +template <typename T> +static constexpr T* GetPointer(TypedStorage<T>& ts) { +    return static_cast<T*>(static_cast<void*>(std::addressof(ts.storage_))); +} + +template <typename T> +static constexpr const T* GetPointer(const TypedStorage<T>& ts) { +    return static_cast<const T*>(static_cast<const void*>(std::addressof(ts.storage_))); +} + +namespace impl { + +template <size_t MaxDepth> +struct OffsetOfUnionHolder { +    template <typename ParentType, typename MemberType, size_t Offset> +    union UnionImpl { +        using PaddingMember = char; +        static constexpr size_t GetOffset() { +            return Offset; +        } + +#pragma pack(push, 1) +        struct { +            PaddingMember padding[Offset]; +            MemberType members[(sizeof(ParentType) / sizeof(MemberType)) + 1]; +        } data; +#pragma pack(pop) +        UnionImpl<ParentType, MemberType, Offset + 1> next_union; +    }; + +    template <typename ParentType, typename MemberType> +    union UnionImpl<ParentType, MemberType, 0> { +        static constexpr size_t GetOffset() { +            return 0; +        } + +        struct { +            MemberType members[(sizeof(ParentType) / sizeof(MemberType)) + 1]; +        } data; +        UnionImpl<ParentType, MemberType, 1> next_union; +    }; + +    template <typename ParentType, typename MemberType> +    union UnionImpl<ParentType, MemberType, MaxDepth> {}; +}; + +template <typename ParentType, typename MemberType> +struct OffsetOfCalculator { +    using UnionHolder = +        typename OffsetOfUnionHolder<sizeof(MemberType)>::template UnionImpl<ParentType, MemberType, +                                                                             0>; +    union Union { +        char c{}; +        UnionHolder first_union; +        TypedStorage<ParentType> parent; + +        constexpr Union() : c() {} +    }; +    static constexpr Union U = {}; + +    static constexpr const MemberType* GetNextAddress(const MemberType* start, +                                                      const MemberType* target) { +        while (start < target) { +            start++; +        } +        return start; +    } + +    static constexpr std::ptrdiff_t GetDifference(const MemberType* start, +                                                  const MemberType* target) { +        return (target - start) * sizeof(MemberType); +    } + +    template <typename CurUnion> +    static constexpr std::ptrdiff_t OffsetOfImpl(MemberType ParentType::*member, +                                                 CurUnion& cur_union) { +        constexpr size_t Offset = CurUnion::GetOffset(); +        const auto target = std::addressof(GetPointer(U.parent)->*member); +        const auto start = std::addressof(cur_union.data.members[0]); +        const auto next = GetNextAddress(start, target); + +        if (next != target) { +            if constexpr (Offset < sizeof(MemberType) - 1) { +                return OffsetOfImpl(member, cur_union.next_union); +            } else { +                UNREACHABLE(); +            } +        } + +        return (next - start) * sizeof(MemberType) + Offset; +    } + +    static constexpr std::ptrdiff_t OffsetOf(MemberType ParentType::*member) { +        return OffsetOfImpl(member, U.first_union); +    } +}; + +template <typename T> +struct GetMemberPointerTraits; + +template <typename P, typename M> +struct GetMemberPointerTraits<M P::*> { +    using Parent = P; +    using Member = M; +}; + +template <auto MemberPtr> +using GetParentType = typename GetMemberPointerTraits<decltype(MemberPtr)>::Parent; + +template <auto MemberPtr> +using GetMemberType = typename GetMemberPointerTraits<decltype(MemberPtr)>::Member; + +template <auto MemberPtr, typename RealParentType = GetParentType<MemberPtr>> +static inline std::ptrdiff_t OffsetOf = [] { +    using DeducedParentType = GetParentType<MemberPtr>; +    using MemberType = GetMemberType<MemberPtr>; +    static_assert(std::is_base_of<DeducedParentType, RealParentType>::value || +                  std::is_same<RealParentType, DeducedParentType>::value); + +    return OffsetOfCalculator<RealParentType, MemberType>::OffsetOf(MemberPtr); +}(); + +} // namespace impl + +template <auto MemberPtr, typename RealParentType = impl::GetParentType<MemberPtr>> +constexpr RealParentType& GetParentReference(impl::GetMemberType<MemberPtr>* member) { +    std::ptrdiff_t Offset = impl::OffsetOf<MemberPtr, RealParentType>; +    return *static_cast<RealParentType*>( +        static_cast<void*>(static_cast<uint8_t*>(static_cast<void*>(member)) - Offset)); +} + +template <auto MemberPtr, typename RealParentType = impl::GetParentType<MemberPtr>> +constexpr RealParentType const& GetParentReference(impl::GetMemberType<MemberPtr> const* member) { +    std::ptrdiff_t Offset = impl::OffsetOf<MemberPtr, RealParentType>; +    return *static_cast<const RealParentType*>(static_cast<const void*>( +        static_cast<const uint8_t*>(static_cast<const void*>(member)) - Offset)); +} + +template <auto MemberPtr, typename RealParentType = impl::GetParentType<MemberPtr>> +constexpr RealParentType* GetParentPointer(impl::GetMemberType<MemberPtr>* member) { +    return std::addressof(GetParentReference<MemberPtr, RealParentType>(member)); +} + +template <auto MemberPtr, typename RealParentType = impl::GetParentType<MemberPtr>> +constexpr RealParentType const* GetParentPointer(impl::GetMemberType<MemberPtr> const* member) { +    return std::addressof(GetParentReference<MemberPtr, RealParentType>(member)); +} + +template <auto MemberPtr, typename RealParentType = impl::GetParentType<MemberPtr>> +constexpr RealParentType& GetParentReference(impl::GetMemberType<MemberPtr>& member) { +    return GetParentReference<MemberPtr, RealParentType>(std::addressof(member)); +} + +template <auto MemberPtr, typename RealParentType = impl::GetParentType<MemberPtr>> +constexpr RealParentType const& GetParentReference(impl::GetMemberType<MemberPtr> const& member) { +    return GetParentReference<MemberPtr, RealParentType>(std::addressof(member)); +} + +template <auto MemberPtr, typename RealParentType = impl::GetParentType<MemberPtr>> +constexpr RealParentType* GetParentPointer(impl::GetMemberType<MemberPtr>& member) { +    return std::addressof(GetParentReference<MemberPtr, RealParentType>(member)); +} + +template <auto MemberPtr, typename RealParentType = impl::GetParentType<MemberPtr>> +constexpr RealParentType const* GetParentPointer(impl::GetMemberType<MemberPtr> const& member) { +    return std::addressof(GetParentReference<MemberPtr, RealParentType>(member)); +} + +} // namespace Common diff --git a/src/common/tree.h b/src/common/tree.h new file mode 100644 index 000000000..a6b636646 --- /dev/null +++ b/src/common/tree.h @@ -0,0 +1,822 @@ +/* $NetBSD: tree.h,v 1.8 2004/03/28 19:38:30 provos Exp $ */ +/* $OpenBSD: tree.h,v 1.7 2002/10/17 21:51:54 art Exp $ */ +/* $FreeBSD$ */ + +/*- + * Copyright 2002 Niels Provos <provos@citi.umich.edu> + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + *    notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + *    notice, this list of conditions and the following disclaimer in the + *    documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _SYS_TREE_H_ +#define _SYS_TREE_H_ + +/* FreeBSD <sys/cdefs.h> has a lot of defines we don't really want. */ +/* tree.h only actually uses __inline and __unused, so we'll just define those. */ + +/* #include <sys/cdefs.h> */ + +#ifndef __inline +#define __inline inline +#endif + +/* + * This file defines data structures for different types of trees: + * splay trees and red-black trees. + * + * A splay tree is a self-organizing data structure.  Every operation + * on the tree causes a splay to happen.  The splay moves the requested + * node to the root of the tree and partly rebalances it. + * + * This has the benefit that request locality causes faster lookups as + * the requested nodes move to the top of the tree.  On the other hand, + * every lookup causes memory writes. + * + * The Balance Theorem bounds the total access time for m operations + * and n inserts on an initially empty tree as O((m + n)lg n).  The + * amortized cost for a sequence of m accesses to a splay tree is O(lg n); + * + * A red-black tree is a binary search tree with the node color as an + * extra attribute.  It fulfills a set of conditions: + * - every search path from the root to a leaf consists of the + *   same number of black nodes, + * - each red node (except for the root) has a black parent, + * - each leaf node is black. + * + * Every operation on a red-black tree is bounded as O(lg n). + * The maximum height of a red-black tree is 2lg (n+1). + */ + +#define SPLAY_HEAD(name, type)                                                                     \ +    struct name {                                                                                  \ +        struct type* sph_root; /* root of the tree */                                              \ +    } + +#define SPLAY_INITIALIZER(root)                                                                    \ +    { NULL } + +#define SPLAY_INIT(root)                                                                           \ +    do {                                                                                           \ +        (root)->sph_root = NULL;                                                                   \ +    } while (/*CONSTCOND*/ 0) + +#define SPLAY_ENTRY(type)                                                                          \ +    struct {                                                                                       \ +        struct type* spe_left;  /* left element */                                                 \ +        struct type* spe_right; /* right element */                                                \ +    } + +#define SPLAY_LEFT(elm, field) (elm)->field.spe_left +#define SPLAY_RIGHT(elm, field) (elm)->field.spe_right +#define SPLAY_ROOT(head) (head)->sph_root +#define SPLAY_EMPTY(head) (SPLAY_ROOT(head) == NULL) + +/* SPLAY_ROTATE_{LEFT,RIGHT} expect that tmp hold SPLAY_{RIGHT,LEFT} */ +#define SPLAY_ROTATE_RIGHT(head, tmp, field)                                                       \ +    do {                                                                                           \ +        SPLAY_LEFT((head)->sph_root, field) = SPLAY_RIGHT(tmp, field);                             \ +        SPLAY_RIGHT(tmp, field) = (head)->sph_root;                                                \ +        (head)->sph_root = tmp;                                                                    \ +    } while (/*CONSTCOND*/ 0) + +#define SPLAY_ROTATE_LEFT(head, tmp, field)                                                        \ +    do {                                                                                           \ +        SPLAY_RIGHT((head)->sph_root, field) = SPLAY_LEFT(tmp, field);                             \ +        SPLAY_LEFT(tmp, field) = (head)->sph_root;                                                 \ +        (head)->sph_root = tmp;                                                                    \ +    } while (/*CONSTCOND*/ 0) + +#define SPLAY_LINKLEFT(head, tmp, field)                                                           \ +    do {                                                                                           \ +        SPLAY_LEFT(tmp, field) = (head)->sph_root;                                                 \ +        tmp = (head)->sph_root;                                                                    \ +        (head)->sph_root = SPLAY_LEFT((head)->sph_root, field);                                    \ +    } while (/*CONSTCOND*/ 0) + +#define SPLAY_LINKRIGHT(head, tmp, field)                                                          \ +    do {                                                                                           \ +        SPLAY_RIGHT(tmp, field) = (head)->sph_root;                                                \ +        tmp = (head)->sph_root;                                                                    \ +        (head)->sph_root = SPLAY_RIGHT((head)->sph_root, field);                                   \ +    } while (/*CONSTCOND*/ 0) + +#define SPLAY_ASSEMBLE(head, node, left, right, field)                                             \ +    do {                                                                                           \ +        SPLAY_RIGHT(left, field) = SPLAY_LEFT((head)->sph_root, field);                            \ +        SPLAY_LEFT(right, field) = SPLAY_RIGHT((head)->sph_root, field);                           \ +        SPLAY_LEFT((head)->sph_root, field) = SPLAY_RIGHT(node, field);                            \ +        SPLAY_RIGHT((head)->sph_root, field) = SPLAY_LEFT(node, field);                            \ +    } while (/*CONSTCOND*/ 0) + +/* Generates prototypes and inline functions */ + +#define SPLAY_PROTOTYPE(name, type, field, cmp)                                                    \ +    void name##_SPLAY(struct name*, struct type*);                                                 \ +    void name##_SPLAY_MINMAX(struct name*, int);                                                   \ +    struct type* name##_SPLAY_INSERT(struct name*, struct type*);                                  \ +    struct type* name##_SPLAY_REMOVE(struct name*, struct type*);                                  \ +                                                                                                   \ +    /* Finds the node with the same key as elm */                                                  \ +    static __inline struct type* name##_SPLAY_FIND(struct name* head, struct type* elm) {          \ +        if (SPLAY_EMPTY(head))                                                                     \ +            return (NULL);                                                                         \ +        name##_SPLAY(head, elm);                                                                   \ +        if ((cmp)(elm, (head)->sph_root) == 0)                                                     \ +            return (head->sph_root);                                                               \ +        return (NULL);                                                                             \ +    }                                                                                              \ +                                                                                                   \ +    static __inline struct type* name##_SPLAY_NEXT(struct name* head, struct type* elm) {          \ +        name##_SPLAY(head, elm);                                                                   \ +        if (SPLAY_RIGHT(elm, field) != NULL) {                                                     \ +            elm = SPLAY_RIGHT(elm, field);                                                         \ +            while (SPLAY_LEFT(elm, field) != NULL) {                                               \ +                elm = SPLAY_LEFT(elm, field);                                                      \ +            }                                                                                      \ +        } else                                                                                     \ +            elm = NULL;                                                                            \ +        return (elm);                                                                              \ +    }                                                                                              \ +                                                                                                   \ +    static __inline struct type* name##_SPLAY_MIN_MAX(struct name* head, int val) {                \ +        name##_SPLAY_MINMAX(head, val);                                                            \ +        return (SPLAY_ROOT(head));                                                                 \ +    } + +/* Main splay operation. + * Moves node close to the key of elm to top + */ +#define SPLAY_GENERATE(name, type, field, cmp)                                                     \ +    struct type* name##_SPLAY_INSERT(struct name* head, struct type* elm) {                        \ +        if (SPLAY_EMPTY(head)) {                                                                   \ +            SPLAY_LEFT(elm, field) = SPLAY_RIGHT(elm, field) = NULL;                               \ +        } else {                                                                                   \ +            int __comp;                                                                            \ +            name##_SPLAY(head, elm);                                                               \ +            __comp = (cmp)(elm, (head)->sph_root);                                                 \ +            if (__comp < 0) {                                                                      \ +                SPLAY_LEFT(elm, field) = SPLAY_LEFT((head)->sph_root, field);                      \ +                SPLAY_RIGHT(elm, field) = (head)->sph_root;                                        \ +                SPLAY_LEFT((head)->sph_root, field) = NULL;                                        \ +            } else if (__comp > 0) {                                                               \ +                SPLAY_RIGHT(elm, field) = SPLAY_RIGHT((head)->sph_root, field);                    \ +                SPLAY_LEFT(elm, field) = (head)->sph_root;                                         \ +                SPLAY_RIGHT((head)->sph_root, field) = NULL;                                       \ +            } else                                                                                 \ +                return ((head)->sph_root);                                                         \ +        }                                                                                          \ +        (head)->sph_root = (elm);                                                                  \ +        return (NULL);                                                                             \ +    }                                                                                              \ +                                                                                                   \ +    struct type* name##_SPLAY_REMOVE(struct name* head, struct type* elm) {                        \ +        struct type* __tmp;                                                                        \ +        if (SPLAY_EMPTY(head))                                                                     \ +            return (NULL);                                                                         \ +        name##_SPLAY(head, elm);                                                                   \ +        if ((cmp)(elm, (head)->sph_root) == 0) {                                                   \ +            if (SPLAY_LEFT((head)->sph_root, field) == NULL) {                                     \ +                (head)->sph_root = SPLAY_RIGHT((head)->sph_root, field);                           \ +            } else {                                                                               \ +                __tmp = SPLAY_RIGHT((head)->sph_root, field);                                      \ +                (head)->sph_root = SPLAY_LEFT((head)->sph_root, field);                            \ +                name##_SPLAY(head, elm);                                                           \ +                SPLAY_RIGHT((head)->sph_root, field) = __tmp;                                      \ +            }                                                                                      \ +            return (elm);                                                                          \ +        }                                                                                          \ +        return (NULL);                                                                             \ +    }                                                                                              \ +                                                                                                   \ +    void name##_SPLAY(struct name* head, struct type* elm) {                                       \ +        struct type __node, *__left, *__right, *__tmp;                                             \ +        int __comp;                                                                                \ +                                                                                                   \ +        SPLAY_LEFT(&__node, field) = SPLAY_RIGHT(&__node, field) = NULL;                           \ +        __left = __right = &__node;                                                                \ +                                                                                                   \ +        while ((__comp = (cmp)(elm, (head)->sph_root)) != 0) {                                     \ +            if (__comp < 0) {                                                                      \ +                __tmp = SPLAY_LEFT((head)->sph_root, field);                                       \ +                if (__tmp == NULL)                                                                 \ +                    break;                                                                         \ +                if ((cmp)(elm, __tmp) < 0) {                                                       \ +                    SPLAY_ROTATE_RIGHT(head, __tmp, field);                                        \ +                    if (SPLAY_LEFT((head)->sph_root, field) == NULL)                               \ +                        break;                                                                     \ +                }                                                                                  \ +                SPLAY_LINKLEFT(head, __right, field);                                              \ +            } else if (__comp > 0) {                                                               \ +                __tmp = SPLAY_RIGHT((head)->sph_root, field);                                      \ +                if (__tmp == NULL)                                                                 \ +                    break;                                                                         \ +                if ((cmp)(elm, __tmp) > 0) {                                                       \ +                    SPLAY_ROTATE_LEFT(head, __tmp, field);                                         \ +                    if (SPLAY_RIGHT((head)->sph_root, field) == NULL)                              \ +                        break;                                                                     \ +                }                                                                                  \ +                SPLAY_LINKRIGHT(head, __left, field);                                              \ +            }                                                                                      \ +        }                                                                                          \ +        SPLAY_ASSEMBLE(head, &__node, __left, __right, field);                                     \ +    }                                                                                              \ +                                                                                                   \ +    /* Splay with either the minimum or the maximum element                                        \ +     * Used to find minimum or maximum element in tree.                                            \ +     */                                                                                            \ +    void name##_SPLAY_MINMAX(struct name* head, int __comp) {                                      \ +        struct type __node, *__left, *__right, *__tmp;                                             \ +                                                                                                   \ +        SPLAY_LEFT(&__node, field) = SPLAY_RIGHT(&__node, field) = NULL;                           \ +        __left = __right = &__node;                                                                \ +                                                                                                   \ +        while (1) {                                                                                \ +            if (__comp < 0) {                                                                      \ +                __tmp = SPLAY_LEFT((head)->sph_root, field);                                       \ +                if (__tmp == NULL)                                                                 \ +                    break;                                                                         \ +                if (__comp < 0) {                                                                  \ +                    SPLAY_ROTATE_RIGHT(head, __tmp, field);                                        \ +                    if (SPLAY_LEFT((head)->sph_root, field) == NULL)                               \ +                        break;                                                                     \ +                }                                                                                  \ +                SPLAY_LINKLEFT(head, __right, field);                                              \ +            } else if (__comp > 0) {                                                               \ +                __tmp = SPLAY_RIGHT((head)->sph_root, field);                                      \ +                if (__tmp == NULL)                                                                 \ +                    break;                                                                         \ +                if (__comp > 0) {                                                                  \ +                    SPLAY_ROTATE_LEFT(head, __tmp, field);                                         \ +                    if (SPLAY_RIGHT((head)->sph_root, field) == NULL)                              \ +                        break;                                                                     \ +                }                                                                                  \ +                SPLAY_LINKRIGHT(head, __left, field);                                              \ +            }                                                                                      \ +        }                                                                                          \ +        SPLAY_ASSEMBLE(head, &__node, __left, __right, field);                                     \ +    } + +#define SPLAY_NEGINF -1 +#define SPLAY_INF 1 + +#define SPLAY_INSERT(name, x, y) name##_SPLAY_INSERT(x, y) +#define SPLAY_REMOVE(name, x, y) name##_SPLAY_REMOVE(x, y) +#define SPLAY_FIND(name, x, y) name##_SPLAY_FIND(x, y) +#define SPLAY_NEXT(name, x, y) name##_SPLAY_NEXT(x, y) +#define SPLAY_MIN(name, x) (SPLAY_EMPTY(x) ? NULL : name##_SPLAY_MIN_MAX(x, SPLAY_NEGINF)) +#define SPLAY_MAX(name, x) (SPLAY_EMPTY(x) ? NULL : name##_SPLAY_MIN_MAX(x, SPLAY_INF)) + +#define SPLAY_FOREACH(x, name, head)                                                               \ +    for ((x) = SPLAY_MIN(name, head); (x) != NULL; (x) = SPLAY_NEXT(name, head, x)) + +/* Macros that define a red-black tree */ +#define RB_HEAD(name, type)                                                                        \ +    struct name {                                                                                  \ +        struct type* rbh_root; /* root of the tree */                                              \ +    } + +#define RB_INITIALIZER(root)                                                                       \ +    { NULL } + +#define RB_INIT(root)                                                                              \ +    do {                                                                                           \ +        (root)->rbh_root = NULL;                                                                   \ +    } while (/*CONSTCOND*/ 0) + +#define RB_BLACK 0 +#define RB_RED 1 +#define RB_ENTRY(type)                                                                             \ +    struct {                                                                                       \ +        struct type* rbe_left;   /* left element */                                                \ +        struct type* rbe_right;  /* right element */                                               \ +        struct type* rbe_parent; /* parent element */                                              \ +        int rbe_color;           /* node color */                                                  \ +    } + +#define RB_LEFT(elm, field) (elm)->field.rbe_left +#define RB_RIGHT(elm, field) (elm)->field.rbe_right +#define RB_PARENT(elm, field) (elm)->field.rbe_parent +#define RB_COLOR(elm, field) (elm)->field.rbe_color +#define RB_ROOT(head) (head)->rbh_root +#define RB_EMPTY(head) (RB_ROOT(head) == NULL) + +#define RB_SET(elm, parent, field)                                                                 \ +    do {                                                                                           \ +        RB_PARENT(elm, field) = parent;                                                            \ +        RB_LEFT(elm, field) = RB_RIGHT(elm, field) = NULL;                                         \ +        RB_COLOR(elm, field) = RB_RED;                                                             \ +    } while (/*CONSTCOND*/ 0) + +#define RB_SET_BLACKRED(black, red, field)                                                         \ +    do {                                                                                           \ +        RB_COLOR(black, field) = RB_BLACK;                                                         \ +        RB_COLOR(red, field) = RB_RED;                                                             \ +    } while (/*CONSTCOND*/ 0) + +#ifndef RB_AUGMENT +#define RB_AUGMENT(x)                                                                              \ +    do {                                                                                           \ +    } while (0) +#endif + +#define RB_ROTATE_LEFT(head, elm, tmp, field)                                                      \ +    do {                                                                                           \ +        (tmp) = RB_RIGHT(elm, field);                                                              \ +        if ((RB_RIGHT(elm, field) = RB_LEFT(tmp, field)) != NULL) {                                \ +            RB_PARENT(RB_LEFT(tmp, field), field) = (elm);                                         \ +        }                                                                                          \ +        RB_AUGMENT(elm);                                                                           \ +        if ((RB_PARENT(tmp, field) = RB_PARENT(elm, field)) != NULL) {                             \ +            if ((elm) == RB_LEFT(RB_PARENT(elm, field), field))                                    \ +                RB_LEFT(RB_PARENT(elm, field), field) = (tmp);                                     \ +            else                                                                                   \ +                RB_RIGHT(RB_PARENT(elm, field), field) = (tmp);                                    \ +        } else                                                                                     \ +            (head)->rbh_root = (tmp);                                                              \ +        RB_LEFT(tmp, field) = (elm);                                                               \ +        RB_PARENT(elm, field) = (tmp);                                                             \ +        RB_AUGMENT(tmp);                                                                           \ +        if ((RB_PARENT(tmp, field)))                                                               \ +            RB_AUGMENT(RB_PARENT(tmp, field));                                                     \ +    } while (/*CONSTCOND*/ 0) + +#define RB_ROTATE_RIGHT(head, elm, tmp, field)                                                     \ +    do {                                                                                           \ +        (tmp) = RB_LEFT(elm, field);                                                               \ +        if ((RB_LEFT(elm, field) = RB_RIGHT(tmp, field)) != NULL) {                                \ +            RB_PARENT(RB_RIGHT(tmp, field), field) = (elm);                                        \ +        }                                                                                          \ +        RB_AUGMENT(elm);                                                                           \ +        if ((RB_PARENT(tmp, field) = RB_PARENT(elm, field)) != NULL) {                             \ +            if ((elm) == RB_LEFT(RB_PARENT(elm, field), field))                                    \ +                RB_LEFT(RB_PARENT(elm, field), field) = (tmp);                                     \ +            else                                                                                   \ +                RB_RIGHT(RB_PARENT(elm, field), field) = (tmp);                                    \ +        } else                                                                                     \ +            (head)->rbh_root = (tmp);                                                              \ +        RB_RIGHT(tmp, field) = (elm);                                                              \ +        RB_PARENT(elm, field) = (tmp);                                                             \ +        RB_AUGMENT(tmp);                                                                           \ +        if ((RB_PARENT(tmp, field)))                                                               \ +            RB_AUGMENT(RB_PARENT(tmp, field));                                                     \ +    } while (/*CONSTCOND*/ 0) + +/* Generates prototypes and inline functions */ +#define RB_PROTOTYPE(name, type, field, cmp) RB_PROTOTYPE_INTERNAL(name, type, field, cmp, ) +#define RB_PROTOTYPE_STATIC(name, type, field, cmp)                                                \ +    RB_PROTOTYPE_INTERNAL(name, type, field, cmp, static) +#define RB_PROTOTYPE_INTERNAL(name, type, field, cmp, attr)                                        \ +    RB_PROTOTYPE_INSERT_COLOR(name, type, attr);                                                   \ +    RB_PROTOTYPE_REMOVE_COLOR(name, type, attr);                                                   \ +    RB_PROTOTYPE_INSERT(name, type, attr);                                                         \ +    RB_PROTOTYPE_REMOVE(name, type, attr);                                                         \ +    RB_PROTOTYPE_FIND(name, type, attr);                                                           \ +    RB_PROTOTYPE_NFIND(name, type, attr);                                                          \ +    RB_PROTOTYPE_FIND_LIGHT(name, type, attr);                                                     \ +    RB_PROTOTYPE_NFIND_LIGHT(name, type, attr);                                                    \ +    RB_PROTOTYPE_NEXT(name, type, attr);                                                           \ +    RB_PROTOTYPE_PREV(name, type, attr);                                                           \ +    RB_PROTOTYPE_MINMAX(name, type, attr); +#define RB_PROTOTYPE_INSERT_COLOR(name, type, attr)                                                \ +    attr void name##_RB_INSERT_COLOR(struct name*, struct type*) +#define RB_PROTOTYPE_REMOVE_COLOR(name, type, attr)                                                \ +    attr void name##_RB_REMOVE_COLOR(struct name*, struct type*, struct type*) +#define RB_PROTOTYPE_REMOVE(name, type, attr)                                                      \ +    attr struct type* name##_RB_REMOVE(struct name*, struct type*) +#define RB_PROTOTYPE_INSERT(name, type, attr)                                                      \ +    attr struct type* name##_RB_INSERT(struct name*, struct type*) +#define RB_PROTOTYPE_FIND(name, type, attr)                                                        \ +    attr struct type* name##_RB_FIND(struct name*, struct type*) +#define RB_PROTOTYPE_NFIND(name, type, attr)                                                       \ +    attr struct type* name##_RB_NFIND(struct name*, struct type*) +#define RB_PROTOTYPE_FIND_LIGHT(name, type, attr)                                                  \ +    attr struct type* name##_RB_FIND_LIGHT(struct name*, const void*) +#define RB_PROTOTYPE_NFIND_LIGHT(name, type, attr)                                                 \ +    attr struct type* name##_RB_NFIND_LIGHT(struct name*, const void*) +#define RB_PROTOTYPE_NEXT(name, type, attr) attr struct type* name##_RB_NEXT(struct type*) +#define RB_PROTOTYPE_PREV(name, type, attr) attr struct type* name##_RB_PREV(struct type*) +#define RB_PROTOTYPE_MINMAX(name, type, attr) attr struct type* name##_RB_MINMAX(struct name*, int) + +/* Main rb operation. + * Moves node close to the key of elm to top + */ +#define RB_GENERATE_WITHOUT_COMPARE(name, type, field)                                             \ +    RB_GENERATE_WITHOUT_COMPARE_INTERNAL(name, type, field, ) +#define RB_GENERATE_WITHOUT_COMPARE_STATIC(name, type, field)                                      \ +    RB_GENERATE_WITHOUT_COMPARE_INTERNAL(name, type, field, static) +#define RB_GENERATE_WITHOUT_COMPARE_INTERNAL(name, type, field, attr)                              \ +    RB_GENERATE_REMOVE_COLOR(name, type, field, attr)                                              \ +    RB_GENERATE_REMOVE(name, type, field, attr)                                                    \ +    RB_GENERATE_NEXT(name, type, field, attr)                                                      \ +    RB_GENERATE_PREV(name, type, field, attr)                                                      \ +    RB_GENERATE_MINMAX(name, type, field, attr) + +#define RB_GENERATE_WITH_COMPARE(name, type, field, cmp, lcmp)                                     \ +    RB_GENERATE_WITH_COMPARE_INTERNAL(name, type, field, cmp, lcmp, ) +#define RB_GENERATE_WITH_COMPARE_STATIC(name, type, field, cmp, lcmp)                              \ +    RB_GENERATE_WITH_COMPARE_INTERNAL(name, type, field, cmp, lcmp, static) +#define RB_GENERATE_WITH_COMPARE_INTERNAL(name, type, field, cmp, lcmp, attr)                      \ +    RB_GENERATE_INSERT_COLOR(name, type, field, attr)                                              \ +    RB_GENERATE_INSERT(name, type, field, cmp, attr)                                               \ +    RB_GENERATE_FIND(name, type, field, cmp, attr)                                                 \ +    RB_GENERATE_NFIND(name, type, field, cmp, attr)                                                \ +    RB_GENERATE_FIND_LIGHT(name, type, field, lcmp, attr)                                          \ +    RB_GENERATE_NFIND_LIGHT(name, type, field, lcmp, attr) + +#define RB_GENERATE_ALL(name, type, field, cmp) RB_GENERATE_ALL_INTERNAL(name, type, field, cmp, ) +#define RB_GENERATE_ALL_STATIC(name, type, field, cmp)                                             \ +    RB_GENERATE_ALL_INTERNAL(name, type, field, cmp, static) +#define RB_GENERATE_ALL_INTERNAL(name, type, field, cmp, attr)                                     \ +    RB_GENERATE_WITHOUT_COMPARE_INTERNAL(name, type, field, attr)                                  \ +    RB_GENERATE_WITH_COMPARE_INTERNAL(name, type, field, cmp, attr) + +#define RB_GENERATE_INSERT_COLOR(name, type, field, attr)                                          \ +    attr void name##_RB_INSERT_COLOR(struct name* head, struct type* elm) {                        \ +        struct type *parent, *gparent, *tmp;                                                       \ +        while ((parent = RB_PARENT(elm, field)) != NULL && RB_COLOR(parent, field) == RB_RED) {    \ +            gparent = RB_PARENT(parent, field);                                                    \ +            if (parent == RB_LEFT(gparent, field)) {                                               \ +                tmp = RB_RIGHT(gparent, field);                                                    \ +                if (tmp && RB_COLOR(tmp, field) == RB_RED) {                                       \ +                    RB_COLOR(tmp, field) = RB_BLACK;                                               \ +                    RB_SET_BLACKRED(parent, gparent, field);                                       \ +                    elm = gparent;                                                                 \ +                    continue;                                                                      \ +                }                                                                                  \ +                if (RB_RIGHT(parent, field) == elm) {                                              \ +                    RB_ROTATE_LEFT(head, parent, tmp, field);                                      \ +                    tmp = parent;                                                                  \ +                    parent = elm;                                                                  \ +                    elm = tmp;                                                                     \ +                }                                                                                  \ +                RB_SET_BLACKRED(parent, gparent, field);                                           \ +                RB_ROTATE_RIGHT(head, gparent, tmp, field);                                        \ +            } else {                                                                               \ +                tmp = RB_LEFT(gparent, field);                                                     \ +                if (tmp && RB_COLOR(tmp, field) == RB_RED) {                                       \ +                    RB_COLOR(tmp, field) = RB_BLACK;                                               \ +                    RB_SET_BLACKRED(parent, gparent, field);                                       \ +                    elm = gparent;                                                                 \ +                    continue;                                                                      \ +                }                                                                                  \ +                if (RB_LEFT(parent, field) == elm) {                                               \ +                    RB_ROTATE_RIGHT(head, parent, tmp, field);                                     \ +                    tmp = parent;                                                                  \ +                    parent = elm;                                                                  \ +                    elm = tmp;                                                                     \ +                }                                                                                  \ +                RB_SET_BLACKRED(parent, gparent, field);                                           \ +                RB_ROTATE_LEFT(head, gparent, tmp, field);                                         \ +            }                                                                                      \ +        }                                                                                          \ +        RB_COLOR(head->rbh_root, field) = RB_BLACK;                                                \ +    } + +#define RB_GENERATE_REMOVE_COLOR(name, type, field, attr)                                          \ +    attr void name##_RB_REMOVE_COLOR(struct name* head, struct type* parent, struct type* elm) {   \ +        struct type* tmp;                                                                          \ +        while ((elm == NULL || RB_COLOR(elm, field) == RB_BLACK) && elm != RB_ROOT(head)) {        \ +            if (RB_LEFT(parent, field) == elm) {                                                   \ +                tmp = RB_RIGHT(parent, field);                                                     \ +                if (RB_COLOR(tmp, field) == RB_RED) {                                              \ +                    RB_SET_BLACKRED(tmp, parent, field);                                           \ +                    RB_ROTATE_LEFT(head, parent, tmp, field);                                      \ +                    tmp = RB_RIGHT(parent, field);                                                 \ +                }                                                                                  \ +                if ((RB_LEFT(tmp, field) == NULL ||                                                \ +                     RB_COLOR(RB_LEFT(tmp, field), field) == RB_BLACK) &&                          \ +                    (RB_RIGHT(tmp, field) == NULL ||                                               \ +                     RB_COLOR(RB_RIGHT(tmp, field), field) == RB_BLACK)) {                         \ +                    RB_COLOR(tmp, field) = RB_RED;                                                 \ +                    elm = parent;                                                                  \ +                    parent = RB_PARENT(elm, field);                                                \ +                } else {                                                                           \ +                    if (RB_RIGHT(tmp, field) == NULL ||                                            \ +                        RB_COLOR(RB_RIGHT(tmp, field), field) == RB_BLACK) {                       \ +                        struct type* oleft;                                                        \ +                        if ((oleft = RB_LEFT(tmp, field)) != NULL)                                 \ +                            RB_COLOR(oleft, field) = RB_BLACK;                                     \ +                        RB_COLOR(tmp, field) = RB_RED;                                             \ +                        RB_ROTATE_RIGHT(head, tmp, oleft, field);                                  \ +                        tmp = RB_RIGHT(parent, field);                                             \ +                    }                                                                              \ +                    RB_COLOR(tmp, field) = RB_COLOR(parent, field);                                \ +                    RB_COLOR(parent, field) = RB_BLACK;                                            \ +                    if (RB_RIGHT(tmp, field))                                                      \ +                        RB_COLOR(RB_RIGHT(tmp, field), field) = RB_BLACK;                          \ +                    RB_ROTATE_LEFT(head, parent, tmp, field);                                      \ +                    elm = RB_ROOT(head);                                                           \ +                    break;                                                                         \ +                }                                                                                  \ +            } else {                                                                               \ +                tmp = RB_LEFT(parent, field);                                                      \ +                if (RB_COLOR(tmp, field) == RB_RED) {                                              \ +                    RB_SET_BLACKRED(tmp, parent, field);                                           \ +                    RB_ROTATE_RIGHT(head, parent, tmp, field);                                     \ +                    tmp = RB_LEFT(parent, field);                                                  \ +                }                                                                                  \ +                if ((RB_LEFT(tmp, field) == NULL ||                                                \ +                     RB_COLOR(RB_LEFT(tmp, field), field) == RB_BLACK) &&                          \ +                    (RB_RIGHT(tmp, field) == NULL ||                                               \ +                     RB_COLOR(RB_RIGHT(tmp, field), field) == RB_BLACK)) {                         \ +                    RB_COLOR(tmp, field) = RB_RED;                                                 \ +                    elm = parent;                                                                  \ +                    parent = RB_PARENT(elm, field);                                                \ +                } else {                                                                           \ +                    if (RB_LEFT(tmp, field) == NULL ||                                             \ +                        RB_COLOR(RB_LEFT(tmp, field), field) == RB_BLACK) {                        \ +                        struct type* oright;                                                       \ +                        if ((oright = RB_RIGHT(tmp, field)) != NULL)                               \ +                            RB_COLOR(oright, field) = RB_BLACK;                                    \ +                        RB_COLOR(tmp, field) = RB_RED;                                             \ +                        RB_ROTATE_LEFT(head, tmp, oright, field);                                  \ +                        tmp = RB_LEFT(parent, field);                                              \ +                    }                                                                              \ +                    RB_COLOR(tmp, field) = RB_COLOR(parent, field);                                \ +                    RB_COLOR(parent, field) = RB_BLACK;                                            \ +                    if (RB_LEFT(tmp, field))                                                       \ +                        RB_COLOR(RB_LEFT(tmp, field), field) = RB_BLACK;                           \ +                    RB_ROTATE_RIGHT(head, parent, tmp, field);                                     \ +                    elm = RB_ROOT(head);                                                           \ +                    break;                                                                         \ +                }                                                                                  \ +            }                                                                                      \ +        }                                                                                          \ +        if (elm)                                                                                   \ +            RB_COLOR(elm, field) = RB_BLACK;                                                       \ +    } + +#define RB_GENERATE_REMOVE(name, type, field, attr)                                                \ +    attr struct type* name##_RB_REMOVE(struct name* head, struct type* elm) {                      \ +        struct type *child, *parent, *old = elm;                                                   \ +        int color;                                                                                 \ +        if (RB_LEFT(elm, field) == NULL)                                                           \ +            child = RB_RIGHT(elm, field);                                                          \ +        else if (RB_RIGHT(elm, field) == NULL)                                                     \ +            child = RB_LEFT(elm, field);                                                           \ +        else {                                                                                     \ +            struct type* left;                                                                     \ +            elm = RB_RIGHT(elm, field);                                                            \ +            while ((left = RB_LEFT(elm, field)) != NULL)                                           \ +                elm = left;                                                                        \ +            child = RB_RIGHT(elm, field);                                                          \ +            parent = RB_PARENT(elm, field);                                                        \ +            color = RB_COLOR(elm, field);                                                          \ +            if (child)                                                                             \ +                RB_PARENT(child, field) = parent;                                                  \ +            if (parent) {                                                                          \ +                if (RB_LEFT(parent, field) == elm)                                                 \ +                    RB_LEFT(parent, field) = child;                                                \ +                else                                                                               \ +                    RB_RIGHT(parent, field) = child;                                               \ +                RB_AUGMENT(parent);                                                                \ +            } else                                                                                 \ +                RB_ROOT(head) = child;                                                             \ +            if (RB_PARENT(elm, field) == old)                                                      \ +                parent = elm;                                                                      \ +            (elm)->field = (old)->field;                                                           \ +            if (RB_PARENT(old, field)) {                                                           \ +                if (RB_LEFT(RB_PARENT(old, field), field) == old)                                  \ +                    RB_LEFT(RB_PARENT(old, field), field) = elm;                                   \ +                else                                                                               \ +                    RB_RIGHT(RB_PARENT(old, field), field) = elm;                                  \ +                RB_AUGMENT(RB_PARENT(old, field));                                                 \ +            } else                                                                                 \ +                RB_ROOT(head) = elm;                                                               \ +            RB_PARENT(RB_LEFT(old, field), field) = elm;                                           \ +            if (RB_RIGHT(old, field))                                                              \ +                RB_PARENT(RB_RIGHT(old, field), field) = elm;                                      \ +            if (parent) {                                                                          \ +                left = parent;                                                                     \ +                do {                                                                               \ +                    RB_AUGMENT(left);                                                              \ +                } while ((left = RB_PARENT(left, field)) != NULL);                                 \ +            }                                                                                      \ +            goto color;                                                                            \ +        }                                                                                          \ +        parent = RB_PARENT(elm, field);                                                            \ +        color = RB_COLOR(elm, field);                                                              \ +        if (child)                                                                                 \ +            RB_PARENT(child, field) = parent;                                                      \ +        if (parent) {                                                                              \ +            if (RB_LEFT(parent, field) == elm)                                                     \ +                RB_LEFT(parent, field) = child;                                                    \ +            else                                                                                   \ +                RB_RIGHT(parent, field) = child;                                                   \ +            RB_AUGMENT(parent);                                                                    \ +        } else                                                                                     \ +            RB_ROOT(head) = child;                                                                 \ +    color:                                                                                         \ +        if (color == RB_BLACK)                                                                     \ +            name##_RB_REMOVE_COLOR(head, parent, child);                                           \ +        return (old);                                                                              \ +    } + +#define RB_GENERATE_INSERT(name, type, field, cmp, attr)                                           \ +    /* Inserts a node into the RB tree */                                                          \ +    attr struct type* name##_RB_INSERT(struct name* head, struct type* elm) {                      \ +        struct type* tmp;                                                                          \ +        struct type* parent = NULL;                                                                \ +        int comp = 0;                                                                              \ +        tmp = RB_ROOT(head);                                                                       \ +        while (tmp) {                                                                              \ +            parent = tmp;                                                                          \ +            comp = (cmp)(elm, parent);                                                             \ +            if (comp < 0)                                                                          \ +                tmp = RB_LEFT(tmp, field);                                                         \ +            else if (comp > 0)                                                                     \ +                tmp = RB_RIGHT(tmp, field);                                                        \ +            else                                                                                   \ +                return (tmp);                                                                      \ +        }                                                                                          \ +        RB_SET(elm, parent, field);                                                                \ +        if (parent != NULL) {                                                                      \ +            if (comp < 0)                                                                          \ +                RB_LEFT(parent, field) = elm;                                                      \ +            else                                                                                   \ +                RB_RIGHT(parent, field) = elm;                                                     \ +            RB_AUGMENT(parent);                                                                    \ +        } else                                                                                     \ +            RB_ROOT(head) = elm;                                                                   \ +        name##_RB_INSERT_COLOR(head, elm);                                                         \ +        return (NULL);                                                                             \ +    } + +#define RB_GENERATE_FIND(name, type, field, cmp, attr)                                             \ +    /* Finds the node with the same key as elm */                                                  \ +    attr struct type* name##_RB_FIND(struct name* head, struct type* elm) {                        \ +        struct type* tmp = RB_ROOT(head);                                                          \ +        int comp;                                                                                  \ +        while (tmp) {                                                                              \ +            comp = cmp(elm, tmp);                                                                  \ +            if (comp < 0)                                                                          \ +                tmp = RB_LEFT(tmp, field);                                                         \ +            else if (comp > 0)                                                                     \ +                tmp = RB_RIGHT(tmp, field);                                                        \ +            else                                                                                   \ +                return (tmp);                                                                      \ +        }                                                                                          \ +        return (NULL);                                                                             \ +    } + +#define RB_GENERATE_NFIND(name, type, field, cmp, attr)                                            \ +    /* Finds the first node greater than or equal to the search key */                             \ +    attr struct type* name##_RB_NFIND(struct name* head, struct type* elm) {                       \ +        struct type* tmp = RB_ROOT(head);                                                          \ +        struct type* res = NULL;                                                                   \ +        int comp;                                                                                  \ +        while (tmp) {                                                                              \ +            comp = cmp(elm, tmp);                                                                  \ +            if (comp < 0) {                                                                        \ +                res = tmp;                                                                         \ +                tmp = RB_LEFT(tmp, field);                                                         \ +            } else if (comp > 0)                                                                   \ +                tmp = RB_RIGHT(tmp, field);                                                        \ +            else                                                                                   \ +                return (tmp);                                                                      \ +        }                                                                                          \ +        return (res);                                                                              \ +    } + +#define RB_GENERATE_FIND_LIGHT(name, type, field, lcmp, attr)                                      \ +    /* Finds the node with the same key as elm */                                                  \ +    attr struct type* name##_RB_FIND_LIGHT(struct name* head, const void* lelm) {                  \ +        struct type* tmp = RB_ROOT(head);                                                          \ +        int comp;                                                                                  \ +        while (tmp) {                                                                              \ +            comp = lcmp(lelm, tmp);                                                                \ +            if (comp < 0)                                                                          \ +                tmp = RB_LEFT(tmp, field);                                                         \ +            else if (comp > 0)                                                                     \ +                tmp = RB_RIGHT(tmp, field);                                                        \ +            else                                                                                   \ +                return (tmp);                                                                      \ +        }                                                                                          \ +        return (NULL);                                                                             \ +    } + +#define RB_GENERATE_NFIND_LIGHT(name, type, field, lcmp, attr)                                     \ +    /* Finds the first node greater than or equal to the search key */                             \ +    attr struct type* name##_RB_NFIND_LIGHT(struct name* head, const void* lelm) {                 \ +        struct type* tmp = RB_ROOT(head);                                                          \ +        struct type* res = NULL;                                                                   \ +        int comp;                                                                                  \ +        while (tmp) {                                                                              \ +            comp = lcmp(lelm, tmp);                                                                \ +            if (comp < 0) {                                                                        \ +                res = tmp;                                                                         \ +                tmp = RB_LEFT(tmp, field);                                                         \ +            } else if (comp > 0)                                                                   \ +                tmp = RB_RIGHT(tmp, field);                                                        \ +            else                                                                                   \ +                return (tmp);                                                                      \ +        }                                                                                          \ +        return (res);                                                                              \ +    } + +#define RB_GENERATE_NEXT(name, type, field, attr)                                                  \ +    /* ARGSUSED */                                                                                 \ +    attr struct type* name##_RB_NEXT(struct type* elm) {                                           \ +        if (RB_RIGHT(elm, field)) {                                                                \ +            elm = RB_RIGHT(elm, field);                                                            \ +            while (RB_LEFT(elm, field))                                                            \ +                elm = RB_LEFT(elm, field);                                                         \ +        } else {                                                                                   \ +            if (RB_PARENT(elm, field) && (elm == RB_LEFT(RB_PARENT(elm, field), field)))           \ +                elm = RB_PARENT(elm, field);                                                       \ +            else {                                                                                 \ +                while (RB_PARENT(elm, field) && (elm == RB_RIGHT(RB_PARENT(elm, field), field)))   \ +                    elm = RB_PARENT(elm, field);                                                   \ +                elm = RB_PARENT(elm, field);                                                       \ +            }                                                                                      \ +        }                                                                                          \ +        return (elm);                                                                              \ +    } + +#define RB_GENERATE_PREV(name, type, field, attr)                                                  \ +    /* ARGSUSED */                                                                                 \ +    attr struct type* name##_RB_PREV(struct type* elm) {                                           \ +        if (RB_LEFT(elm, field)) {                                                                 \ +            elm = RB_LEFT(elm, field);                                                             \ +            while (RB_RIGHT(elm, field))                                                           \ +                elm = RB_RIGHT(elm, field);                                                        \ +        } else {                                                                                   \ +            if (RB_PARENT(elm, field) && (elm == RB_RIGHT(RB_PARENT(elm, field), field)))          \ +                elm = RB_PARENT(elm, field);                                                       \ +            else {                                                                                 \ +                while (RB_PARENT(elm, field) && (elm == RB_LEFT(RB_PARENT(elm, field), field)))    \ +                    elm = RB_PARENT(elm, field);                                                   \ +                elm = RB_PARENT(elm, field);                                                       \ +            }                                                                                      \ +        }                                                                                          \ +        return (elm);                                                                              \ +    } + +#define RB_GENERATE_MINMAX(name, type, field, attr)                                                \ +    attr struct type* name##_RB_MINMAX(struct name* head, int val) {                               \ +        struct type* tmp = RB_ROOT(head);                                                          \ +        struct type* parent = NULL;                                                                \ +        while (tmp) {                                                                              \ +            parent = tmp;                                                                          \ +            if (val < 0)                                                                           \ +                tmp = RB_LEFT(tmp, field);                                                         \ +            else                                                                                   \ +                tmp = RB_RIGHT(tmp, field);                                                        \ +        }                                                                                          \ +        return (parent);                                                                           \ +    } + +#define RB_NEGINF -1 +#define RB_INF 1 + +#define RB_INSERT(name, x, y) name##_RB_INSERT(x, y) +#define RB_REMOVE(name, x, y) name##_RB_REMOVE(x, y) +#define RB_FIND(name, x, y) name##_RB_FIND(x, y) +#define RB_NFIND(name, x, y) name##_RB_NFIND(x, y) +#define RB_FIND_LIGHT(name, x, y) name##_RB_FIND_LIGHT(x, y) +#define RB_NFIND_LIGHT(name, x, y) name##_RB_NFIND_LIGHT(x, y) +#define RB_NEXT(name, x, y) name##_RB_NEXT(y) +#define RB_PREV(name, x, y) name##_RB_PREV(y) +#define RB_MIN(name, x) name##_RB_MINMAX(x, RB_NEGINF) +#define RB_MAX(name, x) name##_RB_MINMAX(x, RB_INF) + +#define RB_FOREACH(x, name, head)                                                                  \ +    for ((x) = RB_MIN(name, head); (x) != NULL; (x) = name##_RB_NEXT(x)) + +#define RB_FOREACH_FROM(x, name, y)                                                                \ +    for ((x) = (y); ((x) != NULL) && ((y) = name##_RB_NEXT(x), (x) != NULL); (x) = (y)) + +#define RB_FOREACH_SAFE(x, name, head, y)                                                          \ +    for ((x) = RB_MIN(name, head); ((x) != NULL) && ((y) = name##_RB_NEXT(x), (x) != NULL);        \ +         (x) = (y)) + +#define RB_FOREACH_REVERSE(x, name, head)                                                          \ +    for ((x) = RB_MAX(name, head); (x) != NULL; (x) = name##_RB_PREV(x)) + +#define RB_FOREACH_REVERSE_FROM(x, name, y)                                                        \ +    for ((x) = (y); ((x) != NULL) && ((y) = name##_RB_PREV(x), (x) != NULL); (x) = (y)) + +#define RB_FOREACH_REVERSE_SAFE(x, name, head, y)                                                  \ +    for ((x) = RB_MAX(name, head); ((x) != NULL) && ((y) = name##_RB_PREV(x), (x) != NULL);        \ +         (x) = (y)) + +#endif /* _SYS_TREE_H_ */ diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index 893df433a..1b8ad476e 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -142,8 +142,6 @@ add_library(core STATIC      hardware_interrupt_manager.h      hle/ipc.h      hle/ipc_helpers.h -    hle/kernel/address_arbiter.cpp -    hle/kernel/address_arbiter.h      hle/kernel/client_port.cpp      hle/kernel/client_port.h      hle/kernel/client_session.cpp @@ -157,13 +155,19 @@ add_library(core STATIC      hle/kernel/handle_table.h      hle/kernel/hle_ipc.cpp      hle/kernel/hle_ipc.h +    hle/kernel/k_address_arbiter.cpp +    hle/kernel/k_address_arbiter.h      hle/kernel/k_affinity_mask.h +    hle/kernel/k_condition_variable.cpp +    hle/kernel/k_condition_variable.h      hle/kernel/k_priority_queue.h      hle/kernel/k_scheduler.cpp      hle/kernel/k_scheduler.h      hle/kernel/k_scheduler_lock.h      hle/kernel/k_scoped_lock.h      hle/kernel/k_scoped_scheduler_lock_and_sleep.h +    hle/kernel/k_synchronization_object.cpp +    hle/kernel/k_synchronization_object.h      hle/kernel/kernel.cpp      hle/kernel/kernel.h      hle/kernel/memory/address_space_info.cpp @@ -183,8 +187,6 @@ add_library(core STATIC      hle/kernel/memory/slab_heap.h      hle/kernel/memory/system_control.cpp      hle/kernel/memory/system_control.h -    hle/kernel/mutex.cpp -    hle/kernel/mutex.h      hle/kernel/object.cpp      hle/kernel/object.h      hle/kernel/physical_core.cpp @@ -210,12 +212,10 @@ add_library(core STATIC      hle/kernel/shared_memory.h      hle/kernel/svc.cpp      hle/kernel/svc.h +    hle/kernel/svc_common.h +    hle/kernel/svc_results.h      hle/kernel/svc_types.h      hle/kernel/svc_wrap.h -    hle/kernel/synchronization_object.cpp -    hle/kernel/synchronization_object.h -    hle/kernel/synchronization.cpp -    hle/kernel/synchronization.h      hle/kernel/thread.cpp      hle/kernel/thread.h      hle/kernel/time_manager.cpp diff --git a/src/core/arm/arm_interface.h b/src/core/arm/arm_interface.h index 70098c526..9a0151736 100644 --- a/src/core/arm/arm_interface.h +++ b/src/core/arm/arm_interface.h @@ -26,9 +26,10 @@ using CPUInterrupts = std::array<CPUInterruptHandler, Core::Hardware::NUM_CPU_CO  /// Generic ARMv8 CPU interface  class ARM_Interface : NonCopyable {  public: -    explicit ARM_Interface(System& system_, CPUInterrupts& interrupt_handlers, bool uses_wall_clock) -        : system{system_}, interrupt_handlers{interrupt_handlers}, uses_wall_clock{ -                                                                       uses_wall_clock} {} +    explicit ARM_Interface(System& system_, CPUInterrupts& interrupt_handlers_, +                           bool uses_wall_clock_) +        : system{system_}, interrupt_handlers{interrupt_handlers_}, uses_wall_clock{ +                                                                        uses_wall_clock_} {}      virtual ~ARM_Interface() = default;      struct ThreadContext32 { diff --git a/src/core/arm/dynarmic/arm_dynarmic_32.cpp b/src/core/arm/dynarmic/arm_dynarmic_32.cpp index 0831dd5d2..6c4c8e9e4 100644 --- a/src/core/arm/dynarmic/arm_dynarmic_32.cpp +++ b/src/core/arm/dynarmic/arm_dynarmic_32.cpp @@ -71,15 +71,8 @@ public:      }      void ExceptionRaised(u32 pc, Dynarmic::A32::Exception exception) override { -        switch (exception) { -        case Dynarmic::A32::Exception::UndefinedInstruction: -        case Dynarmic::A32::Exception::UnpredictableInstruction: -            break; -        case Dynarmic::A32::Exception::Breakpoint: -            break; -        }          LOG_CRITICAL(Core_ARM, "ExceptionRaised(exception = {}, pc = {:08X}, code = {:08X})", -                     static_cast<std::size_t>(exception), pc, MemoryReadCode(pc)); +                     exception, pc, MemoryReadCode(pc));          UNIMPLEMENTED();      } diff --git a/src/core/core_timing.cpp b/src/core/core_timing.cpp index e6c8461a5..874b5673a 100644 --- a/src/core/core_timing.cpp +++ b/src/core/core_timing.cpp @@ -49,6 +49,7 @@ void CoreTiming::ThreadEntry(CoreTiming& instance) {      Common::SetCurrentThreadPriority(Common::ThreadPriority::VeryHigh);      instance.on_thread_init();      instance.ThreadLoop(); +    MicroProfileOnThreadExit();  }  void CoreTiming::Initialize(std::function<void()>&& on_thread_init_) { diff --git a/src/core/file_sys/registered_cache.h b/src/core/file_sys/registered_cache.h index 5b414b0f0..b08a1687a 100644 --- a/src/core/file_sys/registered_cache.h +++ b/src/core/file_sys/registered_cache.h @@ -67,18 +67,18 @@ public:      virtual void Refresh() = 0;      virtual bool HasEntry(u64 title_id, ContentRecordType type) const = 0; -    virtual bool HasEntry(ContentProviderEntry entry) const; +    bool HasEntry(ContentProviderEntry entry) const;      virtual std::optional<u32> GetEntryVersion(u64 title_id) const = 0;      virtual VirtualFile GetEntryUnparsed(u64 title_id, ContentRecordType type) const = 0; -    virtual VirtualFile GetEntryUnparsed(ContentProviderEntry entry) const; +    VirtualFile GetEntryUnparsed(ContentProviderEntry entry) const;      virtual VirtualFile GetEntryRaw(u64 title_id, ContentRecordType type) const = 0; -    virtual VirtualFile GetEntryRaw(ContentProviderEntry entry) const; +    VirtualFile GetEntryRaw(ContentProviderEntry entry) const;      virtual std::unique_ptr<NCA> GetEntry(u64 title_id, ContentRecordType type) const = 0; -    virtual std::unique_ptr<NCA> GetEntry(ContentProviderEntry entry) const; +    std::unique_ptr<NCA> GetEntry(ContentProviderEntry entry) const;      virtual std::vector<ContentProviderEntry> ListEntries() const; diff --git a/src/core/hle/kernel/address_arbiter.cpp b/src/core/hle/kernel/address_arbiter.cpp deleted file mode 100644 index 20ffa7d47..000000000 --- a/src/core/hle/kernel/address_arbiter.cpp +++ /dev/null @@ -1,317 +0,0 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. - -#include <algorithm> -#include <vector> - -#include "common/assert.h" -#include "common/common_types.h" -#include "core/arm/exclusive_monitor.h" -#include "core/core.h" -#include "core/hle/kernel/address_arbiter.h" -#include "core/hle/kernel/errors.h" -#include "core/hle/kernel/handle_table.h" -#include "core/hle/kernel/k_scheduler.h" -#include "core/hle/kernel/k_scoped_scheduler_lock_and_sleep.h" -#include "core/hle/kernel/kernel.h" -#include "core/hle/kernel/thread.h" -#include "core/hle/kernel/time_manager.h" -#include "core/hle/result.h" -#include "core/memory.h" - -namespace Kernel { - -// Wake up num_to_wake (or all) threads in a vector. -void AddressArbiter::WakeThreads(const std::vector<std::shared_ptr<Thread>>& waiting_threads, -                                 s32 num_to_wake) { -    // Only process up to 'target' threads, unless 'target' is <= 0, in which case process -    // them all. -    std::size_t last = waiting_threads.size(); -    if (num_to_wake > 0) { -        last = std::min(last, static_cast<std::size_t>(num_to_wake)); -    } - -    // Signal the waiting threads. -    for (std::size_t i = 0; i < last; i++) { -        waiting_threads[i]->SetSynchronizationResults(nullptr, RESULT_SUCCESS); -        RemoveThread(waiting_threads[i]); -        waiting_threads[i]->WaitForArbitration(false); -        waiting_threads[i]->ResumeFromWait(); -    } -} - -AddressArbiter::AddressArbiter(Core::System& system) : system{system} {} -AddressArbiter::~AddressArbiter() = default; - -ResultCode AddressArbiter::SignalToAddress(VAddr address, SignalType type, s32 value, -                                           s32 num_to_wake) { -    switch (type) { -    case SignalType::Signal: -        return SignalToAddressOnly(address, num_to_wake); -    case SignalType::IncrementAndSignalIfEqual: -        return IncrementAndSignalToAddressIfEqual(address, value, num_to_wake); -    case SignalType::ModifyByWaitingCountAndSignalIfEqual: -        return ModifyByWaitingCountAndSignalToAddressIfEqual(address, value, num_to_wake); -    default: -        return ERR_INVALID_ENUM_VALUE; -    } -} - -ResultCode AddressArbiter::SignalToAddressOnly(VAddr address, s32 num_to_wake) { -    KScopedSchedulerLock lock(system.Kernel()); -    const std::vector<std::shared_ptr<Thread>> waiting_threads = -        GetThreadsWaitingOnAddress(address); -    WakeThreads(waiting_threads, num_to_wake); -    return RESULT_SUCCESS; -} - -ResultCode AddressArbiter::IncrementAndSignalToAddressIfEqual(VAddr address, s32 value, -                                                              s32 num_to_wake) { -    KScopedSchedulerLock lock(system.Kernel()); -    auto& memory = system.Memory(); - -    // Ensure that we can write to the address. -    if (!memory.IsValidVirtualAddress(address)) { -        return ERR_INVALID_ADDRESS_STATE; -    } - -    const std::size_t current_core = system.CurrentCoreIndex(); -    auto& monitor = system.Monitor(); -    u32 current_value; -    do { -        current_value = monitor.ExclusiveRead32(current_core, address); - -        if (current_value != static_cast<u32>(value)) { -            return ERR_INVALID_STATE; -        } -        current_value++; -    } while (!monitor.ExclusiveWrite32(current_core, address, current_value)); - -    return SignalToAddressOnly(address, num_to_wake); -} - -ResultCode AddressArbiter::ModifyByWaitingCountAndSignalToAddressIfEqual(VAddr address, s32 value, -                                                                         s32 num_to_wake) { -    KScopedSchedulerLock lock(system.Kernel()); -    auto& memory = system.Memory(); - -    // Ensure that we can write to the address. -    if (!memory.IsValidVirtualAddress(address)) { -        return ERR_INVALID_ADDRESS_STATE; -    } - -    // Get threads waiting on the address. -    const std::vector<std::shared_ptr<Thread>> waiting_threads = -        GetThreadsWaitingOnAddress(address); - -    const std::size_t current_core = system.CurrentCoreIndex(); -    auto& monitor = system.Monitor(); -    s32 updated_value; -    do { -        updated_value = monitor.ExclusiveRead32(current_core, address); - -        if (updated_value != value) { -            return ERR_INVALID_STATE; -        } -        // Determine the modified value depending on the waiting count. -        if (num_to_wake <= 0) { -            if (waiting_threads.empty()) { -                updated_value = value + 1; -            } else { -                updated_value = value - 1; -            } -        } else { -            if (waiting_threads.empty()) { -                updated_value = value + 1; -            } else if (waiting_threads.size() <= static_cast<u32>(num_to_wake)) { -                updated_value = value - 1; -            } else { -                updated_value = value; -            } -        } -    } while (!monitor.ExclusiveWrite32(current_core, address, updated_value)); - -    WakeThreads(waiting_threads, num_to_wake); -    return RESULT_SUCCESS; -} - -ResultCode AddressArbiter::WaitForAddress(VAddr address, ArbitrationType type, s32 value, -                                          s64 timeout_ns) { -    switch (type) { -    case ArbitrationType::WaitIfLessThan: -        return WaitForAddressIfLessThan(address, value, timeout_ns, false); -    case ArbitrationType::DecrementAndWaitIfLessThan: -        return WaitForAddressIfLessThan(address, value, timeout_ns, true); -    case ArbitrationType::WaitIfEqual: -        return WaitForAddressIfEqual(address, value, timeout_ns); -    default: -        return ERR_INVALID_ENUM_VALUE; -    } -} - -ResultCode AddressArbiter::WaitForAddressIfLessThan(VAddr address, s32 value, s64 timeout, -                                                    bool should_decrement) { -    auto& memory = system.Memory(); -    auto& kernel = system.Kernel(); -    Thread* current_thread = kernel.CurrentScheduler()->GetCurrentThread(); - -    Handle event_handle = InvalidHandle; -    { -        KScopedSchedulerLockAndSleep lock(kernel, event_handle, current_thread, timeout); - -        if (current_thread->IsPendingTermination()) { -            lock.CancelSleep(); -            return ERR_THREAD_TERMINATING; -        } - -        // Ensure that we can read the address. -        if (!memory.IsValidVirtualAddress(address)) { -            lock.CancelSleep(); -            return ERR_INVALID_ADDRESS_STATE; -        } - -        s32 current_value = static_cast<s32>(memory.Read32(address)); -        if (current_value >= value) { -            lock.CancelSleep(); -            return ERR_INVALID_STATE; -        } - -        current_thread->SetSynchronizationResults(nullptr, RESULT_TIMEOUT); - -        s32 decrement_value; - -        const std::size_t current_core = system.CurrentCoreIndex(); -        auto& monitor = system.Monitor(); -        do { -            current_value = static_cast<s32>(monitor.ExclusiveRead32(current_core, address)); -            if (should_decrement) { -                decrement_value = current_value - 1; -            } else { -                decrement_value = current_value; -            } -        } while ( -            !monitor.ExclusiveWrite32(current_core, address, static_cast<u32>(decrement_value))); - -        // Short-circuit without rescheduling, if timeout is zero. -        if (timeout == 0) { -            lock.CancelSleep(); -            return RESULT_TIMEOUT; -        } - -        current_thread->SetArbiterWaitAddress(address); -        InsertThread(SharedFrom(current_thread)); -        current_thread->SetStatus(ThreadStatus::WaitArb); -        current_thread->WaitForArbitration(true); -    } - -    if (event_handle != InvalidHandle) { -        auto& time_manager = kernel.TimeManager(); -        time_manager.UnscheduleTimeEvent(event_handle); -    } - -    { -        KScopedSchedulerLock lock(kernel); -        if (current_thread->IsWaitingForArbitration()) { -            RemoveThread(SharedFrom(current_thread)); -            current_thread->WaitForArbitration(false); -        } -    } - -    return current_thread->GetSignalingResult(); -} - -ResultCode AddressArbiter::WaitForAddressIfEqual(VAddr address, s32 value, s64 timeout) { -    auto& memory = system.Memory(); -    auto& kernel = system.Kernel(); -    Thread* current_thread = kernel.CurrentScheduler()->GetCurrentThread(); - -    Handle event_handle = InvalidHandle; -    { -        KScopedSchedulerLockAndSleep lock(kernel, event_handle, current_thread, timeout); - -        if (current_thread->IsPendingTermination()) { -            lock.CancelSleep(); -            return ERR_THREAD_TERMINATING; -        } - -        // Ensure that we can read the address. -        if (!memory.IsValidVirtualAddress(address)) { -            lock.CancelSleep(); -            return ERR_INVALID_ADDRESS_STATE; -        } - -        s32 current_value = static_cast<s32>(memory.Read32(address)); -        if (current_value != value) { -            lock.CancelSleep(); -            return ERR_INVALID_STATE; -        } - -        // Short-circuit without rescheduling, if timeout is zero. -        if (timeout == 0) { -            lock.CancelSleep(); -            return RESULT_TIMEOUT; -        } - -        current_thread->SetSynchronizationResults(nullptr, RESULT_TIMEOUT); -        current_thread->SetArbiterWaitAddress(address); -        InsertThread(SharedFrom(current_thread)); -        current_thread->SetStatus(ThreadStatus::WaitArb); -        current_thread->WaitForArbitration(true); -    } - -    if (event_handle != InvalidHandle) { -        auto& time_manager = kernel.TimeManager(); -        time_manager.UnscheduleTimeEvent(event_handle); -    } - -    { -        KScopedSchedulerLock lock(kernel); -        if (current_thread->IsWaitingForArbitration()) { -            RemoveThread(SharedFrom(current_thread)); -            current_thread->WaitForArbitration(false); -        } -    } - -    return current_thread->GetSignalingResult(); -} - -void AddressArbiter::InsertThread(std::shared_ptr<Thread> thread) { -    const VAddr arb_addr = thread->GetArbiterWaitAddress(); -    std::list<std::shared_ptr<Thread>>& thread_list = arb_threads[arb_addr]; - -    const auto iter = -        std::find_if(thread_list.cbegin(), thread_list.cend(), [&thread](const auto& entry) { -            return entry->GetPriority() >= thread->GetPriority(); -        }); - -    if (iter == thread_list.cend()) { -        thread_list.push_back(std::move(thread)); -    } else { -        thread_list.insert(iter, std::move(thread)); -    } -} - -void AddressArbiter::RemoveThread(std::shared_ptr<Thread> thread) { -    const VAddr arb_addr = thread->GetArbiterWaitAddress(); -    std::list<std::shared_ptr<Thread>>& thread_list = arb_threads[arb_addr]; - -    const auto iter = std::find_if(thread_list.cbegin(), thread_list.cend(), -                                   [&thread](const auto& entry) { return thread == entry; }); - -    if (iter != thread_list.cend()) { -        thread_list.erase(iter); -    } -} - -std::vector<std::shared_ptr<Thread>> AddressArbiter::GetThreadsWaitingOnAddress( -    VAddr address) const { -    const auto iter = arb_threads.find(address); -    if (iter == arb_threads.cend()) { -        return {}; -    } - -    const std::list<std::shared_ptr<Thread>>& thread_list = iter->second; -    return {thread_list.cbegin(), thread_list.cend()}; -} -} // namespace Kernel diff --git a/src/core/hle/kernel/address_arbiter.h b/src/core/hle/kernel/address_arbiter.h deleted file mode 100644 index b91edc67d..000000000 --- a/src/core/hle/kernel/address_arbiter.h +++ /dev/null @@ -1,91 +0,0 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. - -#pragma once - -#include <list> -#include <memory> -#include <unordered_map> -#include <vector> - -#include "common/common_types.h" - -union ResultCode; - -namespace Core { -class System; -} - -namespace Kernel { - -class Thread; - -class AddressArbiter { -public: -    enum class ArbitrationType { -        WaitIfLessThan = 0, -        DecrementAndWaitIfLessThan = 1, -        WaitIfEqual = 2, -    }; - -    enum class SignalType { -        Signal = 0, -        IncrementAndSignalIfEqual = 1, -        ModifyByWaitingCountAndSignalIfEqual = 2, -    }; - -    explicit AddressArbiter(Core::System& system); -    ~AddressArbiter(); - -    AddressArbiter(const AddressArbiter&) = delete; -    AddressArbiter& operator=(const AddressArbiter&) = delete; - -    AddressArbiter(AddressArbiter&&) = default; -    AddressArbiter& operator=(AddressArbiter&&) = delete; - -    /// Signals an address being waited on with a particular signaling type. -    ResultCode SignalToAddress(VAddr address, SignalType type, s32 value, s32 num_to_wake); - -    /// Waits on an address with a particular arbitration type. -    ResultCode WaitForAddress(VAddr address, ArbitrationType type, s32 value, s64 timeout_ns); - -private: -    /// Signals an address being waited on. -    ResultCode SignalToAddressOnly(VAddr address, s32 num_to_wake); - -    /// Signals an address being waited on and increments its value if equal to the value argument. -    ResultCode IncrementAndSignalToAddressIfEqual(VAddr address, s32 value, s32 num_to_wake); - -    /// Signals an address being waited on and modifies its value based on waiting thread count if -    /// equal to the value argument. -    ResultCode ModifyByWaitingCountAndSignalToAddressIfEqual(VAddr address, s32 value, -                                                             s32 num_to_wake); - -    /// Waits on an address if the value passed is less than the argument value, -    /// optionally decrementing. -    ResultCode WaitForAddressIfLessThan(VAddr address, s32 value, s64 timeout, -                                        bool should_decrement); - -    /// Waits on an address if the value passed is equal to the argument value. -    ResultCode WaitForAddressIfEqual(VAddr address, s32 value, s64 timeout); - -    /// Wake up num_to_wake (or all) threads in a vector. -    void WakeThreads(const std::vector<std::shared_ptr<Thread>>& waiting_threads, s32 num_to_wake); - -    /// Insert a thread into the address arbiter container -    void InsertThread(std::shared_ptr<Thread> thread); - -    /// Removes a thread from the address arbiter container -    void RemoveThread(std::shared_ptr<Thread> thread); - -    // Gets the threads waiting on an address. -    std::vector<std::shared_ptr<Thread>> GetThreadsWaitingOnAddress(VAddr address) const; - -    /// List of threads waiting for a address arbiter -    std::unordered_map<VAddr, std::list<std::shared_ptr<Thread>>> arb_threads; - -    Core::System& system; -}; - -} // namespace Kernel diff --git a/src/core/hle/kernel/client_port.cpp b/src/core/hle/kernel/client_port.cpp index 8aff2227a..f8f005f15 100644 --- a/src/core/hle/kernel/client_port.cpp +++ b/src/core/hle/kernel/client_port.cpp @@ -33,9 +33,6 @@ ResultVal<std::shared_ptr<ClientSession>> ClientPort::Connect() {          server_port->AppendPendingSession(std::move(server));      } -    // Wake the threads waiting on the ServerPort -    server_port->Signal(); -      return MakeResult(std::move(client));  } diff --git a/src/core/hle/kernel/client_session.cpp b/src/core/hle/kernel/client_session.cpp index be9eba519..e8e52900d 100644 --- a/src/core/hle/kernel/client_session.cpp +++ b/src/core/hle/kernel/client_session.cpp @@ -12,7 +12,7 @@  namespace Kernel { -ClientSession::ClientSession(KernelCore& kernel) : SynchronizationObject{kernel} {} +ClientSession::ClientSession(KernelCore& kernel) : KSynchronizationObject{kernel} {}  ClientSession::~ClientSession() {      // This destructor will be called automatically when the last ClientSession handle is closed by @@ -22,15 +22,6 @@ ClientSession::~ClientSession() {      }  } -bool ClientSession::ShouldWait(const Thread* thread) const { -    UNIMPLEMENTED(); -    return {}; -} - -void ClientSession::Acquire(Thread* thread) { -    UNIMPLEMENTED(); -} -  bool ClientSession::IsSignaled() const {      UNIMPLEMENTED();      return true; diff --git a/src/core/hle/kernel/client_session.h b/src/core/hle/kernel/client_session.h index e5e0690c2..d5c9ebee8 100644 --- a/src/core/hle/kernel/client_session.h +++ b/src/core/hle/kernel/client_session.h @@ -7,7 +7,7 @@  #include <memory>  #include <string> -#include "core/hle/kernel/synchronization_object.h" +#include "core/hle/kernel/k_synchronization_object.h"  #include "core/hle/result.h"  union ResultCode; @@ -26,7 +26,7 @@ class KernelCore;  class Session;  class Thread; -class ClientSession final : public SynchronizationObject { +class ClientSession final : public KSynchronizationObject {  public:      explicit ClientSession(KernelCore& kernel);      ~ClientSession() override; @@ -49,10 +49,6 @@ public:      ResultCode SendSyncRequest(std::shared_ptr<Thread> thread, Core::Memory::Memory& memory,                                 Core::Timing::CoreTiming& core_timing); -    bool ShouldWait(const Thread* thread) const override; - -    void Acquire(Thread* thread) override; -      bool IsSignaled() const override;  private: diff --git a/src/core/hle/kernel/errors.h b/src/core/hle/kernel/errors.h index d4e5d88cf..7d32a39f0 100644 --- a/src/core/hle/kernel/errors.h +++ b/src/core/hle/kernel/errors.h @@ -13,12 +13,14 @@ namespace Kernel {  constexpr ResultCode ERR_MAX_CONNECTIONS_REACHED{ErrorModule::Kernel, 7};  constexpr ResultCode ERR_INVALID_CAPABILITY_DESCRIPTOR{ErrorModule::Kernel, 14};  constexpr ResultCode ERR_THREAD_TERMINATING{ErrorModule::Kernel, 59}; +constexpr ResultCode ERR_TERMINATION_REQUESTED{ErrorModule::Kernel, 59};  constexpr ResultCode ERR_INVALID_SIZE{ErrorModule::Kernel, 101};  constexpr ResultCode ERR_INVALID_ADDRESS{ErrorModule::Kernel, 102};  constexpr ResultCode ERR_OUT_OF_RESOURCES{ErrorModule::Kernel, 103};  constexpr ResultCode ERR_OUT_OF_MEMORY{ErrorModule::Kernel, 104};  constexpr ResultCode ERR_HANDLE_TABLE_FULL{ErrorModule::Kernel, 105};  constexpr ResultCode ERR_INVALID_ADDRESS_STATE{ErrorModule::Kernel, 106}; +constexpr ResultCode ERR_INVALID_CURRENT_MEMORY{ErrorModule::Kernel, 106};  constexpr ResultCode ERR_INVALID_MEMORY_PERMISSIONS{ErrorModule::Kernel, 108};  constexpr ResultCode ERR_INVALID_MEMORY_RANGE{ErrorModule::Kernel, 110};  constexpr ResultCode ERR_INVALID_PROCESSOR_ID{ErrorModule::Kernel, 113}; @@ -28,6 +30,7 @@ constexpr ResultCode ERR_INVALID_POINTER{ErrorModule::Kernel, 115};  constexpr ResultCode ERR_INVALID_COMBINATION{ErrorModule::Kernel, 116};  constexpr ResultCode RESULT_TIMEOUT{ErrorModule::Kernel, 117};  constexpr ResultCode ERR_SYNCHRONIZATION_CANCELED{ErrorModule::Kernel, 118}; +constexpr ResultCode ERR_CANCELLED{ErrorModule::Kernel, 118};  constexpr ResultCode ERR_OUT_OF_RANGE{ErrorModule::Kernel, 119};  constexpr ResultCode ERR_INVALID_ENUM_VALUE{ErrorModule::Kernel, 120};  constexpr ResultCode ERR_NOT_FOUND{ErrorModule::Kernel, 121}; diff --git a/src/core/hle/kernel/k_address_arbiter.cpp b/src/core/hle/kernel/k_address_arbiter.cpp new file mode 100644 index 000000000..d9e702f13 --- /dev/null +++ b/src/core/hle/kernel/k_address_arbiter.cpp @@ -0,0 +1,367 @@ +// Copyright 2021 yuzu Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include "core/arm/exclusive_monitor.h" +#include "core/core.h" +#include "core/hle/kernel/k_address_arbiter.h" +#include "core/hle/kernel/k_scheduler.h" +#include "core/hle/kernel/k_scoped_scheduler_lock_and_sleep.h" +#include "core/hle/kernel/kernel.h" +#include "core/hle/kernel/svc_results.h" +#include "core/hle/kernel/thread.h" +#include "core/hle/kernel/time_manager.h" +#include "core/memory.h" + +namespace Kernel { + +KAddressArbiter::KAddressArbiter(Core::System& system_) +    : system{system_}, kernel{system.Kernel()} {} +KAddressArbiter::~KAddressArbiter() = default; + +namespace { + +bool ReadFromUser(Core::System& system, s32* out, VAddr address) { +    *out = system.Memory().Read32(address); +    return true; +} + +bool DecrementIfLessThan(Core::System& system, s32* out, VAddr address, s32 value) { +    auto& monitor = system.Monitor(); +    const auto current_core = system.CurrentCoreIndex(); + +    // TODO(bunnei): We should disable interrupts here via KScopedInterruptDisable. +    // TODO(bunnei): We should call CanAccessAtomic(..) here. + +    // Load the value from the address. +    const s32 current_value = static_cast<s32>(monitor.ExclusiveRead32(current_core, address)); + +    // Compare it to the desired one. +    if (current_value < value) { +        // If less than, we want to try to decrement. +        const s32 decrement_value = current_value - 1; + +        // Decrement and try to store. +        if (!monitor.ExclusiveWrite32(current_core, address, static_cast<u32>(decrement_value))) { +            // If we failed to store, try again. +            DecrementIfLessThan(system, out, address, value); +        } +    } else { +        // Otherwise, clear our exclusive hold and finish +        monitor.ClearExclusive(); +    } + +    // We're done. +    *out = current_value; +    return true; +} + +bool UpdateIfEqual(Core::System& system, s32* out, VAddr address, s32 value, s32 new_value) { +    auto& monitor = system.Monitor(); +    const auto current_core = system.CurrentCoreIndex(); + +    // TODO(bunnei): We should disable interrupts here via KScopedInterruptDisable. +    // TODO(bunnei): We should call CanAccessAtomic(..) here. + +    // Load the value from the address. +    const s32 current_value = static_cast<s32>(monitor.ExclusiveRead32(current_core, address)); + +    // Compare it to the desired one. +    if (current_value == value) { +        // If equal, we want to try to write the new value. + +        // Try to store. +        if (!monitor.ExclusiveWrite32(current_core, address, static_cast<u32>(new_value))) { +            // If we failed to store, try again. +            UpdateIfEqual(system, out, address, value, new_value); +        } +    } else { +        // Otherwise, clear our exclusive hold and finish. +        monitor.ClearExclusive(); +    } + +    // We're done. +    *out = current_value; +    return true; +} + +} // namespace + +ResultCode KAddressArbiter::Signal(VAddr addr, s32 count) { +    // Perform signaling. +    s32 num_waiters{}; +    { +        KScopedSchedulerLock sl(kernel); + +        auto it = thread_tree.nfind_light({addr, -1}); +        while ((it != thread_tree.end()) && (count <= 0 || num_waiters < count) && +               (it->GetAddressArbiterKey() == addr)) { +            Thread* target_thread = std::addressof(*it); +            target_thread->SetSyncedObject(nullptr, RESULT_SUCCESS); + +            ASSERT(target_thread->IsWaitingForAddressArbiter()); +            target_thread->Wakeup(); + +            it = thread_tree.erase(it); +            target_thread->ClearAddressArbiter(); +            ++num_waiters; +        } +    } +    return RESULT_SUCCESS; +} + +ResultCode KAddressArbiter::SignalAndIncrementIfEqual(VAddr addr, s32 value, s32 count) { +    // Perform signaling. +    s32 num_waiters{}; +    { +        KScopedSchedulerLock sl(kernel); + +        // Check the userspace value. +        s32 user_value{}; +        R_UNLESS(UpdateIfEqual(system, std::addressof(user_value), addr, value, value + 1), +                 Svc::ResultInvalidCurrentMemory); +        R_UNLESS(user_value == value, Svc::ResultInvalidState); + +        auto it = thread_tree.nfind_light({addr, -1}); +        while ((it != thread_tree.end()) && (count <= 0 || num_waiters < count) && +               (it->GetAddressArbiterKey() == addr)) { +            Thread* target_thread = std::addressof(*it); +            target_thread->SetSyncedObject(nullptr, RESULT_SUCCESS); + +            ASSERT(target_thread->IsWaitingForAddressArbiter()); +            target_thread->Wakeup(); + +            it = thread_tree.erase(it); +            target_thread->ClearAddressArbiter(); +            ++num_waiters; +        } +    } +    return RESULT_SUCCESS; +} + +ResultCode KAddressArbiter::SignalAndModifyByWaitingCountIfEqual(VAddr addr, s32 value, s32 count) { +    // Perform signaling. +    s32 num_waiters{}; +    { +        KScopedSchedulerLock sl(kernel); + +        auto it = thread_tree.nfind_light({addr, -1}); +        // Determine the updated value. +        s32 new_value{}; +        if (/*GetTargetFirmware() >= TargetFirmware_7_0_0*/ true) { +            if (count <= 0) { +                if ((it != thread_tree.end()) && (it->GetAddressArbiterKey() == addr)) { +                    new_value = value - 2; +                } else { +                    new_value = value + 1; +                } +            } else { +                if ((it != thread_tree.end()) && (it->GetAddressArbiterKey() == addr)) { +                    auto tmp_it = it; +                    s32 tmp_num_waiters{}; +                    while ((++tmp_it != thread_tree.end()) && +                           (tmp_it->GetAddressArbiterKey() == addr)) { +                        if ((tmp_num_waiters++) >= count) { +                            break; +                        } +                    } + +                    if (tmp_num_waiters < count) { +                        new_value = value - 1; +                    } else { +                        new_value = value; +                    } +                } else { +                    new_value = value + 1; +                } +            } +        } else { +            if (count <= 0) { +                if ((it != thread_tree.end()) && (it->GetAddressArbiterKey() == addr)) { +                    new_value = value - 1; +                } else { +                    new_value = value + 1; +                } +            } else { +                auto tmp_it = it; +                s32 tmp_num_waiters{}; +                while ((tmp_it != thread_tree.end()) && (tmp_it->GetAddressArbiterKey() == addr) && +                       (tmp_num_waiters < count + 1)) { +                    ++tmp_num_waiters; +                    ++tmp_it; +                } + +                if (tmp_num_waiters == 0) { +                    new_value = value + 1; +                } else if (tmp_num_waiters <= count) { +                    new_value = value - 1; +                } else { +                    new_value = value; +                } +            } +        } + +        // Check the userspace value. +        s32 user_value{}; +        bool succeeded{}; +        if (value != new_value) { +            succeeded = UpdateIfEqual(system, std::addressof(user_value), addr, value, new_value); +        } else { +            succeeded = ReadFromUser(system, std::addressof(user_value), addr); +        } + +        R_UNLESS(succeeded, Svc::ResultInvalidCurrentMemory); +        R_UNLESS(user_value == value, Svc::ResultInvalidState); + +        while ((it != thread_tree.end()) && (count <= 0 || num_waiters < count) && +               (it->GetAddressArbiterKey() == addr)) { +            Thread* target_thread = std::addressof(*it); +            target_thread->SetSyncedObject(nullptr, RESULT_SUCCESS); + +            ASSERT(target_thread->IsWaitingForAddressArbiter()); +            target_thread->Wakeup(); + +            it = thread_tree.erase(it); +            target_thread->ClearAddressArbiter(); +            ++num_waiters; +        } +    } +    return RESULT_SUCCESS; +} + +ResultCode KAddressArbiter::WaitIfLessThan(VAddr addr, s32 value, bool decrement, s64 timeout) { +    // Prepare to wait. +    Thread* cur_thread = kernel.CurrentScheduler()->GetCurrentThread(); +    Handle timer = InvalidHandle; + +    { +        KScopedSchedulerLockAndSleep slp(kernel, timer, cur_thread, timeout); + +        // Check that the thread isn't terminating. +        if (cur_thread->IsTerminationRequested()) { +            slp.CancelSleep(); +            return Svc::ResultTerminationRequested; +        } + +        // Set the synced object. +        cur_thread->SetSyncedObject(nullptr, Svc::ResultTimedOut); + +        // Read the value from userspace. +        s32 user_value{}; +        bool succeeded{}; +        if (decrement) { +            succeeded = DecrementIfLessThan(system, std::addressof(user_value), addr, value); +        } else { +            succeeded = ReadFromUser(system, std::addressof(user_value), addr); +        } + +        if (!succeeded) { +            slp.CancelSleep(); +            return Svc::ResultInvalidCurrentMemory; +        } + +        // Check that the value is less than the specified one. +        if (user_value >= value) { +            slp.CancelSleep(); +            return Svc::ResultInvalidState; +        } + +        // Check that the timeout is non-zero. +        if (timeout == 0) { +            slp.CancelSleep(); +            return Svc::ResultTimedOut; +        } + +        // Set the arbiter. +        cur_thread->SetAddressArbiter(std::addressof(thread_tree), addr); +        thread_tree.insert(*cur_thread); +        cur_thread->SetState(ThreadState::Waiting); +        cur_thread->SetWaitReasonForDebugging(ThreadWaitReasonForDebugging::Arbitration); +    } + +    // Cancel the timer wait. +    if (timer != InvalidHandle) { +        auto& time_manager = kernel.TimeManager(); +        time_manager.UnscheduleTimeEvent(timer); +    } + +    // Remove from the address arbiter. +    { +        KScopedSchedulerLock sl(kernel); + +        if (cur_thread->IsWaitingForAddressArbiter()) { +            thread_tree.erase(thread_tree.iterator_to(*cur_thread)); +            cur_thread->ClearAddressArbiter(); +        } +    } + +    // Get the result. +    KSynchronizationObject* dummy{}; +    return cur_thread->GetWaitResult(std::addressof(dummy)); +} + +ResultCode KAddressArbiter::WaitIfEqual(VAddr addr, s32 value, s64 timeout) { +    // Prepare to wait. +    Thread* cur_thread = kernel.CurrentScheduler()->GetCurrentThread(); +    Handle timer = InvalidHandle; + +    { +        KScopedSchedulerLockAndSleep slp(kernel, timer, cur_thread, timeout); + +        // Check that the thread isn't terminating. +        if (cur_thread->IsTerminationRequested()) { +            slp.CancelSleep(); +            return Svc::ResultTerminationRequested; +        } + +        // Set the synced object. +        cur_thread->SetSyncedObject(nullptr, Svc::ResultTimedOut); + +        // Read the value from userspace. +        s32 user_value{}; +        if (!ReadFromUser(system, std::addressof(user_value), addr)) { +            slp.CancelSleep(); +            return Svc::ResultInvalidCurrentMemory; +        } + +        // Check that the value is equal. +        if (value != user_value) { +            slp.CancelSleep(); +            return Svc::ResultInvalidState; +        } + +        // Check that the timeout is non-zero. +        if (timeout == 0) { +            slp.CancelSleep(); +            return Svc::ResultTimedOut; +        } + +        // Set the arbiter. +        cur_thread->SetAddressArbiter(std::addressof(thread_tree), addr); +        thread_tree.insert(*cur_thread); +        cur_thread->SetState(ThreadState::Waiting); +        cur_thread->SetWaitReasonForDebugging(ThreadWaitReasonForDebugging::Arbitration); +    } + +    // Cancel the timer wait. +    if (timer != InvalidHandle) { +        auto& time_manager = kernel.TimeManager(); +        time_manager.UnscheduleTimeEvent(timer); +    } + +    // Remove from the address arbiter. +    { +        KScopedSchedulerLock sl(kernel); + +        if (cur_thread->IsWaitingForAddressArbiter()) { +            thread_tree.erase(thread_tree.iterator_to(*cur_thread)); +            cur_thread->ClearAddressArbiter(); +        } +    } + +    // Get the result. +    KSynchronizationObject* dummy{}; +    return cur_thread->GetWaitResult(std::addressof(dummy)); +} + +} // namespace Kernel diff --git a/src/core/hle/kernel/k_address_arbiter.h b/src/core/hle/kernel/k_address_arbiter.h new file mode 100644 index 000000000..8d379b524 --- /dev/null +++ b/src/core/hle/kernel/k_address_arbiter.h @@ -0,0 +1,70 @@ +// Copyright 2021 yuzu Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include "common/assert.h" +#include "common/common_types.h" +#include "core/hle/kernel/k_condition_variable.h" +#include "core/hle/kernel/svc_types.h" + +union ResultCode; + +namespace Core { +class System; +} + +namespace Kernel { + +class KernelCore; + +class KAddressArbiter { +public: +    using ThreadTree = KConditionVariable::ThreadTree; + +    explicit KAddressArbiter(Core::System& system_); +    ~KAddressArbiter(); + +    [[nodiscard]] ResultCode SignalToAddress(VAddr addr, Svc::SignalType type, s32 value, +                                             s32 count) { +        switch (type) { +        case Svc::SignalType::Signal: +            return Signal(addr, count); +        case Svc::SignalType::SignalAndIncrementIfEqual: +            return SignalAndIncrementIfEqual(addr, value, count); +        case Svc::SignalType::SignalAndModifyByWaitingCountIfEqual: +            return SignalAndModifyByWaitingCountIfEqual(addr, value, count); +        } +        UNREACHABLE(); +        return RESULT_UNKNOWN; +    } + +    [[nodiscard]] ResultCode WaitForAddress(VAddr addr, Svc::ArbitrationType type, s32 value, +                                            s64 timeout) { +        switch (type) { +        case Svc::ArbitrationType::WaitIfLessThan: +            return WaitIfLessThan(addr, value, false, timeout); +        case Svc::ArbitrationType::DecrementAndWaitIfLessThan: +            return WaitIfLessThan(addr, value, true, timeout); +        case Svc::ArbitrationType::WaitIfEqual: +            return WaitIfEqual(addr, value, timeout); +        } +        UNREACHABLE(); +        return RESULT_UNKNOWN; +    } + +private: +    [[nodiscard]] ResultCode Signal(VAddr addr, s32 count); +    [[nodiscard]] ResultCode SignalAndIncrementIfEqual(VAddr addr, s32 value, s32 count); +    [[nodiscard]] ResultCode SignalAndModifyByWaitingCountIfEqual(VAddr addr, s32 value, s32 count); +    [[nodiscard]] ResultCode WaitIfLessThan(VAddr addr, s32 value, bool decrement, s64 timeout); +    [[nodiscard]] ResultCode WaitIfEqual(VAddr addr, s32 value, s64 timeout); + +    ThreadTree thread_tree; + +    Core::System& system; +    KernelCore& kernel; +}; + +} // namespace Kernel diff --git a/src/core/hle/kernel/k_condition_variable.cpp b/src/core/hle/kernel/k_condition_variable.cpp new file mode 100644 index 000000000..49a068310 --- /dev/null +++ b/src/core/hle/kernel/k_condition_variable.cpp @@ -0,0 +1,349 @@ +// Copyright 2021 yuzu Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include <vector> + +#include "core/arm/exclusive_monitor.h" +#include "core/core.h" +#include "core/hle/kernel/k_condition_variable.h" +#include "core/hle/kernel/k_scheduler.h" +#include "core/hle/kernel/k_scoped_scheduler_lock_and_sleep.h" +#include "core/hle/kernel/k_synchronization_object.h" +#include "core/hle/kernel/kernel.h" +#include "core/hle/kernel/process.h" +#include "core/hle/kernel/svc_common.h" +#include "core/hle/kernel/svc_results.h" +#include "core/hle/kernel/thread.h" +#include "core/memory.h" + +namespace Kernel { + +namespace { + +bool ReadFromUser(Core::System& system, u32* out, VAddr address) { +    *out = system.Memory().Read32(address); +    return true; +} + +bool WriteToUser(Core::System& system, VAddr address, const u32* p) { +    system.Memory().Write32(address, *p); +    return true; +} + +bool UpdateLockAtomic(Core::System& system, u32* out, VAddr address, u32 if_zero, +                      u32 new_orr_mask) { +    auto& monitor = system.Monitor(); +    const auto current_core = system.CurrentCoreIndex(); + +    // Load the value from the address. +    const auto expected = monitor.ExclusiveRead32(current_core, address); + +    // Orr in the new mask. +    u32 value = expected | new_orr_mask; + +    // If the value is zero, use the if_zero value, otherwise use the newly orr'd value. +    if (!expected) { +        value = if_zero; +    } + +    // Try to store. +    if (!monitor.ExclusiveWrite32(current_core, address, value)) { +        // If we failed to store, try again. +        return UpdateLockAtomic(system, out, address, if_zero, new_orr_mask); +    } + +    // We're done. +    *out = expected; +    return true; +} + +} // namespace + +KConditionVariable::KConditionVariable(Core::System& system_) +    : system{system_}, kernel{system.Kernel()} {} + +KConditionVariable::~KConditionVariable() = default; + +ResultCode KConditionVariable::SignalToAddress(VAddr addr) { +    Thread* owner_thread = kernel.CurrentScheduler()->GetCurrentThread(); + +    // Signal the address. +    { +        KScopedSchedulerLock sl(kernel); + +        // Remove waiter thread. +        s32 num_waiters{}; +        Thread* next_owner_thread = +            owner_thread->RemoveWaiterByKey(std::addressof(num_waiters), addr); + +        // Determine the next tag. +        u32 next_value{}; +        if (next_owner_thread) { +            next_value = next_owner_thread->GetAddressKeyValue(); +            if (num_waiters > 1) { +                next_value |= Svc::HandleWaitMask; +            } + +            next_owner_thread->SetSyncedObject(nullptr, RESULT_SUCCESS); +            next_owner_thread->Wakeup(); +        } + +        // Write the value to userspace. +        if (!WriteToUser(system, addr, std::addressof(next_value))) { +            if (next_owner_thread) { +                next_owner_thread->SetSyncedObject(nullptr, Svc::ResultInvalidCurrentMemory); +            } + +            return Svc::ResultInvalidCurrentMemory; +        } +    } + +    return RESULT_SUCCESS; +} + +ResultCode KConditionVariable::WaitForAddress(Handle handle, VAddr addr, u32 value) { +    Thread* cur_thread = kernel.CurrentScheduler()->GetCurrentThread(); + +    // Wait for the address. +    { +        std::shared_ptr<Thread> owner_thread; +        ASSERT(!owner_thread); +        { +            KScopedSchedulerLock sl(kernel); +            cur_thread->SetSyncedObject(nullptr, RESULT_SUCCESS); + +            // Check if the thread should terminate. +            R_UNLESS(!cur_thread->IsTerminationRequested(), Svc::ResultTerminationRequested); + +            { +                // Read the tag from userspace. +                u32 test_tag{}; +                R_UNLESS(ReadFromUser(system, std::addressof(test_tag), addr), +                         Svc::ResultInvalidCurrentMemory); + +                // If the tag isn't the handle (with wait mask), we're done. +                R_UNLESS(test_tag == (handle | Svc::HandleWaitMask), RESULT_SUCCESS); + +                // Get the lock owner thread. +                owner_thread = kernel.CurrentProcess()->GetHandleTable().Get<Thread>(handle); +                R_UNLESS(owner_thread, Svc::ResultInvalidHandle); + +                // Update the lock. +                cur_thread->SetAddressKey(addr, value); +                owner_thread->AddWaiter(cur_thread); +                cur_thread->SetState(ThreadState::Waiting); +                cur_thread->SetWaitReasonForDebugging(ThreadWaitReasonForDebugging::ConditionVar); +                cur_thread->SetMutexWaitAddressForDebugging(addr); +            } +        } +        ASSERT(owner_thread); +    } + +    // Remove the thread as a waiter from the lock owner. +    { +        KScopedSchedulerLock sl(kernel); +        Thread* owner_thread = cur_thread->GetLockOwner(); +        if (owner_thread != nullptr) { +            owner_thread->RemoveWaiter(cur_thread); +        } +    } + +    // Get the wait result. +    KSynchronizationObject* dummy{}; +    return cur_thread->GetWaitResult(std::addressof(dummy)); +} + +Thread* KConditionVariable::SignalImpl(Thread* thread) { +    // Check pre-conditions. +    ASSERT(kernel.GlobalSchedulerContext().IsLocked()); + +    // Update the tag. +    VAddr address = thread->GetAddressKey(); +    u32 own_tag = thread->GetAddressKeyValue(); + +    u32 prev_tag{}; +    bool can_access{}; +    { +        // TODO(bunnei): We should disable interrupts here via KScopedInterruptDisable. +        // TODO(bunnei): We should call CanAccessAtomic(..) here. +        can_access = true; +        if (can_access) { +            UpdateLockAtomic(system, std::addressof(prev_tag), address, own_tag, +                             Svc::HandleWaitMask); +        } +    } + +    Thread* thread_to_close = nullptr; +    if (can_access) { +        if (prev_tag == InvalidHandle) { +            // If nobody held the lock previously, we're all good. +            thread->SetSyncedObject(nullptr, RESULT_SUCCESS); +            thread->Wakeup(); +        } else { +            // Get the previous owner. +            auto owner_thread = kernel.CurrentProcess()->GetHandleTable().Get<Thread>( +                prev_tag & ~Svc::HandleWaitMask); + +            if (owner_thread) { +                // Add the thread as a waiter on the owner. +                owner_thread->AddWaiter(thread); +                thread_to_close = owner_thread.get(); +            } else { +                // The lock was tagged with a thread that doesn't exist. +                thread->SetSyncedObject(nullptr, Svc::ResultInvalidState); +                thread->Wakeup(); +            } +        } +    } else { +        // If the address wasn't accessible, note so. +        thread->SetSyncedObject(nullptr, Svc::ResultInvalidCurrentMemory); +        thread->Wakeup(); +    } + +    return thread_to_close; +} + +void KConditionVariable::Signal(u64 cv_key, s32 count) { +    // Prepare for signaling. +    constexpr int MaxThreads = 16; + +    // TODO(bunnei): This should just be Thread once we implement KAutoObject instead of using +    // std::shared_ptr. +    std::vector<std::shared_ptr<Thread>> thread_list; +    std::array<Thread*, MaxThreads> thread_array; +    s32 num_to_close{}; + +    // Perform signaling. +    s32 num_waiters{}; +    { +        KScopedSchedulerLock sl(kernel); + +        auto it = thread_tree.nfind_light({cv_key, -1}); +        while ((it != thread_tree.end()) && (count <= 0 || num_waiters < count) && +               (it->GetConditionVariableKey() == cv_key)) { +            Thread* target_thread = std::addressof(*it); + +            if (Thread* thread = SignalImpl(target_thread); thread != nullptr) { +                if (num_to_close < MaxThreads) { +                    thread_array[num_to_close++] = thread; +                } else { +                    thread_list.push_back(SharedFrom(thread)); +                } +            } + +            it = thread_tree.erase(it); +            target_thread->ClearConditionVariable(); +            ++num_waiters; +        } + +        // If we have no waiters, clear the has waiter flag. +        if (it == thread_tree.end() || it->GetConditionVariableKey() != cv_key) { +            const u32 has_waiter_flag{}; +            WriteToUser(system, cv_key, std::addressof(has_waiter_flag)); +        } +    } + +    // Close threads in the array. +    for (auto i = 0; i < num_to_close; ++i) { +        thread_array[i]->Close(); +    } + +    // Close threads in the list. +    for (auto it = thread_list.begin(); it != thread_list.end(); it = thread_list.erase(it)) { +        (*it)->Close(); +    } +} + +ResultCode KConditionVariable::Wait(VAddr addr, u64 key, u32 value, s64 timeout) { +    // Prepare to wait. +    Thread* cur_thread = kernel.CurrentScheduler()->GetCurrentThread(); +    Handle timer = InvalidHandle; + +    { +        KScopedSchedulerLockAndSleep slp(kernel, timer, cur_thread, timeout); + +        // Set the synced object. +        cur_thread->SetSyncedObject(nullptr, Svc::ResultTimedOut); + +        // Check that the thread isn't terminating. +        if (cur_thread->IsTerminationRequested()) { +            slp.CancelSleep(); +            return Svc::ResultTerminationRequested; +        } + +        // Update the value and process for the next owner. +        { +            // Remove waiter thread. +            s32 num_waiters{}; +            Thread* next_owner_thread = +                cur_thread->RemoveWaiterByKey(std::addressof(num_waiters), addr); + +            // Update for the next owner thread. +            u32 next_value{}; +            if (next_owner_thread != nullptr) { +                // Get the next tag value. +                next_value = next_owner_thread->GetAddressKeyValue(); +                if (num_waiters > 1) { +                    next_value |= Svc::HandleWaitMask; +                } + +                // Wake up the next owner. +                next_owner_thread->SetSyncedObject(nullptr, RESULT_SUCCESS); +                next_owner_thread->Wakeup(); +            } + +            // Write to the cv key. +            { +                const u32 has_waiter_flag = 1; +                WriteToUser(system, key, std::addressof(has_waiter_flag)); +                // TODO(bunnei): We should call DataMemoryBarrier(..) here. +            } + +            // Write the value to userspace. +            if (!WriteToUser(system, addr, std::addressof(next_value))) { +                slp.CancelSleep(); +                return Svc::ResultInvalidCurrentMemory; +            } +        } + +        // Update condition variable tracking. +        { +            cur_thread->SetConditionVariable(std::addressof(thread_tree), addr, key, value); +            thread_tree.insert(*cur_thread); +        } + +        // If the timeout is non-zero, set the thread as waiting. +        if (timeout != 0) { +            cur_thread->SetState(ThreadState::Waiting); +            cur_thread->SetWaitReasonForDebugging(ThreadWaitReasonForDebugging::ConditionVar); +            cur_thread->SetMutexWaitAddressForDebugging(addr); +        } +    } + +    // Cancel the timer wait. +    if (timer != InvalidHandle) { +        auto& time_manager = kernel.TimeManager(); +        time_manager.UnscheduleTimeEvent(timer); +    } + +    // Remove from the condition variable. +    { +        KScopedSchedulerLock sl(kernel); + +        if (Thread* owner = cur_thread->GetLockOwner(); owner != nullptr) { +            owner->RemoveWaiter(cur_thread); +        } + +        if (cur_thread->IsWaitingForConditionVariable()) { +            thread_tree.erase(thread_tree.iterator_to(*cur_thread)); +            cur_thread->ClearConditionVariable(); +        } +    } + +    // Get the result. +    KSynchronizationObject* dummy{}; +    return cur_thread->GetWaitResult(std::addressof(dummy)); +} + +} // namespace Kernel diff --git a/src/core/hle/kernel/k_condition_variable.h b/src/core/hle/kernel/k_condition_variable.h new file mode 100644 index 000000000..98ed5b323 --- /dev/null +++ b/src/core/hle/kernel/k_condition_variable.h @@ -0,0 +1,59 @@ +// Copyright 2021 yuzu Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include "common/assert.h" +#include "common/common_types.h" + +#include "core/hle/kernel/k_scheduler.h" +#include "core/hle/kernel/kernel.h" +#include "core/hle/kernel/thread.h" +#include "core/hle/result.h" + +namespace Core { +class System; +} + +namespace Kernel { + +class KConditionVariable { +public: +    using ThreadTree = typename Thread::ConditionVariableThreadTreeType; + +    explicit KConditionVariable(Core::System& system_); +    ~KConditionVariable(); + +    // Arbitration +    [[nodiscard]] ResultCode SignalToAddress(VAddr addr); +    [[nodiscard]] ResultCode WaitForAddress(Handle handle, VAddr addr, u32 value); + +    // Condition variable +    void Signal(u64 cv_key, s32 count); +    [[nodiscard]] ResultCode Wait(VAddr addr, u64 key, u32 value, s64 timeout); + +private: +    [[nodiscard]] Thread* SignalImpl(Thread* thread); + +    ThreadTree thread_tree; + +    Core::System& system; +    KernelCore& kernel; +}; + +inline void BeforeUpdatePriority(const KernelCore& kernel, KConditionVariable::ThreadTree* tree, +                                 Thread* thread) { +    ASSERT(kernel.GlobalSchedulerContext().IsLocked()); + +    tree->erase(tree->iterator_to(*thread)); +} + +inline void AfterUpdatePriority(const KernelCore& kernel, KConditionVariable::ThreadTree* tree, +                                Thread* thread) { +    ASSERT(kernel.GlobalSchedulerContext().IsLocked()); + +    tree->insert(*thread); +} + +} // namespace Kernel diff --git a/src/core/hle/kernel/k_scheduler.cpp b/src/core/hle/kernel/k_scheduler.cpp index c5fd82a6b..42f0ea483 100644 --- a/src/core/hle/kernel/k_scheduler.cpp +++ b/src/core/hle/kernel/k_scheduler.cpp @@ -180,22 +180,22 @@ u64 KScheduler::UpdateHighestPriorityThreadsImpl(KernelCore& kernel) {      return cores_needing_scheduling;  } -void KScheduler::OnThreadStateChanged(KernelCore& kernel, Thread* thread, u32 old_state) { +void KScheduler::OnThreadStateChanged(KernelCore& kernel, Thread* thread, ThreadState old_state) {      ASSERT(kernel.GlobalSchedulerContext().IsLocked());      // Check if the state has changed, because if it hasn't there's nothing to do. -    const auto cur_state = thread->scheduling_state; +    const auto cur_state = thread->GetRawState();      if (cur_state == old_state) {          return;      }      // Update the priority queues. -    if (old_state == static_cast<u32>(ThreadSchedStatus::Runnable)) { +    if (old_state == ThreadState::Runnable) {          // If we were previously runnable, then we're not runnable now, and we should remove.          GetPriorityQueue(kernel).Remove(thread);          IncrementScheduledCount(thread);          SetSchedulerUpdateNeeded(kernel); -    } else if (cur_state == static_cast<u32>(ThreadSchedStatus::Runnable)) { +    } else if (cur_state == ThreadState::Runnable) {          // If we're now runnable, then we weren't previously, and we should add.          GetPriorityQueue(kernel).PushBack(thread);          IncrementScheduledCount(thread); @@ -203,13 +203,11 @@ void KScheduler::OnThreadStateChanged(KernelCore& kernel, Thread* thread, u32 ol      }  } -void KScheduler::OnThreadPriorityChanged(KernelCore& kernel, Thread* thread, Thread* current_thread, -                                         u32 old_priority) { - +void KScheduler::OnThreadPriorityChanged(KernelCore& kernel, Thread* thread, s32 old_priority) {      ASSERT(kernel.GlobalSchedulerContext().IsLocked());      // If the thread is runnable, we want to change its priority in the queue. -    if (thread->scheduling_state == static_cast<u32>(ThreadSchedStatus::Runnable)) { +    if (thread->GetRawState() == ThreadState::Runnable) {          GetPriorityQueue(kernel).ChangePriority(              old_priority, thread == kernel.CurrentScheduler()->GetCurrentThread(), thread);          IncrementScheduledCount(thread); @@ -222,7 +220,7 @@ void KScheduler::OnThreadAffinityMaskChanged(KernelCore& kernel, Thread* thread,      ASSERT(kernel.GlobalSchedulerContext().IsLocked());      // If the thread is runnable, we want to change its affinity in the queue. -    if (thread->scheduling_state == static_cast<u32>(ThreadSchedStatus::Runnable)) { +    if (thread->GetRawState() == ThreadState::Runnable) {          GetPriorityQueue(kernel).ChangeAffinityMask(old_core, old_affinity, thread);          IncrementScheduledCount(thread);          SetSchedulerUpdateNeeded(kernel); @@ -292,7 +290,7 @@ void KScheduler::RotateScheduledQueue(s32 core_id, s32 priority) {          // If the best thread we can choose has a priority the same or worse than ours, try to          // migrate a higher priority thread. -        if (best_thread != nullptr && best_thread->GetPriority() >= static_cast<u32>(priority)) { +        if (best_thread != nullptr && best_thread->GetPriority() >= priority) {              Thread* suggested = priority_queue.GetSuggestedFront(core_id);              while (suggested != nullptr) {                  // If the suggestion's priority is the same as ours, don't bother. @@ -395,8 +393,8 @@ void KScheduler::YieldWithoutCoreMigration() {      {          KScopedSchedulerLock lock(kernel); -        const auto cur_state = cur_thread.scheduling_state; -        if (cur_state == static_cast<u32>(ThreadSchedStatus::Runnable)) { +        const auto cur_state = cur_thread.GetRawState(); +        if (cur_state == ThreadState::Runnable) {              // Put the current thread at the back of the queue.              Thread* next_thread = priority_queue.MoveToScheduledBack(std::addressof(cur_thread));              IncrementScheduledCount(std::addressof(cur_thread)); @@ -436,8 +434,8 @@ void KScheduler::YieldWithCoreMigration() {      {          KScopedSchedulerLock lock(kernel); -        const auto cur_state = cur_thread.scheduling_state; -        if (cur_state == static_cast<u32>(ThreadSchedStatus::Runnable)) { +        const auto cur_state = cur_thread.GetRawState(); +        if (cur_state == ThreadState::Runnable) {              // Get the current active core.              const s32 core_id = cur_thread.GetActiveCore(); @@ -526,8 +524,8 @@ void KScheduler::YieldToAnyThread() {      {          KScopedSchedulerLock lock(kernel); -        const auto cur_state = cur_thread.scheduling_state; -        if (cur_state == static_cast<u32>(ThreadSchedStatus::Runnable)) { +        const auto cur_state = cur_thread.GetRawState(); +        if (cur_state == ThreadState::Runnable) {              // Get the current active core.              const s32 core_id = cur_thread.GetActiveCore(); @@ -645,8 +643,7 @@ void KScheduler::Unload(Thread* thread) {  void KScheduler::Reload(Thread* thread) {      if (thread) { -        ASSERT_MSG(thread->GetSchedulingStatus() == ThreadSchedStatus::Runnable, -                   "Thread must be runnable."); +        ASSERT_MSG(thread->GetState() == ThreadState::Runnable, "Thread must be runnable.");          // Cancel any outstanding wakeup events for this thread          thread->SetIsRunning(true); @@ -725,7 +722,7 @@ void KScheduler::SwitchToCurrent() {          do {              if (current_thread != nullptr && !current_thread->IsHLEThread()) {                  current_thread->context_guard.lock(); -                if (!current_thread->IsRunnable()) { +                if (current_thread->GetRawState() != ThreadState::Runnable) {                      current_thread->context_guard.unlock();                      break;                  } @@ -772,7 +769,7 @@ void KScheduler::Initialize() {      {          KScopedSchedulerLock lock{system.Kernel()}; -        idle_thread->SetStatus(ThreadStatus::Ready); +        idle_thread->SetState(ThreadState::Runnable);      }  } diff --git a/src/core/hle/kernel/k_scheduler.h b/src/core/hle/kernel/k_scheduler.h index e84abc84c..783665123 100644 --- a/src/core/hle/kernel/k_scheduler.h +++ b/src/core/hle/kernel/k_scheduler.h @@ -100,11 +100,10 @@ public:      void YieldToAnyThread();      /// Notify the scheduler a thread's status has changed. -    static void OnThreadStateChanged(KernelCore& kernel, Thread* thread, u32 old_state); +    static void OnThreadStateChanged(KernelCore& kernel, Thread* thread, ThreadState old_state);      /// Notify the scheduler a thread's priority has changed. -    static void OnThreadPriorityChanged(KernelCore& kernel, Thread* thread, Thread* current_thread, -                                        u32 old_priority); +    static void OnThreadPriorityChanged(KernelCore& kernel, Thread* thread, s32 old_priority);      /// Notify the scheduler a thread's core and/or affinity mask has changed.      static void OnThreadAffinityMaskChanged(KernelCore& kernel, Thread* thread, diff --git a/src/core/hle/kernel/k_scheduler_lock.h b/src/core/hle/kernel/k_scheduler_lock.h index 2f1c1f691..9b40bd22c 100644 --- a/src/core/hle/kernel/k_scheduler_lock.h +++ b/src/core/hle/kernel/k_scheduler_lock.h @@ -19,7 +19,7 @@ class KernelCore;  template <typename SchedulerType>  class KAbstractSchedulerLock {  public: -    explicit KAbstractSchedulerLock(KernelCore& kernel) : kernel{kernel} {} +    explicit KAbstractSchedulerLock(KernelCore& kernel_) : kernel{kernel_} {}      bool IsLockedByCurrentThread() const {          return this->owner_thread == kernel.GetCurrentEmuThreadID(); diff --git a/src/core/hle/kernel/k_synchronization_object.cpp b/src/core/hle/kernel/k_synchronization_object.cpp new file mode 100644 index 000000000..1c508cb55 --- /dev/null +++ b/src/core/hle/kernel/k_synchronization_object.cpp @@ -0,0 +1,172 @@ +// Copyright 2021 yuzu Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include "common/assert.h" +#include "common/common_types.h" +#include "core/hle/kernel/k_scheduler.h" +#include "core/hle/kernel/k_scoped_scheduler_lock_and_sleep.h" +#include "core/hle/kernel/k_synchronization_object.h" +#include "core/hle/kernel/kernel.h" +#include "core/hle/kernel/svc_results.h" +#include "core/hle/kernel/thread.h" + +namespace Kernel { + +ResultCode KSynchronizationObject::Wait(KernelCore& kernel, s32* out_index, +                                        KSynchronizationObject** objects, const s32 num_objects, +                                        s64 timeout) { +    // Allocate space on stack for thread nodes. +    std::vector<ThreadListNode> thread_nodes(num_objects); + +    // Prepare for wait. +    Thread* thread = kernel.CurrentScheduler()->GetCurrentThread(); +    Handle timer = InvalidHandle; + +    { +        // Setup the scheduling lock and sleep. +        KScopedSchedulerLockAndSleep slp(kernel, timer, thread, timeout); + +        // Check if any of the objects are already signaled. +        for (auto i = 0; i < num_objects; ++i) { +            ASSERT(objects[i] != nullptr); + +            if (objects[i]->IsSignaled()) { +                *out_index = i; +                slp.CancelSleep(); +                return RESULT_SUCCESS; +            } +        } + +        // Check if the timeout is zero. +        if (timeout == 0) { +            slp.CancelSleep(); +            return Svc::ResultTimedOut; +        } + +        // Check if the thread should terminate. +        if (thread->IsTerminationRequested()) { +            slp.CancelSleep(); +            return Svc::ResultTerminationRequested; +        } + +        // Check if waiting was canceled. +        if (thread->IsWaitCancelled()) { +            slp.CancelSleep(); +            thread->ClearWaitCancelled(); +            return Svc::ResultCancelled; +        } + +        // Add the waiters. +        for (auto i = 0; i < num_objects; ++i) { +            thread_nodes[i].thread = thread; +            thread_nodes[i].next = nullptr; + +            if (objects[i]->thread_list_tail == nullptr) { +                objects[i]->thread_list_head = std::addressof(thread_nodes[i]); +            } else { +                objects[i]->thread_list_tail->next = std::addressof(thread_nodes[i]); +            } + +            objects[i]->thread_list_tail = std::addressof(thread_nodes[i]); +        } + +        // For debugging only +        thread->SetWaitObjectsForDebugging({objects, static_cast<std::size_t>(num_objects)}); + +        // Mark the thread as waiting. +        thread->SetCancellable(); +        thread->SetSyncedObject(nullptr, Svc::ResultTimedOut); +        thread->SetState(ThreadState::Waiting); +        thread->SetWaitReasonForDebugging(ThreadWaitReasonForDebugging::Synchronization); +    } + +    // The lock/sleep is done, so we should be able to get our result. + +    // Thread is no longer cancellable. +    thread->ClearCancellable(); + +    // For debugging only +    thread->SetWaitObjectsForDebugging({}); + +    // Cancel the timer as needed. +    if (timer != InvalidHandle) { +        auto& time_manager = kernel.TimeManager(); +        time_manager.UnscheduleTimeEvent(timer); +    } + +    // Get the wait result. +    ResultCode wait_result{RESULT_SUCCESS}; +    s32 sync_index = -1; +    { +        KScopedSchedulerLock lock(kernel); +        KSynchronizationObject* synced_obj; +        wait_result = thread->GetWaitResult(std::addressof(synced_obj)); + +        for (auto i = 0; i < num_objects; ++i) { +            // Unlink the object from the list. +            ThreadListNode* prev_ptr = +                reinterpret_cast<ThreadListNode*>(std::addressof(objects[i]->thread_list_head)); +            ThreadListNode* prev_val = nullptr; +            ThreadListNode *prev, *tail_prev; + +            do { +                prev = prev_ptr; +                prev_ptr = prev_ptr->next; +                tail_prev = prev_val; +                prev_val = prev_ptr; +            } while (prev_ptr != std::addressof(thread_nodes[i])); + +            if (objects[i]->thread_list_tail == std::addressof(thread_nodes[i])) { +                objects[i]->thread_list_tail = tail_prev; +            } + +            prev->next = thread_nodes[i].next; + +            if (objects[i] == synced_obj) { +                sync_index = i; +            } +        } +    } + +    // Set output. +    *out_index = sync_index; +    return wait_result; +} + +KSynchronizationObject::KSynchronizationObject(KernelCore& kernel) : Object{kernel} {} + +KSynchronizationObject ::~KSynchronizationObject() = default; + +void KSynchronizationObject::NotifyAvailable(ResultCode result) { +    KScopedSchedulerLock lock(kernel); + +    // If we're not signaled, we've nothing to notify. +    if (!this->IsSignaled()) { +        return; +    } + +    // Iterate over each thread. +    for (auto* cur_node = thread_list_head; cur_node != nullptr; cur_node = cur_node->next) { +        Thread* thread = cur_node->thread; +        if (thread->GetState() == ThreadState::Waiting) { +            thread->SetSyncedObject(this, result); +            thread->SetState(ThreadState::Runnable); +        } +    } +} + +std::vector<Thread*> KSynchronizationObject::GetWaitingThreadsForDebugging() const { +    std::vector<Thread*> threads; + +    // If debugging, dump the list of waiters. +    { +        KScopedSchedulerLock lock(kernel); +        for (auto* cur_node = thread_list_head; cur_node != nullptr; cur_node = cur_node->next) { +            threads.emplace_back(cur_node->thread); +        } +    } + +    return threads; +} +} // namespace Kernel diff --git a/src/core/hle/kernel/k_synchronization_object.h b/src/core/hle/kernel/k_synchronization_object.h new file mode 100644 index 000000000..14d80ebf1 --- /dev/null +++ b/src/core/hle/kernel/k_synchronization_object.h @@ -0,0 +1,58 @@ +// Copyright 2021 yuzu Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include <vector> + +#include "core/hle/kernel/object.h" +#include "core/hle/result.h" + +namespace Kernel { + +class KernelCore; +class Synchronization; +class Thread; + +/// Class that represents a Kernel object that a thread can be waiting on +class KSynchronizationObject : public Object { +public: +    struct ThreadListNode { +        ThreadListNode* next{}; +        Thread* thread{}; +    }; + +    [[nodiscard]] static ResultCode Wait(KernelCore& kernel, s32* out_index, +                                         KSynchronizationObject** objects, const s32 num_objects, +                                         s64 timeout); + +    [[nodiscard]] virtual bool IsSignaled() const = 0; + +    [[nodiscard]] std::vector<Thread*> GetWaitingThreadsForDebugging() const; + +protected: +    explicit KSynchronizationObject(KernelCore& kernel); +    virtual ~KSynchronizationObject(); + +    void NotifyAvailable(ResultCode result); +    void NotifyAvailable() { +        return this->NotifyAvailable(RESULT_SUCCESS); +    } + +private: +    ThreadListNode* thread_list_head{}; +    ThreadListNode* thread_list_tail{}; +}; + +// Specialization of DynamicObjectCast for KSynchronizationObjects +template <> +inline std::shared_ptr<KSynchronizationObject> DynamicObjectCast<KSynchronizationObject>( +    std::shared_ptr<Object> object) { +    if (object != nullptr && object->IsWaitable()) { +        return std::static_pointer_cast<KSynchronizationObject>(object); +    } +    return nullptr; +} + +} // namespace Kernel diff --git a/src/core/hle/kernel/kernel.cpp b/src/core/hle/kernel/kernel.cpp index e8ece8164..c0ff287a6 100644 --- a/src/core/hle/kernel/kernel.cpp +++ b/src/core/hle/kernel/kernel.cpp @@ -38,7 +38,6 @@  #include "core/hle/kernel/resource_limit.h"  #include "core/hle/kernel/service_thread.h"  #include "core/hle/kernel/shared_memory.h" -#include "core/hle/kernel/synchronization.h"  #include "core/hle/kernel/thread.h"  #include "core/hle/kernel/time_manager.h"  #include "core/hle/lock.h" @@ -51,8 +50,7 @@ namespace Kernel {  struct KernelCore::Impl {      explicit Impl(Core::System& system, KernelCore& kernel) -        : synchronization{system}, time_manager{system}, global_handle_table{kernel}, system{ -                                                                                          system} {} +        : time_manager{system}, global_handle_table{kernel}, system{system} {}      void SetMulticore(bool is_multicore) {          this->is_multicore = is_multicore; @@ -307,7 +305,6 @@ struct KernelCore::Impl {      std::vector<std::shared_ptr<Process>> process_list;      Process* current_process = nullptr;      std::unique_ptr<Kernel::GlobalSchedulerContext> global_scheduler_context; -    Kernel::Synchronization synchronization;      Kernel::TimeManager time_manager;      std::shared_ptr<ResourceLimit> system_resource_limit; @@ -461,14 +458,6 @@ const std::array<Core::CPUInterruptHandler, Core::Hardware::NUM_CPU_CORES>& Kern      return impl->interrupts;  } -Kernel::Synchronization& KernelCore::Synchronization() { -    return impl->synchronization; -} - -const Kernel::Synchronization& KernelCore::Synchronization() const { -    return impl->synchronization; -} -  Kernel::TimeManager& KernelCore::TimeManager() {      return impl->time_manager;  } @@ -613,9 +602,11 @@ void KernelCore::Suspend(bool in_suspention) {      const bool should_suspend = exception_exited || in_suspention;      {          KScopedSchedulerLock lock(*this); -        ThreadStatus status = should_suspend ? ThreadStatus::Ready : ThreadStatus::WaitSleep; +        const auto state = should_suspend ? ThreadState::Runnable : ThreadState::Waiting;          for (std::size_t i = 0; i < Core::Hardware::NUM_CPU_CORES; i++) { -            impl->suspend_threads[i]->SetStatus(status); +            impl->suspend_threads[i]->SetState(state); +            impl->suspend_threads[i]->SetWaitReasonForDebugging( +                ThreadWaitReasonForDebugging::Suspended);          }      }  } diff --git a/src/core/hle/kernel/kernel.h b/src/core/hle/kernel/kernel.h index e3169f5a7..933d9a7d6 100644 --- a/src/core/hle/kernel/kernel.h +++ b/src/core/hle/kernel/kernel.h @@ -33,7 +33,6 @@ template <typename T>  class SlabHeap;  } // namespace Memory -class AddressArbiter;  class ClientPort;  class GlobalSchedulerContext;  class HandleTable; @@ -129,12 +128,6 @@ public:      /// Gets the an instance of the current physical CPU core.      const Kernel::PhysicalCore& CurrentPhysicalCore() const; -    /// Gets the an instance of the Synchronization Interface. -    Kernel::Synchronization& Synchronization(); - -    /// Gets the an instance of the Synchronization Interface. -    const Kernel::Synchronization& Synchronization() const; -      /// Gets the an instance of the TimeManager Interface.      Kernel::TimeManager& TimeManager(); diff --git a/src/core/hle/kernel/memory/memory_layout.h b/src/core/hle/kernel/memory/memory_layout.h index 9b3d6267a..c7c0b2f49 100644 --- a/src/core/hle/kernel/memory/memory_layout.h +++ b/src/core/hle/kernel/memory/memory_layout.h @@ -5,9 +5,28 @@  #pragma once  #include "common/common_types.h" +#include "core/device_memory.h"  namespace Kernel::Memory { +constexpr std::size_t KernelAslrAlignment = 2 * 1024 * 1024; +constexpr std::size_t KernelVirtualAddressSpaceWidth = 1ULL << 39; +constexpr std::size_t KernelPhysicalAddressSpaceWidth = 1ULL << 48; +constexpr std::size_t KernelVirtualAddressSpaceBase = 0ULL - KernelVirtualAddressSpaceWidth; +constexpr std::size_t KernelVirtualAddressSpaceEnd = +    KernelVirtualAddressSpaceBase + (KernelVirtualAddressSpaceWidth - KernelAslrAlignment); +constexpr std::size_t KernelVirtualAddressSpaceLast = KernelVirtualAddressSpaceEnd - 1; +constexpr std::size_t KernelVirtualAddressSpaceSize = +    KernelVirtualAddressSpaceEnd - KernelVirtualAddressSpaceBase; + +constexpr bool IsKernelAddressKey(VAddr key) { +    return KernelVirtualAddressSpaceBase <= key && key <= KernelVirtualAddressSpaceLast; +} + +constexpr bool IsKernelAddress(VAddr address) { +    return KernelVirtualAddressSpaceBase <= address && address < KernelVirtualAddressSpaceEnd; +} +  class MemoryRegion final {      friend class MemoryLayout; diff --git a/src/core/hle/kernel/mutex.cpp b/src/core/hle/kernel/mutex.cpp deleted file mode 100644 index 4f8075e0e..000000000 --- a/src/core/hle/kernel/mutex.cpp +++ /dev/null @@ -1,170 +0,0 @@ -// Copyright 2014 Citra Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. - -#include <memory> -#include <utility> -#include <vector> - -#include "common/assert.h" -#include "common/logging/log.h" -#include "core/core.h" -#include "core/hle/kernel/errors.h" -#include "core/hle/kernel/handle_table.h" -#include "core/hle/kernel/k_scheduler.h" -#include "core/hle/kernel/kernel.h" -#include "core/hle/kernel/mutex.h" -#include "core/hle/kernel/object.h" -#include "core/hle/kernel/process.h" -#include "core/hle/kernel/thread.h" -#include "core/hle/result.h" -#include "core/memory.h" - -namespace Kernel { - -/// Returns the number of threads that are waiting for a mutex, and the highest priority one among -/// those. -static std::pair<std::shared_ptr<Thread>, u32> GetHighestPriorityMutexWaitingThread( -    const std::shared_ptr<Thread>& current_thread, VAddr mutex_addr) { - -    std::shared_ptr<Thread> highest_priority_thread; -    u32 num_waiters = 0; - -    for (const auto& thread : current_thread->GetMutexWaitingThreads()) { -        if (thread->GetMutexWaitAddress() != mutex_addr) -            continue; - -        ++num_waiters; -        if (highest_priority_thread == nullptr || -            thread->GetPriority() < highest_priority_thread->GetPriority()) { -            highest_priority_thread = thread; -        } -    } - -    return {highest_priority_thread, num_waiters}; -} - -/// Update the mutex owner field of all threads waiting on the mutex to point to the new owner. -static void TransferMutexOwnership(VAddr mutex_addr, std::shared_ptr<Thread> current_thread, -                                   std::shared_ptr<Thread> new_owner) { -    current_thread->RemoveMutexWaiter(new_owner); -    const auto threads = current_thread->GetMutexWaitingThreads(); -    for (const auto& thread : threads) { -        if (thread->GetMutexWaitAddress() != mutex_addr) -            continue; - -        ASSERT(thread->GetLockOwner() == current_thread.get()); -        current_thread->RemoveMutexWaiter(thread); -        if (new_owner != thread) -            new_owner->AddMutexWaiter(thread); -    } -} - -Mutex::Mutex(Core::System& system) : system{system} {} -Mutex::~Mutex() = default; - -ResultCode Mutex::TryAcquire(VAddr address, Handle holding_thread_handle, -                             Handle requesting_thread_handle) { -    // The mutex address must be 4-byte aligned -    if ((address % sizeof(u32)) != 0) { -        LOG_ERROR(Kernel, "Address is not 4-byte aligned! address={:016X}", address); -        return ERR_INVALID_ADDRESS; -    } - -    auto& kernel = system.Kernel(); -    std::shared_ptr<Thread> current_thread = -        SharedFrom(kernel.CurrentScheduler()->GetCurrentThread()); -    { -        KScopedSchedulerLock lock(kernel); -        // The mutex address must be 4-byte aligned -        if ((address % sizeof(u32)) != 0) { -            return ERR_INVALID_ADDRESS; -        } - -        const auto& handle_table = kernel.CurrentProcess()->GetHandleTable(); -        std::shared_ptr<Thread> holding_thread = handle_table.Get<Thread>(holding_thread_handle); -        std::shared_ptr<Thread> requesting_thread = -            handle_table.Get<Thread>(requesting_thread_handle); - -        // TODO(Subv): It is currently unknown if it is possible to lock a mutex in behalf of -        // another thread. -        ASSERT(requesting_thread == current_thread); - -        current_thread->SetSynchronizationResults(nullptr, RESULT_SUCCESS); - -        const u32 addr_value = system.Memory().Read32(address); - -        // If the mutex isn't being held, just return success. -        if (addr_value != (holding_thread_handle | Mutex::MutexHasWaitersFlag)) { -            return RESULT_SUCCESS; -        } - -        if (holding_thread == nullptr) { -            return ERR_INVALID_HANDLE; -        } - -        // Wait until the mutex is released -        current_thread->SetMutexWaitAddress(address); -        current_thread->SetWaitHandle(requesting_thread_handle); - -        current_thread->SetStatus(ThreadStatus::WaitMutex); - -        // Update the lock holder thread's priority to prevent priority inversion. -        holding_thread->AddMutexWaiter(current_thread); -    } - -    { -        KScopedSchedulerLock lock(kernel); -        auto* owner = current_thread->GetLockOwner(); -        if (owner != nullptr) { -            owner->RemoveMutexWaiter(current_thread); -        } -    } -    return current_thread->GetSignalingResult(); -} - -std::pair<ResultCode, std::shared_ptr<Thread>> Mutex::Unlock(std::shared_ptr<Thread> owner, -                                                             VAddr address) { -    // The mutex address must be 4-byte aligned -    if ((address % sizeof(u32)) != 0) { -        LOG_ERROR(Kernel, "Address is not 4-byte aligned! address={:016X}", address); -        return {ERR_INVALID_ADDRESS, nullptr}; -    } - -    auto [new_owner, num_waiters] = GetHighestPriorityMutexWaitingThread(owner, address); -    if (new_owner == nullptr) { -        system.Memory().Write32(address, 0); -        return {RESULT_SUCCESS, nullptr}; -    } -    // Transfer the ownership of the mutex from the previous owner to the new one. -    TransferMutexOwnership(address, owner, new_owner); -    u32 mutex_value = new_owner->GetWaitHandle(); -    if (num_waiters >= 2) { -        // Notify the guest that there are still some threads waiting for the mutex -        mutex_value |= Mutex::MutexHasWaitersFlag; -    } -    new_owner->SetSynchronizationResults(nullptr, RESULT_SUCCESS); -    new_owner->SetLockOwner(nullptr); -    new_owner->ResumeFromWait(); - -    system.Memory().Write32(address, mutex_value); -    return {RESULT_SUCCESS, new_owner}; -} - -ResultCode Mutex::Release(VAddr address) { -    auto& kernel = system.Kernel(); -    KScopedSchedulerLock lock(kernel); - -    std::shared_ptr<Thread> current_thread = -        SharedFrom(kernel.CurrentScheduler()->GetCurrentThread()); - -    auto [result, new_owner] = Unlock(current_thread, address); - -    if (result != RESULT_SUCCESS && new_owner != nullptr) { -        new_owner->SetSynchronizationResults(nullptr, result); -    } - -    return result; -} - -} // namespace Kernel diff --git a/src/core/hle/kernel/mutex.h b/src/core/hle/kernel/mutex.h deleted file mode 100644 index 3b81dc3df..000000000 --- a/src/core/hle/kernel/mutex.h +++ /dev/null @@ -1,42 +0,0 @@ -// Copyright 2014 Citra Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. - -#pragma once - -#include "common/common_types.h" - -union ResultCode; - -namespace Core { -class System; -} - -namespace Kernel { - -class Mutex final { -public: -    explicit Mutex(Core::System& system); -    ~Mutex(); - -    /// Flag that indicates that a mutex still has threads waiting for it. -    static constexpr u32 MutexHasWaitersFlag = 0x40000000; -    /// Mask of the bits in a mutex address value that contain the mutex owner. -    static constexpr u32 MutexOwnerMask = 0xBFFFFFFF; - -    /// Attempts to acquire a mutex at the specified address. -    ResultCode TryAcquire(VAddr address, Handle holding_thread_handle, -                          Handle requesting_thread_handle); - -    /// Unlocks a mutex for owner at address -    std::pair<ResultCode, std::shared_ptr<Thread>> Unlock(std::shared_ptr<Thread> owner, -                                                          VAddr address); - -    /// Releases the mutex at the specified address. -    ResultCode Release(VAddr address); - -private: -    Core::System& system; -}; - -} // namespace Kernel diff --git a/src/core/hle/kernel/object.h b/src/core/hle/kernel/object.h index e3391e2af..27124ef67 100644 --- a/src/core/hle/kernel/object.h +++ b/src/core/hle/kernel/object.h @@ -50,6 +50,11 @@ public:      }      virtual HandleType GetHandleType() const = 0; +    void Close() { +        // TODO(bunnei): This is a placeholder to decrement the reference count, which we will use +        // when we implement KAutoObject instead of using shared_ptr. +    } +      /**       * Check if a thread can wait on the object       * @return True if a thread can wait on the object, otherwise false diff --git a/src/core/hle/kernel/process.cpp b/src/core/hle/kernel/process.cpp index b905b486a..37b77fa6e 100644 --- a/src/core/hle/kernel/process.cpp +++ b/src/core/hle/kernel/process.cpp @@ -55,7 +55,7 @@ void SetupMainThread(Core::System& system, Process& owner_process, u32 priority,      // Threads by default are dormant, wake up the main thread so it runs when the scheduler fires      {          KScopedSchedulerLock lock{kernel}; -        thread->SetStatus(ThreadStatus::Ready); +        thread->SetState(ThreadState::Runnable);      }  }  } // Anonymous namespace @@ -162,48 +162,6 @@ u64 Process::GetTotalPhysicalMemoryUsedWithoutSystemResource() const {      return GetTotalPhysicalMemoryUsed() - GetSystemResourceUsage();  } -void Process::InsertConditionVariableThread(std::shared_ptr<Thread> thread) { -    VAddr cond_var_addr = thread->GetCondVarWaitAddress(); -    std::list<std::shared_ptr<Thread>>& thread_list = cond_var_threads[cond_var_addr]; -    auto it = thread_list.begin(); -    while (it != thread_list.end()) { -        const std::shared_ptr<Thread> current_thread = *it; -        if (current_thread->GetPriority() > thread->GetPriority()) { -            thread_list.insert(it, thread); -            return; -        } -        ++it; -    } -    thread_list.push_back(thread); -} - -void Process::RemoveConditionVariableThread(std::shared_ptr<Thread> thread) { -    VAddr cond_var_addr = thread->GetCondVarWaitAddress(); -    std::list<std::shared_ptr<Thread>>& thread_list = cond_var_threads[cond_var_addr]; -    auto it = thread_list.begin(); -    while (it != thread_list.end()) { -        const std::shared_ptr<Thread> current_thread = *it; -        if (current_thread.get() == thread.get()) { -            thread_list.erase(it); -            return; -        } -        ++it; -    } -} - -std::vector<std::shared_ptr<Thread>> Process::GetConditionVariableThreads( -    const VAddr cond_var_addr) { -    std::vector<std::shared_ptr<Thread>> result{}; -    std::list<std::shared_ptr<Thread>>& thread_list = cond_var_threads[cond_var_addr]; -    auto it = thread_list.begin(); -    while (it != thread_list.end()) { -        std::shared_ptr<Thread> current_thread = *it; -        result.push_back(current_thread); -        ++it; -    } -    return result; -} -  void Process::RegisterThread(const Thread* thread) {      thread_list.push_back(thread);  } @@ -318,7 +276,7 @@ void Process::PrepareForTermination() {                  continue;              // TODO(Subv): When are the other running/ready threads terminated? -            ASSERT_MSG(thread->GetStatus() == ThreadStatus::WaitSynch, +            ASSERT_MSG(thread->GetState() == ThreadState::Waiting,                         "Exiting processes with non-waiting threads is currently unimplemented");              thread->Stop(); @@ -406,21 +364,18 @@ void Process::LoadModule(CodeSet code_set, VAddr base_addr) {      ReprotectSegment(code_set.DataSegment(), Memory::MemoryPermission::ReadAndWrite);  } +bool Process::IsSignaled() const { +    ASSERT(kernel.GlobalSchedulerContext().IsLocked()); +    return is_signaled; +} +  Process::Process(Core::System& system) -    : SynchronizationObject{system.Kernel()}, page_table{std::make_unique<Memory::PageTable>( -                                                  system)}, -      handle_table{system.Kernel()}, address_arbiter{system}, mutex{system}, system{system} {} +    : KSynchronizationObject{system.Kernel()}, +      page_table{std::make_unique<Memory::PageTable>(system)}, handle_table{system.Kernel()}, +      address_arbiter{system}, condition_var{system}, system{system} {}  Process::~Process() = default; -void Process::Acquire(Thread* thread) { -    ASSERT_MSG(!ShouldWait(thread), "Object unavailable!"); -} - -bool Process::ShouldWait(const Thread* thread) const { -    return !is_signaled; -} -  void Process::ChangeStatus(ProcessStatus new_status) {      if (status == new_status) {          return; @@ -428,7 +383,7 @@ void Process::ChangeStatus(ProcessStatus new_status) {      status = new_status;      is_signaled = true; -    Signal(); +    NotifyAvailable();  }  ResultCode Process::AllocateMainThreadStack(std::size_t stack_size) { diff --git a/src/core/hle/kernel/process.h b/src/core/hle/kernel/process.h index e412e58aa..564e1f27d 100644 --- a/src/core/hle/kernel/process.h +++ b/src/core/hle/kernel/process.h @@ -11,11 +11,11 @@  #include <unordered_map>  #include <vector>  #include "common/common_types.h" -#include "core/hle/kernel/address_arbiter.h"  #include "core/hle/kernel/handle_table.h" -#include "core/hle/kernel/mutex.h" +#include "core/hle/kernel/k_address_arbiter.h" +#include "core/hle/kernel/k_condition_variable.h" +#include "core/hle/kernel/k_synchronization_object.h"  #include "core/hle/kernel/process_capability.h" -#include "core/hle/kernel/synchronization_object.h"  #include "core/hle/result.h"  namespace Core { @@ -63,7 +63,7 @@ enum class ProcessStatus {      DebugBreak,  }; -class Process final : public SynchronizationObject { +class Process final : public KSynchronizationObject {  public:      explicit Process(Core::System& system);      ~Process() override; @@ -123,24 +123,30 @@ public:          return handle_table;      } -    /// Gets a reference to the process' address arbiter. -    AddressArbiter& GetAddressArbiter() { -        return address_arbiter; +    ResultCode SignalToAddress(VAddr address) { +        return condition_var.SignalToAddress(address);      } -    /// Gets a const reference to the process' address arbiter. -    const AddressArbiter& GetAddressArbiter() const { -        return address_arbiter; +    ResultCode WaitForAddress(Handle handle, VAddr address, u32 tag) { +        return condition_var.WaitForAddress(handle, address, tag);      } -    /// Gets a reference to the process' mutex lock. -    Mutex& GetMutex() { -        return mutex; +    void SignalConditionVariable(u64 cv_key, int32_t count) { +        return condition_var.Signal(cv_key, count);      } -    /// Gets a const reference to the process' mutex lock -    const Mutex& GetMutex() const { -        return mutex; +    ResultCode WaitConditionVariable(VAddr address, u64 cv_key, u32 tag, s64 ns) { +        return condition_var.Wait(address, cv_key, tag, ns); +    } + +    ResultCode SignalAddressArbiter(VAddr address, Svc::SignalType signal_type, s32 value, +                                    s32 count) { +        return address_arbiter.SignalToAddress(address, signal_type, value, count); +    } + +    ResultCode WaitAddressArbiter(VAddr address, Svc::ArbitrationType arb_type, s32 value, +                                  s64 timeout) { +        return address_arbiter.WaitForAddress(address, arb_type, value, timeout);      }      /// Gets the address to the process' dedicated TLS region. @@ -250,15 +256,6 @@ public:          return thread_list;      } -    /// Insert a thread into the condition variable wait container -    void InsertConditionVariableThread(std::shared_ptr<Thread> thread); - -    /// Remove a thread from the condition variable wait container -    void RemoveConditionVariableThread(std::shared_ptr<Thread> thread); - -    /// Obtain all condition variable threads waiting for some address -    std::vector<std::shared_ptr<Thread>> GetConditionVariableThreads(VAddr cond_var_addr); -      /// Registers a thread as being created under this process,      /// adding it to this process' thread list.      void RegisterThread(const Thread* thread); @@ -304,6 +301,8 @@ public:      void LoadModule(CodeSet code_set, VAddr base_addr); +    bool IsSignaled() const override; +      ///////////////////////////////////////////////////////////////////////////////////////////////      // Thread-local storage management @@ -314,12 +313,6 @@ public:      void FreeTLSRegion(VAddr tls_address);  private: -    /// Checks if the specified thread should wait until this process is available. -    bool ShouldWait(const Thread* thread) const override; - -    /// Acquires/locks this process for the specified thread if it's available. -    void Acquire(Thread* thread) override; -      /// Changes the process status. If the status is different      /// from the current process status, then this will trigger      /// a process signal. @@ -373,12 +366,12 @@ private:      HandleTable handle_table;      /// Per-process address arbiter. -    AddressArbiter address_arbiter; +    KAddressArbiter address_arbiter;      /// The per-process mutex lock instance used for handling various      /// forms of services, such as lock arbitration, and condition      /// variable related facilities. -    Mutex mutex; +    KConditionVariable condition_var;      /// Address indicating the location of the process' dedicated TLS region.      VAddr tls_region_address = 0; @@ -389,9 +382,6 @@ private:      /// List of threads that are running with this process as their owner.      std::list<const Thread*> thread_list; -    /// List of threads waiting for a condition variable -    std::unordered_map<VAddr, std::list<std::shared_ptr<Thread>>> cond_var_threads; -      /// Address of the top of the main thread's stack      VAddr main_thread_stack_top{}; @@ -410,6 +400,8 @@ private:      /// Schedule count of this process      s64 schedule_count{}; +    bool is_signaled{}; +      /// System context      Core::System& system;  }; diff --git a/src/core/hle/kernel/readable_event.cpp b/src/core/hle/kernel/readable_event.cpp index cea262ce0..99ed0857e 100644 --- a/src/core/hle/kernel/readable_event.cpp +++ b/src/core/hle/kernel/readable_event.cpp @@ -14,24 +14,22 @@  namespace Kernel { -ReadableEvent::ReadableEvent(KernelCore& kernel) : SynchronizationObject{kernel} {} +ReadableEvent::ReadableEvent(KernelCore& kernel) : KSynchronizationObject{kernel} {}  ReadableEvent::~ReadableEvent() = default; -bool ReadableEvent::ShouldWait(const Thread* thread) const { -    return !is_signaled; -} - -void ReadableEvent::Acquire(Thread* thread) { -    ASSERT_MSG(IsSignaled(), "object unavailable!"); -} -  void ReadableEvent::Signal() {      if (is_signaled) {          return;      }      is_signaled = true; -    SynchronizationObject::Signal(); +    NotifyAvailable(); +} + +bool ReadableEvent::IsSignaled() const { +    ASSERT(kernel.GlobalSchedulerContext().IsLocked()); + +    return is_signaled;  }  void ReadableEvent::Clear() { diff --git a/src/core/hle/kernel/readable_event.h b/src/core/hle/kernel/readable_event.h index 3264dd066..34e477274 100644 --- a/src/core/hle/kernel/readable_event.h +++ b/src/core/hle/kernel/readable_event.h @@ -4,8 +4,8 @@  #pragma once +#include "core/hle/kernel/k_synchronization_object.h"  #include "core/hle/kernel/object.h" -#include "core/hle/kernel/synchronization_object.h"  union ResultCode; @@ -14,7 +14,7 @@ namespace Kernel {  class KernelCore;  class WritableEvent; -class ReadableEvent final : public SynchronizationObject { +class ReadableEvent final : public KSynchronizationObject {      friend class WritableEvent;  public: @@ -32,9 +32,6 @@ public:          return HANDLE_TYPE;      } -    bool ShouldWait(const Thread* thread) const override; -    void Acquire(Thread* thread) override; -      /// Unconditionally clears the readable event's state.      void Clear(); @@ -46,11 +43,14 @@ public:      ///      then ERR_INVALID_STATE will be returned.      ResultCode Reset(); -    void Signal() override; +    void Signal(); + +    bool IsSignaled() const override;  private:      explicit ReadableEvent(KernelCore& kernel); +    bool is_signaled{};      std::string name; ///< Name of event (optional)  }; diff --git a/src/core/hle/kernel/server_port.cpp b/src/core/hle/kernel/server_port.cpp index a549ae9d7..82857f93b 100644 --- a/src/core/hle/kernel/server_port.cpp +++ b/src/core/hle/kernel/server_port.cpp @@ -13,7 +13,7 @@  namespace Kernel { -ServerPort::ServerPort(KernelCore& kernel) : SynchronizationObject{kernel} {} +ServerPort::ServerPort(KernelCore& kernel) : KSynchronizationObject{kernel} {}  ServerPort::~ServerPort() = default;  ResultVal<std::shared_ptr<ServerSession>> ServerPort::Accept() { @@ -28,15 +28,9 @@ ResultVal<std::shared_ptr<ServerSession>> ServerPort::Accept() {  void ServerPort::AppendPendingSession(std::shared_ptr<ServerSession> pending_session) {      pending_sessions.push_back(std::move(pending_session)); -} - -bool ServerPort::ShouldWait(const Thread* thread) const { -    // If there are no pending sessions, we wait until a new one is added. -    return pending_sessions.empty(); -} - -void ServerPort::Acquire(Thread* thread) { -    ASSERT_MSG(!ShouldWait(thread), "object unavailable!"); +    if (pending_sessions.size() == 1) { +        NotifyAvailable(); +    }  }  bool ServerPort::IsSignaled() const { diff --git a/src/core/hle/kernel/server_port.h b/src/core/hle/kernel/server_port.h index 41b191b86..6470df993 100644 --- a/src/core/hle/kernel/server_port.h +++ b/src/core/hle/kernel/server_port.h @@ -9,8 +9,8 @@  #include <utility>  #include <vector>  #include "common/common_types.h" +#include "core/hle/kernel/k_synchronization_object.h"  #include "core/hle/kernel/object.h" -#include "core/hle/kernel/synchronization_object.h"  #include "core/hle/result.h"  namespace Kernel { @@ -20,7 +20,7 @@ class KernelCore;  class ServerSession;  class SessionRequestHandler; -class ServerPort final : public SynchronizationObject { +class ServerPort final : public KSynchronizationObject {  public:      explicit ServerPort(KernelCore& kernel);      ~ServerPort() override; @@ -79,9 +79,6 @@ public:      /// waiting to be accepted by this port.      void AppendPendingSession(std::shared_ptr<ServerSession> pending_session); -    bool ShouldWait(const Thread* thread) const override; -    void Acquire(Thread* thread) override; -      bool IsSignaled() const override;  private: diff --git a/src/core/hle/kernel/server_session.cpp b/src/core/hle/kernel/server_session.cpp index b40fe3916..4f2bb7822 100644 --- a/src/core/hle/kernel/server_session.cpp +++ b/src/core/hle/kernel/server_session.cpp @@ -24,7 +24,7 @@  namespace Kernel { -ServerSession::ServerSession(KernelCore& kernel) : SynchronizationObject{kernel} {} +ServerSession::ServerSession(KernelCore& kernel) : KSynchronizationObject{kernel} {}  ServerSession::~ServerSession() {      kernel.ReleaseServiceThread(service_thread); @@ -42,16 +42,6 @@ ResultVal<std::shared_ptr<ServerSession>> ServerSession::Create(KernelCore& kern      return MakeResult(std::move(session));  } -bool ServerSession::ShouldWait(const Thread* thread) const { -    // Closed sessions should never wait, an error will be returned from svcReplyAndReceive. -    if (!parent->Client()) { -        return false; -    } - -    // Wait if we have no pending requests, or if we're currently handling a request. -    return pending_requesting_threads.empty() || currently_handling != nullptr; -} -  bool ServerSession::IsSignaled() const {      // Closed sessions should never wait, an error will be returned from svcReplyAndReceive.      if (!parent->Client()) { @@ -62,15 +52,6 @@ bool ServerSession::IsSignaled() const {      return !pending_requesting_threads.empty() && currently_handling == nullptr;  } -void ServerSession::Acquire(Thread* thread) { -    ASSERT_MSG(!ShouldWait(thread), "object unavailable!"); -    // We are now handling a request, pop it from the stack. -    // TODO(Subv): What happens if the client endpoint is closed before any requests are made? -    ASSERT(!pending_requesting_threads.empty()); -    currently_handling = pending_requesting_threads.back(); -    pending_requesting_threads.pop_back(); -} -  void ServerSession::ClientDisconnected() {      // We keep a shared pointer to the hle handler to keep it alive throughout      // the call to ClientDisconnected, as ClientDisconnected invalidates the @@ -172,7 +153,7 @@ ResultCode ServerSession::CompleteSyncRequest(HLERequestContext& context) {      {          KScopedSchedulerLock lock(kernel);          if (!context.IsThreadWaiting()) { -            context.GetThread().ResumeFromWait(); +            context.GetThread().Wakeup();              context.GetThread().SetSynchronizationResults(nullptr, result);          }      } diff --git a/src/core/hle/kernel/server_session.h b/src/core/hle/kernel/server_session.h index e8d1d99ea..9155cf7f5 100644 --- a/src/core/hle/kernel/server_session.h +++ b/src/core/hle/kernel/server_session.h @@ -10,8 +10,8 @@  #include <vector>  #include "common/threadsafe_queue.h" +#include "core/hle/kernel/k_synchronization_object.h"  #include "core/hle/kernel/service_thread.h" -#include "core/hle/kernel/synchronization_object.h"  #include "core/hle/result.h"  namespace Core::Memory { @@ -43,7 +43,7 @@ class Thread;   * After the server replies to the request, the response is marshalled back to the caller's   * TLS buffer and control is transferred back to it.   */ -class ServerSession final : public SynchronizationObject { +class ServerSession final : public KSynchronizationObject {      friend class ServiceThread;  public: @@ -77,8 +77,6 @@ public:          return parent.get();      } -    bool IsSignaled() const override; -      /**       * Sets the HLE handler for the session. This handler will be called to service IPC requests       * instead of the regular IPC machinery. (The regular IPC machinery is currently not @@ -100,10 +98,6 @@ public:      ResultCode HandleSyncRequest(std::shared_ptr<Thread> thread, Core::Memory::Memory& memory,                                   Core::Timing::CoreTiming& core_timing); -    bool ShouldWait(const Thread* thread) const override; - -    void Acquire(Thread* thread) override; -      /// Called when a client disconnection occurs.      void ClientDisconnected(); @@ -130,6 +124,8 @@ public:          convert_to_domain = true;      } +    bool IsSignaled() const override; +  private:      /// Queues a sync request from the emulated application.      ResultCode QueueSyncRequest(std::shared_ptr<Thread> thread, Core::Memory::Memory& memory); diff --git a/src/core/hle/kernel/session.cpp b/src/core/hle/kernel/session.cpp index e4dd53e24..75304b961 100644 --- a/src/core/hle/kernel/session.cpp +++ b/src/core/hle/kernel/session.cpp @@ -9,7 +9,7 @@  namespace Kernel { -Session::Session(KernelCore& kernel) : SynchronizationObject{kernel} {} +Session::Session(KernelCore& kernel) : KSynchronizationObject{kernel} {}  Session::~Session() = default;  Session::SessionPair Session::Create(KernelCore& kernel, std::string name) { @@ -24,18 +24,9 @@ Session::SessionPair Session::Create(KernelCore& kernel, std::string name) {      return std::make_pair(std::move(client_session), std::move(server_session));  } -bool Session::ShouldWait(const Thread* thread) const { -    UNIMPLEMENTED(); -    return {}; -} -  bool Session::IsSignaled() const {      UNIMPLEMENTED();      return true;  } -void Session::Acquire(Thread* thread) { -    UNIMPLEMENTED(); -} -  } // namespace Kernel diff --git a/src/core/hle/kernel/session.h b/src/core/hle/kernel/session.h index 7cd9c0d77..f6dd2c1d2 100644 --- a/src/core/hle/kernel/session.h +++ b/src/core/hle/kernel/session.h @@ -8,7 +8,7 @@  #include <string>  #include <utility> -#include "core/hle/kernel/synchronization_object.h" +#include "core/hle/kernel/k_synchronization_object.h"  namespace Kernel { @@ -19,7 +19,7 @@ class ServerSession;   * Parent structure to link the client and server endpoints of a session with their associated   * client port.   */ -class Session final : public SynchronizationObject { +class Session final : public KSynchronizationObject {  public:      explicit Session(KernelCore& kernel);      ~Session() override; @@ -37,12 +37,8 @@ public:          return HANDLE_TYPE;      } -    bool ShouldWait(const Thread* thread) const override; -      bool IsSignaled() const override; -    void Acquire(Thread* thread) override; -      std::shared_ptr<ClientSession> Client() {          if (auto result{client.lock()}) {              return result; diff --git a/src/core/hle/kernel/svc.cpp b/src/core/hle/kernel/svc.cpp index de3ed25da..cc8b661af 100644 --- a/src/core/hle/kernel/svc.cpp +++ b/src/core/hle/kernel/svc.cpp @@ -10,6 +10,7 @@  #include "common/alignment.h"  #include "common/assert.h" +#include "common/common_funcs.h"  #include "common/fiber.h"  #include "common/logging/log.h"  #include "common/microprofile.h" @@ -19,26 +20,28 @@  #include "core/core_timing.h"  #include "core/core_timing_util.h"  #include "core/cpu_manager.h" -#include "core/hle/kernel/address_arbiter.h"  #include "core/hle/kernel/client_port.h"  #include "core/hle/kernel/client_session.h"  #include "core/hle/kernel/errors.h"  #include "core/hle/kernel/handle_table.h" +#include "core/hle/kernel/k_address_arbiter.h" +#include "core/hle/kernel/k_condition_variable.h"  #include "core/hle/kernel/k_scheduler.h"  #include "core/hle/kernel/k_scoped_scheduler_lock_and_sleep.h" +#include "core/hle/kernel/k_synchronization_object.h"  #include "core/hle/kernel/kernel.h"  #include "core/hle/kernel/memory/memory_block.h" +#include "core/hle/kernel/memory/memory_layout.h"  #include "core/hle/kernel/memory/page_table.h" -#include "core/hle/kernel/mutex.h"  #include "core/hle/kernel/physical_core.h"  #include "core/hle/kernel/process.h"  #include "core/hle/kernel/readable_event.h"  #include "core/hle/kernel/resource_limit.h"  #include "core/hle/kernel/shared_memory.h"  #include "core/hle/kernel/svc.h" +#include "core/hle/kernel/svc_results.h"  #include "core/hle/kernel/svc_types.h"  #include "core/hle/kernel/svc_wrap.h" -#include "core/hle/kernel/synchronization.h"  #include "core/hle/kernel/thread.h"  #include "core/hle/kernel/time_manager.h"  #include "core/hle/kernel/transfer_memory.h" @@ -343,27 +346,11 @@ static ResultCode SendSyncRequest(Core::System& system, Handle handle) {      auto thread = kernel.CurrentScheduler()->GetCurrentThread();      {          KScopedSchedulerLock lock(kernel); -        thread->InvalidateHLECallback(); -        thread->SetStatus(ThreadStatus::WaitIPC); +        thread->SetState(ThreadState::Waiting); +        thread->SetWaitReasonForDebugging(ThreadWaitReasonForDebugging::IPC);          session->SendSyncRequest(SharedFrom(thread), system.Memory(), system.CoreTiming());      } -    if (thread->HasHLECallback()) { -        Handle event_handle = thread->GetHLETimeEvent(); -        if (event_handle != InvalidHandle) { -            auto& time_manager = kernel.TimeManager(); -            time_manager.UnscheduleTimeEvent(event_handle); -        } - -        { -            KScopedSchedulerLock lock(kernel); -            auto* sync_object = thread->GetHLESyncObject(); -            sync_object->RemoveWaitingThread(SharedFrom(thread)); -        } - -        thread->InvokeHLECallback(SharedFrom(thread)); -    } -      return thread->GetSignalingResult();  } @@ -436,7 +423,7 @@ static ResultCode GetProcessId32(Core::System& system, u32* process_id_low, u32*  }  /// Wait for the given handles to synchronize, timeout after the specified nanoseconds -static ResultCode WaitSynchronization(Core::System& system, Handle* index, VAddr handles_address, +static ResultCode WaitSynchronization(Core::System& system, s32* index, VAddr handles_address,                                        u64 handle_count, s64 nano_seconds) {      LOG_TRACE(Kernel_SVC, "called handles_address=0x{:X}, handle_count={}, nano_seconds={}",                handles_address, handle_count, nano_seconds); @@ -458,28 +445,26 @@ static ResultCode WaitSynchronization(Core::System& system, Handle* index, VAddr      }      auto& kernel = system.Kernel(); -    Thread::ThreadSynchronizationObjects objects(handle_count); +    std::vector<KSynchronizationObject*> objects(handle_count);      const auto& handle_table = kernel.CurrentProcess()->GetHandleTable();      for (u64 i = 0; i < handle_count; ++i) {          const Handle handle = memory.Read32(handles_address + i * sizeof(Handle)); -        const auto object = handle_table.Get<SynchronizationObject>(handle); +        const auto object = handle_table.Get<KSynchronizationObject>(handle);          if (object == nullptr) {              LOG_ERROR(Kernel_SVC, "Object is a nullptr");              return ERR_INVALID_HANDLE;          } -        objects[i] = object; +        objects[i] = object.get();      } -    auto& synchronization = kernel.Synchronization(); -    const auto [result, handle_result] = synchronization.WaitFor(objects, nano_seconds); -    *index = handle_result; -    return result; +    return KSynchronizationObject::Wait(kernel, index, objects.data(), +                                        static_cast<s32>(objects.size()), nano_seconds);  }  static ResultCode WaitSynchronization32(Core::System& system, u32 timeout_low, u32 handles_address, -                                        s32 handle_count, u32 timeout_high, Handle* index) { +                                        s32 handle_count, u32 timeout_high, s32* index) {      const s64 nano_seconds{(static_cast<s64>(timeout_high) << 32) | static_cast<s64>(timeout_low)};      return WaitSynchronization(system, index, handles_address, handle_count, nano_seconds);  } @@ -504,56 +489,37 @@ static ResultCode CancelSynchronization32(Core::System& system, Handle thread_ha      return CancelSynchronization(system, thread_handle);  } -/// Attempts to locks a mutex, creating it if it does not already exist -static ResultCode ArbitrateLock(Core::System& system, Handle holding_thread_handle, -                                VAddr mutex_addr, Handle requesting_thread_handle) { -    LOG_TRACE(Kernel_SVC, -              "called holding_thread_handle=0x{:08X}, mutex_addr=0x{:X}, " -              "requesting_current_thread_handle=0x{:08X}", -              holding_thread_handle, mutex_addr, requesting_thread_handle); - -    if (Core::Memory::IsKernelVirtualAddress(mutex_addr)) { -        LOG_ERROR(Kernel_SVC, "Mutex Address is a kernel virtual address, mutex_addr={:016X}", -                  mutex_addr); -        return ERR_INVALID_ADDRESS_STATE; -    } +/// Attempts to locks a mutex +static ResultCode ArbitrateLock(Core::System& system, Handle thread_handle, VAddr address, +                                u32 tag) { +    LOG_TRACE(Kernel_SVC, "called thread_handle=0x{:08X}, address=0x{:X}, tag=0x{:08X}", +              thread_handle, address, tag); -    if (!Common::IsWordAligned(mutex_addr)) { -        LOG_ERROR(Kernel_SVC, "Mutex Address is not word aligned, mutex_addr={:016X}", mutex_addr); -        return ERR_INVALID_ADDRESS; -    } +    // Validate the input address. +    R_UNLESS(!Memory::IsKernelAddress(address), Svc::ResultInvalidCurrentMemory); +    R_UNLESS(Common::IsAligned(address, sizeof(u32)), Svc::ResultInvalidAddress); -    auto* const current_process = system.Kernel().CurrentProcess(); -    return current_process->GetMutex().TryAcquire(mutex_addr, holding_thread_handle, -                                                  requesting_thread_handle); +    return system.Kernel().CurrentProcess()->WaitForAddress(thread_handle, address, tag);  } -static ResultCode ArbitrateLock32(Core::System& system, Handle holding_thread_handle, -                                  u32 mutex_addr, Handle requesting_thread_handle) { -    return ArbitrateLock(system, holding_thread_handle, mutex_addr, requesting_thread_handle); +static ResultCode ArbitrateLock32(Core::System& system, Handle thread_handle, u32 address, +                                  u32 tag) { +    return ArbitrateLock(system, thread_handle, address, tag);  }  /// Unlock a mutex -static ResultCode ArbitrateUnlock(Core::System& system, VAddr mutex_addr) { -    LOG_TRACE(Kernel_SVC, "called mutex_addr=0x{:X}", mutex_addr); - -    if (Core::Memory::IsKernelVirtualAddress(mutex_addr)) { -        LOG_ERROR(Kernel_SVC, "Mutex Address is a kernel virtual address, mutex_addr={:016X}", -                  mutex_addr); -        return ERR_INVALID_ADDRESS_STATE; -    } +static ResultCode ArbitrateUnlock(Core::System& system, VAddr address) { +    LOG_TRACE(Kernel_SVC, "called address=0x{:X}", address); -    if (!Common::IsWordAligned(mutex_addr)) { -        LOG_ERROR(Kernel_SVC, "Mutex Address is not word aligned, mutex_addr={:016X}", mutex_addr); -        return ERR_INVALID_ADDRESS; -    } +    // Validate the input address. +    R_UNLESS(!Memory::IsKernelAddress(address), Svc::ResultInvalidCurrentMemory); +    R_UNLESS(Common::IsAligned(address, sizeof(u32)), Svc::ResultInvalidAddress); -    auto* const current_process = system.Kernel().CurrentProcess(); -    return current_process->GetMutex().Release(mutex_addr); +    return system.Kernel().CurrentProcess()->SignalToAddress(address);  } -static ResultCode ArbitrateUnlock32(Core::System& system, u32 mutex_addr) { -    return ArbitrateUnlock(system, mutex_addr); +static ResultCode ArbitrateUnlock32(Core::System& system, u32 address) { +    return ArbitrateUnlock(system, address);  }  enum class BreakType : u32 { @@ -1180,7 +1146,7 @@ static ResultCode SetThreadPriority(Core::System& system, Handle handle, u32 pri          return ERR_INVALID_HANDLE;      } -    thread->SetPriority(priority); +    thread->SetBasePriority(priority);      return RESULT_SUCCESS;  } @@ -1559,7 +1525,7 @@ static ResultCode StartThread(Core::System& system, Handle thread_handle) {          return ERR_INVALID_HANDLE;      } -    ASSERT(thread->GetStatus() == ThreadStatus::Dormant); +    ASSERT(thread->GetState() == ThreadState::Initialized);      return thread->Start();  } @@ -1620,224 +1586,135 @@ static void SleepThread32(Core::System& system, u32 nanoseconds_low, u32 nanosec  }  /// Wait process wide key atomic -static ResultCode WaitProcessWideKeyAtomic(Core::System& system, VAddr mutex_addr, -                                           VAddr condition_variable_addr, Handle thread_handle, -                                           s64 nano_seconds) { -    LOG_TRACE( -        Kernel_SVC, -        "called mutex_addr={:X}, condition_variable_addr={:X}, thread_handle=0x{:08X}, timeout={}", -        mutex_addr, condition_variable_addr, thread_handle, nano_seconds); - -    if (Core::Memory::IsKernelVirtualAddress(mutex_addr)) { -        LOG_ERROR( -            Kernel_SVC, -            "Given mutex address must not be within the kernel address space. address=0x{:016X}", -            mutex_addr); -        return ERR_INVALID_ADDRESS_STATE; -    } - -    if (!Common::IsWordAligned(mutex_addr)) { -        LOG_ERROR(Kernel_SVC, "Given mutex address must be word-aligned. address=0x{:016X}", -                  mutex_addr); -        return ERR_INVALID_ADDRESS; -    } - -    ASSERT(condition_variable_addr == Common::AlignDown(condition_variable_addr, 4)); -    auto& kernel = system.Kernel(); -    Handle event_handle; -    Thread* current_thread = kernel.CurrentScheduler()->GetCurrentThread(); -    auto* const current_process = kernel.CurrentProcess(); -    { -        KScopedSchedulerLockAndSleep lock(kernel, event_handle, current_thread, nano_seconds); -        const auto& handle_table = current_process->GetHandleTable(); -        std::shared_ptr<Thread> thread = handle_table.Get<Thread>(thread_handle); -        ASSERT(thread); - -        current_thread->SetSynchronizationResults(nullptr, RESULT_TIMEOUT); - -        if (thread->IsPendingTermination()) { -            lock.CancelSleep(); -            return ERR_THREAD_TERMINATING; -        } - -        const auto release_result = current_process->GetMutex().Release(mutex_addr); -        if (release_result.IsError()) { -            lock.CancelSleep(); -            return release_result; -        } - -        if (nano_seconds == 0) { -            lock.CancelSleep(); -            return RESULT_TIMEOUT; -        } - -        current_thread->SetCondVarWaitAddress(condition_variable_addr); -        current_thread->SetMutexWaitAddress(mutex_addr); -        current_thread->SetWaitHandle(thread_handle); -        current_thread->SetStatus(ThreadStatus::WaitCondVar); -        current_process->InsertConditionVariableThread(SharedFrom(current_thread)); -    } - -    if (event_handle != InvalidHandle) { -        auto& time_manager = kernel.TimeManager(); -        time_manager.UnscheduleTimeEvent(event_handle); -    } - -    { -        KScopedSchedulerLock lock(kernel); - -        auto* owner = current_thread->GetLockOwner(); -        if (owner != nullptr) { -            owner->RemoveMutexWaiter(SharedFrom(current_thread)); +static ResultCode WaitProcessWideKeyAtomic(Core::System& system, VAddr address, VAddr cv_key, +                                           u32 tag, s64 timeout_ns) { +    LOG_TRACE(Kernel_SVC, "called address={:X}, cv_key={:X}, tag=0x{:08X}, timeout_ns={}", address, +              cv_key, tag, timeout_ns); + +    // Validate input. +    R_UNLESS(!Memory::IsKernelAddress(address), Svc::ResultInvalidCurrentMemory); +    R_UNLESS(Common::IsAligned(address, sizeof(int32_t)), Svc::ResultInvalidAddress); + +    // Convert timeout from nanoseconds to ticks. +    s64 timeout{}; +    if (timeout_ns > 0) { +        const s64 offset_tick(timeout_ns); +        if (offset_tick > 0) { +            timeout = offset_tick + 2; +            if (timeout <= 0) { +                timeout = std::numeric_limits<s64>::max(); +            } +        } else { +            timeout = std::numeric_limits<s64>::max();          } - -        current_process->RemoveConditionVariableThread(SharedFrom(current_thread)); +    } else { +        timeout = timeout_ns;      } -    // Note: Deliberately don't attempt to inherit the lock owner's priority. -    return current_thread->GetSignalingResult(); +    // Wait on the condition variable. +    return system.Kernel().CurrentProcess()->WaitConditionVariable( +        address, Common::AlignDown(cv_key, sizeof(u32)), tag, timeout);  } -static ResultCode WaitProcessWideKeyAtomic32(Core::System& system, u32 mutex_addr, -                                             u32 condition_variable_addr, Handle thread_handle, -                                             u32 nanoseconds_low, u32 nanoseconds_high) { -    const auto nanoseconds = static_cast<s64>(nanoseconds_low | (u64{nanoseconds_high} << 32)); -    return WaitProcessWideKeyAtomic(system, mutex_addr, condition_variable_addr, thread_handle, -                                    nanoseconds); +static ResultCode WaitProcessWideKeyAtomic32(Core::System& system, u32 address, u32 cv_key, u32 tag, +                                             u32 timeout_ns_low, u32 timeout_ns_high) { +    const auto timeout_ns = static_cast<s64>(timeout_ns_low | (u64{timeout_ns_high} << 32)); +    return WaitProcessWideKeyAtomic(system, address, cv_key, tag, timeout_ns);  }  /// Signal process wide key -static void SignalProcessWideKey(Core::System& system, VAddr condition_variable_addr, s32 target) { -    LOG_TRACE(Kernel_SVC, "called, condition_variable_addr=0x{:X}, target=0x{:08X}", -              condition_variable_addr, target); +static void SignalProcessWideKey(Core::System& system, VAddr cv_key, s32 count) { +    LOG_TRACE(Kernel_SVC, "called, cv_key=0x{:X}, count=0x{:08X}", cv_key, count); -    ASSERT(condition_variable_addr == Common::AlignDown(condition_variable_addr, 4)); +    // Signal the condition variable. +    return system.Kernel().CurrentProcess()->SignalConditionVariable( +        Common::AlignDown(cv_key, sizeof(u32)), count); +} -    // Retrieve a list of all threads that are waiting for this condition variable. -    auto& kernel = system.Kernel(); -    KScopedSchedulerLock lock(kernel); -    auto* const current_process = kernel.CurrentProcess(); -    std::vector<std::shared_ptr<Thread>> waiting_threads = -        current_process->GetConditionVariableThreads(condition_variable_addr); - -    // Only process up to 'target' threads, unless 'target' is less equal 0, in which case process -    // them all. -    std::size_t last = waiting_threads.size(); -    if (target > 0) { -        last = std::min(waiting_threads.size(), static_cast<std::size_t>(target)); -    } -    for (std::size_t index = 0; index < last; ++index) { -        auto& thread = waiting_threads[index]; - -        ASSERT(thread->GetCondVarWaitAddress() == condition_variable_addr); - -        // liberate Cond Var Thread. -        current_process->RemoveConditionVariableThread(thread); - -        const std::size_t current_core = system.CurrentCoreIndex(); -        auto& monitor = system.Monitor(); - -        // Atomically read the value of the mutex. -        u32 mutex_val = 0; -        u32 update_val = 0; -        const VAddr mutex_address = thread->GetMutexWaitAddress(); -        do { -            // If the mutex is not yet acquired, acquire it. -            mutex_val = monitor.ExclusiveRead32(current_core, mutex_address); - -            if (mutex_val != 0) { -                update_val = mutex_val | Mutex::MutexHasWaitersFlag; -            } else { -                update_val = thread->GetWaitHandle(); -            } -        } while (!monitor.ExclusiveWrite32(current_core, mutex_address, update_val)); -        monitor.ClearExclusive(); -        if (mutex_val == 0) { -            // We were able to acquire the mutex, resume this thread. -            auto* const lock_owner = thread->GetLockOwner(); -            if (lock_owner != nullptr) { -                lock_owner->RemoveMutexWaiter(thread); -            } +static void SignalProcessWideKey32(Core::System& system, u32 cv_key, s32 count) { +    SignalProcessWideKey(system, cv_key, count); +} -            thread->SetLockOwner(nullptr); -            thread->SetSynchronizationResults(nullptr, RESULT_SUCCESS); -            thread->ResumeFromWait(); -        } else { -            // The mutex is already owned by some other thread, make this thread wait on it. -            const Handle owner_handle = static_cast<Handle>(mutex_val & Mutex::MutexOwnerMask); -            const auto& handle_table = system.Kernel().CurrentProcess()->GetHandleTable(); -            auto owner = handle_table.Get<Thread>(owner_handle); -            ASSERT(owner); -            if (thread->GetStatus() == ThreadStatus::WaitCondVar) { -                thread->SetStatus(ThreadStatus::WaitMutex); -            } +namespace { -            owner->AddMutexWaiter(thread); -        } +constexpr bool IsValidSignalType(Svc::SignalType type) { +    switch (type) { +    case Svc::SignalType::Signal: +    case Svc::SignalType::SignalAndIncrementIfEqual: +    case Svc::SignalType::SignalAndModifyByWaitingCountIfEqual: +        return true; +    default: +        return false;      }  } -static void SignalProcessWideKey32(Core::System& system, u32 condition_variable_addr, s32 target) { -    SignalProcessWideKey(system, condition_variable_addr, target); +constexpr bool IsValidArbitrationType(Svc::ArbitrationType type) { +    switch (type) { +    case Svc::ArbitrationType::WaitIfLessThan: +    case Svc::ArbitrationType::DecrementAndWaitIfLessThan: +    case Svc::ArbitrationType::WaitIfEqual: +        return true; +    default: +        return false; +    }  } -// Wait for an address (via Address Arbiter) -static ResultCode WaitForAddress(Core::System& system, VAddr address, u32 type, s32 value, -                                 s64 timeout) { -    LOG_TRACE(Kernel_SVC, "called, address=0x{:X}, type=0x{:X}, value=0x{:X}, timeout={}", address, -              type, value, timeout); - -    // If the passed address is a kernel virtual address, return invalid memory state. -    if (Core::Memory::IsKernelVirtualAddress(address)) { -        LOG_ERROR(Kernel_SVC, "Address is a kernel virtual address, address={:016X}", address); -        return ERR_INVALID_ADDRESS_STATE; -    } +} // namespace -    // If the address is not properly aligned to 4 bytes, return invalid address. -    if (!Common::IsWordAligned(address)) { -        LOG_ERROR(Kernel_SVC, "Address is not word aligned, address={:016X}", address); -        return ERR_INVALID_ADDRESS; +// Wait for an address (via Address Arbiter) +static ResultCode WaitForAddress(Core::System& system, VAddr address, Svc::ArbitrationType arb_type, +                                 s32 value, s64 timeout_ns) { +    LOG_TRACE(Kernel_SVC, "called, address=0x{:X}, arb_type=0x{:X}, value=0x{:X}, timeout_ns={}", +              address, arb_type, value, timeout_ns); + +    // Validate input. +    R_UNLESS(!Memory::IsKernelAddress(address), Svc::ResultInvalidCurrentMemory); +    R_UNLESS(Common::IsAligned(address, sizeof(int32_t)), Svc::ResultInvalidAddress); +    R_UNLESS(IsValidArbitrationType(arb_type), Svc::ResultInvalidEnumValue); + +    // Convert timeout from nanoseconds to ticks. +    s64 timeout{}; +    if (timeout_ns > 0) { +        const s64 offset_tick(timeout_ns); +        if (offset_tick > 0) { +            timeout = offset_tick + 2; +            if (timeout <= 0) { +                timeout = std::numeric_limits<s64>::max(); +            } +        } else { +            timeout = std::numeric_limits<s64>::max(); +        } +    } else { +        timeout = timeout_ns;      } -    const auto arbitration_type = static_cast<AddressArbiter::ArbitrationType>(type); -    auto& address_arbiter = system.Kernel().CurrentProcess()->GetAddressArbiter(); -    const ResultCode result = -        address_arbiter.WaitForAddress(address, arbitration_type, value, timeout); -    return result; +    return system.Kernel().CurrentProcess()->WaitAddressArbiter(address, arb_type, value, timeout);  } -static ResultCode WaitForAddress32(Core::System& system, u32 address, u32 type, s32 value, -                                   u32 timeout_low, u32 timeout_high) { -    const auto timeout = static_cast<s64>(timeout_low | (u64{timeout_high} << 32)); -    return WaitForAddress(system, address, type, value, timeout); +static ResultCode WaitForAddress32(Core::System& system, u32 address, Svc::ArbitrationType arb_type, +                                   s32 value, u32 timeout_ns_low, u32 timeout_ns_high) { +    const auto timeout = static_cast<s64>(timeout_ns_low | (u64{timeout_ns_high} << 32)); +    return WaitForAddress(system, address, arb_type, value, timeout);  }  // Signals to an address (via Address Arbiter) -static ResultCode SignalToAddress(Core::System& system, VAddr address, u32 type, s32 value, -                                  s32 num_to_wake) { -    LOG_TRACE(Kernel_SVC, "called, address=0x{:X}, type=0x{:X}, value=0x{:X}, num_to_wake=0x{:X}", -              address, type, value, num_to_wake); - -    // If the passed address is a kernel virtual address, return invalid memory state. -    if (Core::Memory::IsKernelVirtualAddress(address)) { -        LOG_ERROR(Kernel_SVC, "Address is a kernel virtual address, address={:016X}", address); -        return ERR_INVALID_ADDRESS_STATE; -    } +static ResultCode SignalToAddress(Core::System& system, VAddr address, Svc::SignalType signal_type, +                                  s32 value, s32 count) { +    LOG_TRACE(Kernel_SVC, "called, address=0x{:X}, signal_type=0x{:X}, value=0x{:X}, count=0x{:X}", +              address, signal_type, value, count); -    // If the address is not properly aligned to 4 bytes, return invalid address. -    if (!Common::IsWordAligned(address)) { -        LOG_ERROR(Kernel_SVC, "Address is not word aligned, address={:016X}", address); -        return ERR_INVALID_ADDRESS; -    } +    // Validate input. +    R_UNLESS(!Memory::IsKernelAddress(address), Svc::ResultInvalidCurrentMemory); +    R_UNLESS(Common::IsAligned(address, sizeof(s32)), Svc::ResultInvalidAddress); +    R_UNLESS(IsValidSignalType(signal_type), Svc::ResultInvalidEnumValue); -    const auto signal_type = static_cast<AddressArbiter::SignalType>(type); -    auto& address_arbiter = system.Kernel().CurrentProcess()->GetAddressArbiter(); -    return address_arbiter.SignalToAddress(address, signal_type, value, num_to_wake); +    return system.Kernel().CurrentProcess()->SignalAddressArbiter(address, signal_type, value, +                                                                  count);  } -static ResultCode SignalToAddress32(Core::System& system, u32 address, u32 type, s32 value, -                                    s32 num_to_wake) { -    return SignalToAddress(system, address, type, value, num_to_wake); +static ResultCode SignalToAddress32(Core::System& system, u32 address, Svc::SignalType signal_type, +                                    s32 value, s32 count) { +    return SignalToAddress(system, address, signal_type, value, count);  }  static void KernelDebug([[maybe_unused]] Core::System& system, diff --git a/src/core/hle/kernel/svc_common.h b/src/core/hle/kernel/svc_common.h new file mode 100644 index 000000000..4af049551 --- /dev/null +++ b/src/core/hle/kernel/svc_common.h @@ -0,0 +1,14 @@ +// Copyright 2020 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include "common/common_types.h" + +namespace Kernel::Svc { + +constexpr s32 ArgumentHandleCountMax = 0x40; +constexpr u32 HandleWaitMask{1u << 30}; + +} // namespace Kernel::Svc diff --git a/src/core/hle/kernel/svc_results.h b/src/core/hle/kernel/svc_results.h new file mode 100644 index 000000000..78282f021 --- /dev/null +++ b/src/core/hle/kernel/svc_results.h @@ -0,0 +1,20 @@ +// Copyright 2020 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include "core/hle/result.h" + +namespace Kernel::Svc { + +constexpr ResultCode ResultTerminationRequested{ErrorModule::Kernel, 59}; +constexpr ResultCode ResultInvalidAddress{ErrorModule::Kernel, 102}; +constexpr ResultCode ResultInvalidCurrentMemory{ErrorModule::Kernel, 106}; +constexpr ResultCode ResultInvalidHandle{ErrorModule::Kernel, 114}; +constexpr ResultCode ResultTimedOut{ErrorModule::Kernel, 117}; +constexpr ResultCode ResultCancelled{ErrorModule::Kernel, 118}; +constexpr ResultCode ResultInvalidEnumValue{ErrorModule::Kernel, 120}; +constexpr ResultCode ResultInvalidState{ErrorModule::Kernel, 125}; + +} // namespace Kernel::Svc diff --git a/src/core/hle/kernel/svc_types.h b/src/core/hle/kernel/svc_types.h index 11e1d8e2d..d623f7a50 100644 --- a/src/core/hle/kernel/svc_types.h +++ b/src/core/hle/kernel/svc_types.h @@ -65,4 +65,16 @@ struct MemoryInfo {      u32 padding{};  }; +enum class SignalType : u32 { +    Signal = 0, +    SignalAndIncrementIfEqual = 1, +    SignalAndModifyByWaitingCountIfEqual = 2, +}; + +enum class ArbitrationType : u32 { +    WaitIfLessThan = 0, +    DecrementAndWaitIfLessThan = 1, +    WaitIfEqual = 2, +}; +  } // namespace Kernel::Svc diff --git a/src/core/hle/kernel/svc_wrap.h b/src/core/hle/kernel/svc_wrap.h index 0b6dd9df0..a32750ed7 100644 --- a/src/core/hle/kernel/svc_wrap.h +++ b/src/core/hle/kernel/svc_wrap.h @@ -7,6 +7,7 @@  #include "common/common_types.h"  #include "core/arm/arm_interface.h"  #include "core/core.h" +#include "core/hle/kernel/svc_types.h"  #include "core/hle/result.h"  namespace Kernel { @@ -215,9 +216,10 @@ void SvcWrap64(Core::System& system) {          func(system, static_cast<u32>(Param(system, 0)), Param(system, 1), Param(system, 2)).raw);  } -template <ResultCode func(Core::System&, u32*, u64, u64, s64)> +// Used by WaitSynchronization +template <ResultCode func(Core::System&, s32*, u64, u64, s64)>  void SvcWrap64(Core::System& system) { -    u32 param_1 = 0; +    s32 param_1 = 0;      const u32 retval = func(system, ¶m_1, Param(system, 1), static_cast<u32>(Param(system, 2)),                              static_cast<s64>(Param(system, 3)))                             .raw; @@ -276,18 +278,22 @@ void SvcWrap64(Core::System& system) {      FuncReturn(system, retval);  } -template <ResultCode func(Core::System&, u64, u32, s32, s64)> +// Used by WaitForAddress +template <ResultCode func(Core::System&, u64, Svc::ArbitrationType, s32, s64)>  void SvcWrap64(Core::System& system) { -    FuncReturn(system, func(system, Param(system, 0), static_cast<u32>(Param(system, 1)), -                            static_cast<s32>(Param(system, 2)), static_cast<s64>(Param(system, 3))) -                           .raw); +    FuncReturn(system, +               func(system, Param(system, 0), static_cast<Svc::ArbitrationType>(Param(system, 1)), +                    static_cast<s32>(Param(system, 2)), static_cast<s64>(Param(system, 3))) +                   .raw);  } -template <ResultCode func(Core::System&, u64, u32, s32, s32)> +// Used by SignalToAddress +template <ResultCode func(Core::System&, u64, Svc::SignalType, s32, s32)>  void SvcWrap64(Core::System& system) { -    FuncReturn(system, func(system, Param(system, 0), static_cast<u32>(Param(system, 1)), -                            static_cast<s32>(Param(system, 2)), static_cast<s32>(Param(system, 3))) -                           .raw); +    FuncReturn(system, +               func(system, Param(system, 0), static_cast<Svc::SignalType>(Param(system, 1)), +                    static_cast<s32>(Param(system, 2)), static_cast<s32>(Param(system, 3))) +                   .raw);  }  //////////////////////////////////////////////////////////////////////////////////////////////////// @@ -503,22 +509,23 @@ void SvcWrap32(Core::System& system) {  }  // Used by WaitForAddress32 -template <ResultCode func(Core::System&, u32, u32, s32, u32, u32)> +template <ResultCode func(Core::System&, u32, Svc::ArbitrationType, s32, u32, u32)>  void SvcWrap32(Core::System& system) {      const u32 retval = func(system, static_cast<u32>(Param(system, 0)), -                            static_cast<u32>(Param(system, 1)), static_cast<s32>(Param(system, 2)), -                            static_cast<u32>(Param(system, 3)), static_cast<u32>(Param(system, 4))) +                            static_cast<Svc::ArbitrationType>(Param(system, 1)), +                            static_cast<s32>(Param(system, 2)), static_cast<u32>(Param(system, 3)), +                            static_cast<u32>(Param(system, 4)))                             .raw;      FuncReturn(system, retval);  }  // Used by SignalToAddress32 -template <ResultCode func(Core::System&, u32, u32, s32, s32)> +template <ResultCode func(Core::System&, u32, Svc::SignalType, s32, s32)>  void SvcWrap32(Core::System& system) { -    const u32 retval = -        func(system, static_cast<u32>(Param(system, 0)), static_cast<u32>(Param(system, 1)), -             static_cast<s32>(Param(system, 2)), static_cast<s32>(Param(system, 3))) -            .raw; +    const u32 retval = func(system, static_cast<u32>(Param(system, 0)), +                            static_cast<Svc::SignalType>(Param(system, 1)), +                            static_cast<s32>(Param(system, 2)), static_cast<s32>(Param(system, 3))) +                           .raw;      FuncReturn(system, retval);  } @@ -539,9 +546,9 @@ void SvcWrap32(Core::System& system) {  }  // Used by WaitSynchronization32 -template <ResultCode func(Core::System&, u32, u32, s32, u32, Handle*)> +template <ResultCode func(Core::System&, u32, u32, s32, u32, s32*)>  void SvcWrap32(Core::System& system) { -    u32 param_1 = 0; +    s32 param_1 = 0;      const u32 retval = func(system, Param32(system, 0), Param32(system, 1), Param32(system, 2),                              Param32(system, 3), ¶m_1)                             .raw; diff --git a/src/core/hle/kernel/synchronization.cpp b/src/core/hle/kernel/synchronization.cpp deleted file mode 100644 index d3f520ea2..000000000 --- a/src/core/hle/kernel/synchronization.cpp +++ /dev/null @@ -1,116 +0,0 @@ -// Copyright 2020 yuzu Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. - -#include "core/core.h" -#include "core/hle/kernel/errors.h" -#include "core/hle/kernel/handle_table.h" -#include "core/hle/kernel/k_scheduler.h" -#include "core/hle/kernel/k_scoped_scheduler_lock_and_sleep.h" -#include "core/hle/kernel/kernel.h" -#include "core/hle/kernel/synchronization.h" -#include "core/hle/kernel/synchronization_object.h" -#include "core/hle/kernel/thread.h" -#include "core/hle/kernel/time_manager.h" - -namespace Kernel { - -Synchronization::Synchronization(Core::System& system) : system{system} {} - -void Synchronization::SignalObject(SynchronizationObject& obj) const { -    auto& kernel = system.Kernel(); -    KScopedSchedulerLock lock(kernel); -    if (obj.IsSignaled()) { -        for (auto thread : obj.GetWaitingThreads()) { -            if (thread->GetSchedulingStatus() == ThreadSchedStatus::Paused) { -                if (thread->GetStatus() != ThreadStatus::WaitHLEEvent) { -                    ASSERT(thread->GetStatus() == ThreadStatus::WaitSynch); -                    ASSERT(thread->IsWaitingSync()); -                } -                thread->SetSynchronizationResults(&obj, RESULT_SUCCESS); -                thread->ResumeFromWait(); -            } -        } -        obj.ClearWaitingThreads(); -    } -} - -std::pair<ResultCode, Handle> Synchronization::WaitFor( -    std::vector<std::shared_ptr<SynchronizationObject>>& sync_objects, s64 nano_seconds) { -    auto& kernel = system.Kernel(); -    auto* const thread = kernel.CurrentScheduler()->GetCurrentThread(); -    Handle event_handle = InvalidHandle; -    { -        KScopedSchedulerLockAndSleep lock(kernel, event_handle, thread, nano_seconds); -        const auto itr = -            std::find_if(sync_objects.begin(), sync_objects.end(), -                         [thread](const std::shared_ptr<SynchronizationObject>& object) { -                             return object->IsSignaled(); -                         }); - -        if (itr != sync_objects.end()) { -            // We found a ready object, acquire it and set the result value -            SynchronizationObject* object = itr->get(); -            object->Acquire(thread); -            const u32 index = static_cast<s32>(std::distance(sync_objects.begin(), itr)); -            lock.CancelSleep(); -            return {RESULT_SUCCESS, index}; -        } - -        if (nano_seconds == 0) { -            lock.CancelSleep(); -            return {RESULT_TIMEOUT, InvalidHandle}; -        } - -        if (thread->IsPendingTermination()) { -            lock.CancelSleep(); -            return {ERR_THREAD_TERMINATING, InvalidHandle}; -        } - -        if (thread->IsSyncCancelled()) { -            thread->SetSyncCancelled(false); -            lock.CancelSleep(); -            return {ERR_SYNCHRONIZATION_CANCELED, InvalidHandle}; -        } - -        for (auto& object : sync_objects) { -            object->AddWaitingThread(SharedFrom(thread)); -        } - -        thread->SetSynchronizationObjects(&sync_objects); -        thread->SetSynchronizationResults(nullptr, RESULT_TIMEOUT); -        thread->SetStatus(ThreadStatus::WaitSynch); -        thread->SetWaitingSync(true); -    } -    thread->SetWaitingSync(false); - -    if (event_handle != InvalidHandle) { -        auto& time_manager = kernel.TimeManager(); -        time_manager.UnscheduleTimeEvent(event_handle); -    } - -    { -        KScopedSchedulerLock lock(kernel); -        ResultCode signaling_result = thread->GetSignalingResult(); -        SynchronizationObject* signaling_object = thread->GetSignalingObject(); -        thread->SetSynchronizationObjects(nullptr); -        auto shared_thread = SharedFrom(thread); -        for (auto& obj : sync_objects) { -            obj->RemoveWaitingThread(shared_thread); -        } -        if (signaling_object != nullptr) { -            const auto itr = std::find_if( -                sync_objects.begin(), sync_objects.end(), -                [signaling_object](const std::shared_ptr<SynchronizationObject>& object) { -                    return object.get() == signaling_object; -                }); -            ASSERT(itr != sync_objects.end()); -            signaling_object->Acquire(thread); -            const u32 index = static_cast<s32>(std::distance(sync_objects.begin(), itr)); -            return {signaling_result, index}; -        } -        return {signaling_result, -1}; -    } -} - -} // namespace Kernel diff --git a/src/core/hle/kernel/synchronization.h b/src/core/hle/kernel/synchronization.h deleted file mode 100644 index 379f4b1d3..000000000 --- a/src/core/hle/kernel/synchronization.h +++ /dev/null @@ -1,44 +0,0 @@ -// Copyright 2020 yuzu Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. - -#pragma once - -#include <memory> -#include <utility> -#include <vector> - -#include "core/hle/kernel/object.h" -#include "core/hle/result.h" - -namespace Core { -class System; -} // namespace Core - -namespace Kernel { - -class SynchronizationObject; - -/** - * The 'Synchronization' class is an interface for handling synchronization methods - * used by Synchronization objects and synchronization SVCs. This centralizes processing of - * such - */ -class Synchronization { -public: -    explicit Synchronization(Core::System& system); - -    /// Signals a synchronization object, waking up all its waiting threads -    void SignalObject(SynchronizationObject& obj) const; - -    /// Tries to see if waiting for any of the sync_objects is necessary, if not -    /// it returns Success and the handle index of the signaled sync object. In -    /// case not, the current thread will be locked and wait for nano_seconds or -    /// for a synchronization object to signal. -    std::pair<ResultCode, Handle> WaitFor( -        std::vector<std::shared_ptr<SynchronizationObject>>& sync_objects, s64 nano_seconds); - -private: -    Core::System& system; -}; -} // namespace Kernel diff --git a/src/core/hle/kernel/synchronization_object.cpp b/src/core/hle/kernel/synchronization_object.cpp deleted file mode 100644 index ba4d39157..000000000 --- a/src/core/hle/kernel/synchronization_object.cpp +++ /dev/null @@ -1,49 +0,0 @@ -// Copyright 2014 Citra Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. - -#include <algorithm> -#include "common/assert.h" -#include "common/common_types.h" -#include "common/logging/log.h" -#include "core/core.h" -#include "core/hle/kernel/kernel.h" -#include "core/hle/kernel/object.h" -#include "core/hle/kernel/process.h" -#include "core/hle/kernel/synchronization.h" -#include "core/hle/kernel/synchronization_object.h" -#include "core/hle/kernel/thread.h" - -namespace Kernel { - -SynchronizationObject::SynchronizationObject(KernelCore& kernel) : Object{kernel} {} -SynchronizationObject::~SynchronizationObject() = default; - -void SynchronizationObject::Signal() { -    kernel.Synchronization().SignalObject(*this); -} - -void SynchronizationObject::AddWaitingThread(std::shared_ptr<Thread> thread) { -    auto itr = std::find(waiting_threads.begin(), waiting_threads.end(), thread); -    if (itr == waiting_threads.end()) -        waiting_threads.push_back(std::move(thread)); -} - -void SynchronizationObject::RemoveWaitingThread(std::shared_ptr<Thread> thread) { -    auto itr = std::find(waiting_threads.begin(), waiting_threads.end(), thread); -    // If a thread passed multiple handles to the same object, -    // the kernel might attempt to remove the thread from the object's -    // waiting threads list multiple times. -    if (itr != waiting_threads.end()) -        waiting_threads.erase(itr); -} - -void SynchronizationObject::ClearWaitingThreads() { -    waiting_threads.clear(); -} - -const std::vector<std::shared_ptr<Thread>>& SynchronizationObject::GetWaitingThreads() const { -    return waiting_threads; -} - -} // namespace Kernel diff --git a/src/core/hle/kernel/synchronization_object.h b/src/core/hle/kernel/synchronization_object.h deleted file mode 100644 index 7408ed51f..000000000 --- a/src/core/hle/kernel/synchronization_object.h +++ /dev/null @@ -1,77 +0,0 @@ -// Copyright 2014 Citra Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. - -#pragma once - -#include <atomic> -#include <memory> -#include <vector> - -#include "core/hle/kernel/object.h" - -namespace Kernel { - -class KernelCore; -class Synchronization; -class Thread; - -/// Class that represents a Kernel object that a thread can be waiting on -class SynchronizationObject : public Object { -public: -    explicit SynchronizationObject(KernelCore& kernel); -    ~SynchronizationObject() override; - -    /** -     * Check if the specified thread should wait until the object is available -     * @param thread The thread about which we're deciding. -     * @return True if the current thread should wait due to this object being unavailable -     */ -    virtual bool ShouldWait(const Thread* thread) const = 0; - -    /// Acquire/lock the object for the specified thread if it is available -    virtual void Acquire(Thread* thread) = 0; - -    /// Signal this object -    virtual void Signal(); - -    virtual bool IsSignaled() const { -        return is_signaled; -    } - -    /** -     * Add a thread to wait on this object -     * @param thread Pointer to thread to add -     */ -    void AddWaitingThread(std::shared_ptr<Thread> thread); - -    /** -     * Removes a thread from waiting on this object (e.g. if it was resumed already) -     * @param thread Pointer to thread to remove -     */ -    void RemoveWaitingThread(std::shared_ptr<Thread> thread); - -    /// Get a const reference to the waiting threads list for debug use -    const std::vector<std::shared_ptr<Thread>>& GetWaitingThreads() const; - -    void ClearWaitingThreads(); - -protected: -    std::atomic_bool is_signaled{}; // Tells if this sync object is signaled - -private: -    /// Threads waiting for this object to become available -    std::vector<std::shared_ptr<Thread>> waiting_threads; -}; - -// Specialization of DynamicObjectCast for SynchronizationObjects -template <> -inline std::shared_ptr<SynchronizationObject> DynamicObjectCast<SynchronizationObject>( -    std::shared_ptr<Object> object) { -    if (object != nullptr && object->IsWaitable()) { -        return std::static_pointer_cast<SynchronizationObject>(object); -    } -    return nullptr; -} - -} // namespace Kernel diff --git a/src/core/hle/kernel/thread.cpp b/src/core/hle/kernel/thread.cpp index a4f9e0d97..d97323255 100644 --- a/src/core/hle/kernel/thread.cpp +++ b/src/core/hle/kernel/thread.cpp @@ -17,9 +17,11 @@  #include "core/hardware_properties.h"  #include "core/hle/kernel/errors.h"  #include "core/hle/kernel/handle_table.h" +#include "core/hle/kernel/k_condition_variable.h"  #include "core/hle/kernel/k_scheduler.h"  #include "core/hle/kernel/k_scoped_scheduler_lock_and_sleep.h"  #include "core/hle/kernel/kernel.h" +#include "core/hle/kernel/memory/memory_layout.h"  #include "core/hle/kernel/object.h"  #include "core/hle/kernel/process.h"  #include "core/hle/kernel/thread.h" @@ -34,26 +36,19 @@  namespace Kernel { -bool Thread::ShouldWait(const Thread* thread) const { -    return status != ThreadStatus::Dead; -} -  bool Thread::IsSignaled() const { -    return status == ThreadStatus::Dead; -} - -void Thread::Acquire(Thread* thread) { -    ASSERT_MSG(!ShouldWait(thread), "object unavailable!"); +    return signaled;  } -Thread::Thread(KernelCore& kernel) : SynchronizationObject{kernel} {} +Thread::Thread(KernelCore& kernel) : KSynchronizationObject{kernel} {}  Thread::~Thread() = default;  void Thread::Stop() {      {          KScopedSchedulerLock lock(kernel); -        SetStatus(ThreadStatus::Dead); -        Signal(); +        SetState(ThreadState::Terminated); +        signaled = true; +        NotifyAvailable();          kernel.GlobalHandleTable().Close(global_handle);          if (owner_process) { @@ -67,59 +62,27 @@ void Thread::Stop() {      global_handle = 0;  } -void Thread::ResumeFromWait() { +void Thread::Wakeup() {      KScopedSchedulerLock lock(kernel); -    switch (status) { -    case ThreadStatus::Paused: -    case ThreadStatus::WaitSynch: -    case ThreadStatus::WaitHLEEvent: -    case ThreadStatus::WaitSleep: -    case ThreadStatus::WaitIPC: -    case ThreadStatus::WaitMutex: -    case ThreadStatus::WaitCondVar: -    case ThreadStatus::WaitArb: -    case ThreadStatus::Dormant: -        break; - -    case ThreadStatus::Ready: -        // The thread's wakeup callback must have already been cleared when the thread was first -        // awoken. -        ASSERT(hle_callback == nullptr); -        // If the thread is waiting on multiple wait objects, it might be awoken more than once -        // before actually resuming. We can ignore subsequent wakeups if the thread status has -        // already been set to ThreadStatus::Ready. -        return; -    case ThreadStatus::Dead: -        // This should never happen, as threads must complete before being stopped. -        DEBUG_ASSERT_MSG(false, "Thread with object id {} cannot be resumed because it's DEAD.", -                         GetObjectId()); -        return; -    } - -    SetStatus(ThreadStatus::Ready); -} - -void Thread::OnWakeUp() { -    KScopedSchedulerLock lock(kernel); -    SetStatus(ThreadStatus::Ready); +    SetState(ThreadState::Runnable);  }  ResultCode Thread::Start() {      KScopedSchedulerLock lock(kernel); -    SetStatus(ThreadStatus::Ready); +    SetState(ThreadState::Runnable);      return RESULT_SUCCESS;  }  void Thread::CancelWait() {      KScopedSchedulerLock lock(kernel); -    if (GetSchedulingStatus() != ThreadSchedStatus::Paused || !is_waiting_on_sync) { +    if (GetState() != ThreadState::Waiting || !is_cancellable) {          is_sync_cancelled = true;          return;      }      // TODO(Blinkhawk): Implement cancel of server session      is_sync_cancelled = false;      SetSynchronizationResults(nullptr, ERR_SYNCHRONIZATION_CANCELED); -    SetStatus(ThreadStatus::Ready); +    SetState(ThreadState::Runnable);  }  static void ResetThreadContext32(Core::ARM_Interface::ThreadContext32& context, u32 stack_top, @@ -183,25 +146,24 @@ ResultVal<std::shared_ptr<Thread>> Thread::Create(Core::System& system, ThreadTy      std::shared_ptr<Thread> thread = std::make_shared<Thread>(kernel);      thread->thread_id = kernel.CreateNewThreadID(); -    thread->status = ThreadStatus::Dormant; +    thread->thread_state = ThreadState::Initialized;      thread->entry_point = entry_point;      thread->stack_top = stack_top;      thread->disable_count = 1;      thread->tpidr_el0 = 0; -    thread->nominal_priority = thread->current_priority = priority; +    thread->current_priority = priority; +    thread->base_priority = priority; +    thread->lock_owner = nullptr;      thread->schedule_count = -1;      thread->last_scheduled_tick = 0;      thread->processor_id = processor_id;      thread->ideal_core = processor_id;      thread->affinity_mask.SetAffinity(processor_id, true); -    thread->wait_objects = nullptr; -    thread->mutex_wait_address = 0; -    thread->condvar_wait_address = 0; -    thread->wait_handle = 0;      thread->name = std::move(name);      thread->global_handle = kernel.GlobalHandleTable().Create(thread).Unwrap();      thread->owner_process = owner_process;      thread->type = type_flags; +    thread->signaled = false;      if ((type_flags & THREADTYPE_IDLE) == 0) {          auto& scheduler = kernel.GlobalSchedulerContext();          scheduler.AddThread(thread); @@ -226,153 +188,185 @@ ResultVal<std::shared_ptr<Thread>> Thread::Create(Core::System& system, ThreadTy      return MakeResult<std::shared_ptr<Thread>>(std::move(thread));  } -void Thread::SetPriority(u32 priority) { -    KScopedSchedulerLock lock(kernel); +void Thread::SetBasePriority(u32 priority) {      ASSERT_MSG(priority <= THREADPRIO_LOWEST && priority >= THREADPRIO_HIGHEST,                 "Invalid priority value."); -    nominal_priority = priority; -    UpdatePriority(); + +    KScopedSchedulerLock lock(kernel); + +    // Change our base priority. +    base_priority = priority; + +    // Perform a priority restoration. +    RestorePriority(kernel, this);  } -void Thread::SetSynchronizationResults(SynchronizationObject* object, ResultCode result) { +void Thread::SetSynchronizationResults(KSynchronizationObject* object, ResultCode result) {      signaling_object = object;      signaling_result = result;  } -s32 Thread::GetSynchronizationObjectIndex(std::shared_ptr<SynchronizationObject> object) const { -    ASSERT_MSG(!wait_objects->empty(), "Thread is not waiting for anything"); -    const auto match = std::find(wait_objects->rbegin(), wait_objects->rend(), object); -    return static_cast<s32>(std::distance(match, wait_objects->rend()) - 1); -} -  VAddr Thread::GetCommandBufferAddress() const {      // Offset from the start of TLS at which the IPC command buffer begins.      constexpr u64 command_header_offset = 0x80;      return GetTLSAddress() + command_header_offset;  } -void Thread::SetStatus(ThreadStatus new_status) { -    if (new_status == status) { -        return; -    } +void Thread::SetState(ThreadState state) { +    KScopedSchedulerLock sl(kernel); -    switch (new_status) { -    case ThreadStatus::Ready: -        SetSchedulingStatus(ThreadSchedStatus::Runnable); -        break; -    case ThreadStatus::Dormant: -        SetSchedulingStatus(ThreadSchedStatus::None); -        break; -    case ThreadStatus::Dead: -        SetSchedulingStatus(ThreadSchedStatus::Exited); -        break; -    default: -        SetSchedulingStatus(ThreadSchedStatus::Paused); -        break; -    } +    // Clear debugging state +    SetMutexWaitAddressForDebugging({}); +    SetWaitReasonForDebugging({}); -    status = new_status; +    const ThreadState old_state = thread_state; +    thread_state = +        static_cast<ThreadState>((old_state & ~ThreadState::Mask) | (state & ThreadState::Mask)); +    if (thread_state != old_state) { +        KScheduler::OnThreadStateChanged(kernel, this, old_state); +    }  } -void Thread::AddMutexWaiter(std::shared_ptr<Thread> thread) { -    if (thread->lock_owner.get() == this) { -        // If the thread is already waiting for this thread to release the mutex, ensure that the -        // waiters list is consistent and return without doing anything. -        const auto iter = std::find(wait_mutex_threads.begin(), wait_mutex_threads.end(), thread); -        ASSERT(iter != wait_mutex_threads.end()); -        return; +void Thread::AddWaiterImpl(Thread* thread) { +    ASSERT(kernel.GlobalSchedulerContext().IsLocked()); + +    // Find the right spot to insert the waiter. +    auto it = waiter_list.begin(); +    while (it != waiter_list.end()) { +        if (it->GetPriority() > thread->GetPriority()) { +            break; +        } +        it++;      } -    // A thread can't wait on two different mutexes at the same time. -    ASSERT(thread->lock_owner == nullptr); +    // Keep track of how many kernel waiters we have. +    if (Memory::IsKernelAddressKey(thread->GetAddressKey())) { +        ASSERT((num_kernel_waiters++) >= 0); +    } -    // Ensure that the thread is not already in the list of mutex waiters -    const auto iter = std::find(wait_mutex_threads.begin(), wait_mutex_threads.end(), thread); -    ASSERT(iter == wait_mutex_threads.end()); +    // Insert the waiter. +    waiter_list.insert(it, *thread); +    thread->SetLockOwner(this); +} -    // Keep the list in an ordered fashion -    const auto insertion_point = std::find_if( -        wait_mutex_threads.begin(), wait_mutex_threads.end(), -        [&thread](const auto& entry) { return entry->GetPriority() > thread->GetPriority(); }); -    wait_mutex_threads.insert(insertion_point, thread); -    thread->lock_owner = SharedFrom(this); +void Thread::RemoveWaiterImpl(Thread* thread) { +    ASSERT(kernel.GlobalSchedulerContext().IsLocked()); -    UpdatePriority(); -} +    // Keep track of how many kernel waiters we have. +    if (Memory::IsKernelAddressKey(thread->GetAddressKey())) { +        ASSERT((num_kernel_waiters--) > 0); +    } -void Thread::RemoveMutexWaiter(std::shared_ptr<Thread> thread) { -    ASSERT(thread->lock_owner.get() == this); +    // Remove the waiter. +    waiter_list.erase(waiter_list.iterator_to(*thread)); +    thread->SetLockOwner(nullptr); +} -    // Ensure that the thread is in the list of mutex waiters -    const auto iter = std::find(wait_mutex_threads.begin(), wait_mutex_threads.end(), thread); -    ASSERT(iter != wait_mutex_threads.end()); +void Thread::RestorePriority(KernelCore& kernel, Thread* thread) { +    ASSERT(kernel.GlobalSchedulerContext().IsLocked()); -    wait_mutex_threads.erase(iter); +    while (true) { +        // We want to inherit priority where possible. +        s32 new_priority = thread->GetBasePriority(); +        if (thread->HasWaiters()) { +            new_priority = std::min(new_priority, thread->waiter_list.front().GetPriority()); +        } -    thread->lock_owner = nullptr; -    UpdatePriority(); -} +        // If the priority we would inherit is not different from ours, don't do anything. +        if (new_priority == thread->GetPriority()) { +            return; +        } -void Thread::UpdatePriority() { -    // If any of the threads waiting on the mutex have a higher priority -    // (taking into account priority inheritance), then this thread inherits -    // that thread's priority. -    u32 new_priority = nominal_priority; -    if (!wait_mutex_threads.empty()) { -        if (wait_mutex_threads.front()->current_priority < new_priority) { -            new_priority = wait_mutex_threads.front()->current_priority; +        // Ensure we don't violate condition variable red black tree invariants. +        if (auto* cv_tree = thread->GetConditionVariableTree(); cv_tree != nullptr) { +            BeforeUpdatePriority(kernel, cv_tree, thread);          } -    } -    if (new_priority == current_priority) { -        return; -    } +        // Change the priority. +        const s32 old_priority = thread->GetPriority(); +        thread->SetPriority(new_priority); -    if (GetStatus() == ThreadStatus::WaitCondVar) { -        owner_process->RemoveConditionVariableThread(SharedFrom(this)); -    } +        // Restore the condition variable, if relevant. +        if (auto* cv_tree = thread->GetConditionVariableTree(); cv_tree != nullptr) { +            AfterUpdatePriority(kernel, cv_tree, thread); +        } -    SetCurrentPriority(new_priority); +        // Update the scheduler. +        KScheduler::OnThreadPriorityChanged(kernel, thread, old_priority); -    if (GetStatus() == ThreadStatus::WaitCondVar) { -        owner_process->InsertConditionVariableThread(SharedFrom(this)); -    } +        // Keep the lock owner up to date. +        Thread* lock_owner = thread->GetLockOwner(); +        if (lock_owner == nullptr) { +            return; +        } -    if (!lock_owner) { -        return; +        // Update the thread in the lock owner's sorted list, and continue inheriting. +        lock_owner->RemoveWaiterImpl(thread); +        lock_owner->AddWaiterImpl(thread); +        thread = lock_owner;      } +} -    // Ensure that the thread is within the correct location in the waiting list. -    auto old_owner = lock_owner; -    lock_owner->RemoveMutexWaiter(SharedFrom(this)); -    old_owner->AddMutexWaiter(SharedFrom(this)); - -    // Recursively update the priority of the thread that depends on the priority of this one. -    lock_owner->UpdatePriority(); +void Thread::AddWaiter(Thread* thread) { +    AddWaiterImpl(thread); +    RestorePriority(kernel, this);  } -bool Thread::AllSynchronizationObjectsReady() const { -    return std::none_of(wait_objects->begin(), wait_objects->end(), -                        [this](const std::shared_ptr<SynchronizationObject>& object) { -                            return object->ShouldWait(this); -                        }); +void Thread::RemoveWaiter(Thread* thread) { +    RemoveWaiterImpl(thread); +    RestorePriority(kernel, this);  } -bool Thread::InvokeHLECallback(std::shared_ptr<Thread> thread) { -    ASSERT(hle_callback); -    return hle_callback(std::move(thread)); +Thread* Thread::RemoveWaiterByKey(s32* out_num_waiters, VAddr key) { +    ASSERT(kernel.GlobalSchedulerContext().IsLocked()); + +    s32 num_waiters{}; +    Thread* next_lock_owner{}; +    auto it = waiter_list.begin(); +    while (it != waiter_list.end()) { +        if (it->GetAddressKey() == key) { +            Thread* thread = std::addressof(*it); + +            // Keep track of how many kernel waiters we have. +            if (Memory::IsKernelAddressKey(thread->GetAddressKey())) { +                ASSERT((num_kernel_waiters--) > 0); +            } +            it = waiter_list.erase(it); + +            // Update the next lock owner. +            if (next_lock_owner == nullptr) { +                next_lock_owner = thread; +                next_lock_owner->SetLockOwner(nullptr); +            } else { +                next_lock_owner->AddWaiterImpl(thread); +            } +            num_waiters++; +        } else { +            it++; +        } +    } + +    // Do priority updates, if we have a next owner. +    if (next_lock_owner) { +        RestorePriority(kernel, this); +        RestorePriority(kernel, next_lock_owner); +    } + +    // Return output. +    *out_num_waiters = num_waiters; +    return next_lock_owner;  }  ResultCode Thread::SetActivity(ThreadActivity value) {      KScopedSchedulerLock lock(kernel); -    auto sched_status = GetSchedulingStatus(); +    auto sched_status = GetState(); -    if (sched_status != ThreadSchedStatus::Runnable && sched_status != ThreadSchedStatus::Paused) { +    if (sched_status != ThreadState::Runnable && sched_status != ThreadState::Waiting) {          return ERR_INVALID_STATE;      } -    if (IsPendingTermination()) { +    if (IsTerminationRequested()) {          return RESULT_SUCCESS;      } @@ -394,7 +388,8 @@ ResultCode Thread::Sleep(s64 nanoseconds) {      Handle event_handle{};      {          KScopedSchedulerLockAndSleep lock(kernel, event_handle, this, nanoseconds); -        SetStatus(ThreadStatus::WaitSleep); +        SetState(ThreadState::Waiting); +        SetWaitReasonForDebugging(ThreadWaitReasonForDebugging::Sleep);      }      if (event_handle != InvalidHandle) { @@ -405,34 +400,21 @@ ResultCode Thread::Sleep(s64 nanoseconds) {  }  void Thread::AddSchedulingFlag(ThreadSchedFlags flag) { -    const u32 old_state = scheduling_state; +    const auto old_state = GetRawState();      pausing_state |= static_cast<u32>(flag); -    const u32 base_scheduling = static_cast<u32>(GetSchedulingStatus()); -    scheduling_state = base_scheduling | pausing_state; +    const auto base_scheduling = GetState(); +    thread_state = base_scheduling | static_cast<ThreadState>(pausing_state);      KScheduler::OnThreadStateChanged(kernel, this, old_state);  }  void Thread::RemoveSchedulingFlag(ThreadSchedFlags flag) { -    const u32 old_state = scheduling_state; +    const auto old_state = GetRawState();      pausing_state &= ~static_cast<u32>(flag); -    const u32 base_scheduling = static_cast<u32>(GetSchedulingStatus()); -    scheduling_state = base_scheduling | pausing_state; +    const auto base_scheduling = GetState(); +    thread_state = base_scheduling | static_cast<ThreadState>(pausing_state);      KScheduler::OnThreadStateChanged(kernel, this, old_state);  } -void Thread::SetSchedulingStatus(ThreadSchedStatus new_status) { -    const u32 old_state = scheduling_state; -    scheduling_state = (scheduling_state & static_cast<u32>(ThreadSchedMasks::HighMask)) | -                       static_cast<u32>(new_status); -    KScheduler::OnThreadStateChanged(kernel, this, old_state); -} - -void Thread::SetCurrentPriority(u32 new_priority) { -    const u32 old_priority = std::exchange(current_priority, new_priority); -    KScheduler::OnThreadPriorityChanged(kernel, this, kernel.CurrentScheduler()->GetCurrentThread(), -                                        old_priority); -} -  ResultCode Thread::SetCoreAndAffinityMask(s32 new_core, u64 new_affinity_mask) {      KScopedSchedulerLock lock(kernel);      const auto HighestSetCore = [](u64 mask, u32 max_cores) { diff --git a/src/core/hle/kernel/thread.h b/src/core/hle/kernel/thread.h index 11ef29888..6b66c9a0e 100644 --- a/src/core/hle/kernel/thread.h +++ b/src/core/hle/kernel/thread.h @@ -6,16 +6,21 @@  #include <array>  #include <functional> +#include <span>  #include <string>  #include <utility>  #include <vector> +#include <boost/intrusive/list.hpp> +  #include "common/common_types.h" +#include "common/intrusive_red_black_tree.h"  #include "common/spin_lock.h"  #include "core/arm/arm_interface.h"  #include "core/hle/kernel/k_affinity_mask.h" +#include "core/hle/kernel/k_synchronization_object.h"  #include "core/hle/kernel/object.h" -#include "core/hle/kernel/synchronization_object.h" +#include "core/hle/kernel/svc_common.h"  #include "core/hle/result.h"  namespace Common { @@ -73,19 +78,24 @@ enum ThreadProcessorId : s32 {                                       (1 << THREADPROCESSORID_2) | (1 << THREADPROCESSORID_3)  }; -enum class ThreadStatus { -    Ready,        ///< Ready to run -    Paused,       ///< Paused by SetThreadActivity or debug -    WaitHLEEvent, ///< Waiting for hle event to finish -    WaitSleep,    ///< Waiting due to a SleepThread SVC -    WaitIPC,      ///< Waiting for the reply from an IPC request -    WaitSynch,    ///< Waiting due to WaitSynchronization -    WaitMutex,    ///< Waiting due to an ArbitrateLock svc -    WaitCondVar,  ///< Waiting due to an WaitProcessWideKey svc -    WaitArb,      ///< Waiting due to a SignalToAddress/WaitForAddress svc -    Dormant,      ///< Created but not yet made ready -    Dead          ///< Run to completion, or forcefully terminated +enum class ThreadState : u16 { +    Initialized = 0, +    Waiting = 1, +    Runnable = 2, +    Terminated = 3, + +    SuspendShift = 4, +    Mask = (1 << SuspendShift) - 1, + +    ProcessSuspended = (1 << (0 + SuspendShift)), +    ThreadSuspended = (1 << (1 + SuspendShift)), +    DebugSuspended = (1 << (2 + SuspendShift)), +    BacktraceSuspended = (1 << (3 + SuspendShift)), +    InitSuspended = (1 << (4 + SuspendShift)), + +    SuspendFlagMask = ((1 << 5) - 1) << SuspendShift,  }; +DECLARE_ENUM_FLAG_OPERATORS(ThreadState);  enum class ThreadWakeupReason {      Signal, // The thread was woken up by WakeupAllWaitingThreads due to an object signal. @@ -97,13 +107,6 @@ enum class ThreadActivity : u32 {      Paused = 1,  }; -enum class ThreadSchedStatus : u32 { -    None = 0, -    Paused = 1, -    Runnable = 2, -    Exited = 3, -}; -  enum class ThreadSchedFlags : u32 {      ProcessPauseFlag = 1 << 4,      ThreadPauseFlag = 1 << 5, @@ -111,13 +114,20 @@ enum class ThreadSchedFlags : u32 {      KernelInitPauseFlag = 1 << 8,  }; -enum class ThreadSchedMasks : u32 { -    LowMask = 0x000f, -    HighMask = 0xfff0, -    ForcePauseMask = 0x0070, +enum class ThreadWaitReasonForDebugging : u32 { +    None,            ///< Thread is not waiting +    Sleep,           ///< Thread is waiting due to a SleepThread SVC +    IPC,             ///< Thread is waiting for the reply from an IPC request +    Synchronization, ///< Thread is waiting due to a WaitSynchronization SVC +    ConditionVar,    ///< Thread is waiting due to a WaitProcessWideKey SVC +    Arbitration,     ///< Thread is waiting due to a SignalToAddress/WaitForAddress SVC +    Suspended,       ///< Thread is waiting due to process suspension  }; -class Thread final : public SynchronizationObject { +class Thread final : public KSynchronizationObject, public boost::intrusive::list_base_hook<> { +    friend class KScheduler; +    friend class Process; +  public:      explicit Thread(KernelCore& kernel);      ~Thread() override; @@ -127,10 +137,6 @@ public:      using ThreadContext32 = Core::ARM_Interface::ThreadContext32;      using ThreadContext64 = Core::ARM_Interface::ThreadContext64; -    using ThreadSynchronizationObjects = std::vector<std::shared_ptr<SynchronizationObject>>; - -    using HLECallback = std::function<bool(std::shared_ptr<Thread> thread)>; -      /**       * Creates and returns a new thread. The new thread is immediately scheduled       * @param system The instance of the whole system @@ -186,59 +192,54 @@ public:          return HANDLE_TYPE;      } -    bool ShouldWait(const Thread* thread) const override; -    void Acquire(Thread* thread) override; -    bool IsSignaled() const override; -      /**       * Gets the thread's current priority       * @return The current thread's priority       */ -    u32 GetPriority() const { +    [[nodiscard]] s32 GetPriority() const {          return current_priority;      }      /** +     * Sets the thread's current priority. +     * @param priority The new priority. +     */ +    void SetPriority(s32 priority) { +        current_priority = priority; +    } + +    /**       * Gets the thread's nominal priority.       * @return The current thread's nominal priority.       */ -    u32 GetNominalPriority() const { -        return nominal_priority; +    [[nodiscard]] s32 GetBasePriority() const { +        return base_priority;      }      /** -     * Sets the thread's current priority -     * @param priority The new priority +     * Sets the thread's nominal priority. +     * @param priority The new priority.       */ -    void SetPriority(u32 priority); - -    /// Adds a thread to the list of threads that are waiting for a lock held by this thread. -    void AddMutexWaiter(std::shared_ptr<Thread> thread); - -    /// Removes a thread from the list of threads that are waiting for a lock held by this thread. -    void RemoveMutexWaiter(std::shared_ptr<Thread> thread); - -    /// Recalculates the current priority taking into account priority inheritance. -    void UpdatePriority(); +    void SetBasePriority(u32 priority);      /// Changes the core that the thread is running or scheduled to run on. -    ResultCode SetCoreAndAffinityMask(s32 new_core, u64 new_affinity_mask); +    [[nodiscard]] ResultCode SetCoreAndAffinityMask(s32 new_core, u64 new_affinity_mask);      /**       * Gets the thread's thread ID       * @return The thread's ID       */ -    u64 GetThreadID() const { +    [[nodiscard]] u64 GetThreadID() const {          return thread_id;      }      /// Resumes a thread from waiting -    void ResumeFromWait(); - -    void OnWakeUp(); +    void Wakeup();      ResultCode Start(); +    virtual bool IsSignaled() const override; +      /// Cancels a waiting operation that this thread may or may not be within.      ///      /// When the thread is within a waiting state, this will set the thread's @@ -247,29 +248,20 @@ public:      ///      void CancelWait(); -    void SetSynchronizationResults(SynchronizationObject* object, ResultCode result); +    void SetSynchronizationResults(KSynchronizationObject* object, ResultCode result); -    SynchronizationObject* GetSignalingObject() const { -        return signaling_object; +    void SetSyncedObject(KSynchronizationObject* object, ResultCode result) { +        SetSynchronizationResults(object, result);      } -    ResultCode GetSignalingResult() const { +    ResultCode GetWaitResult(KSynchronizationObject** out) const { +        *out = signaling_object;          return signaling_result;      } -    /** -     * Retrieves the index that this particular object occupies in the list of objects -     * that the thread passed to WaitSynchronization, starting the search from the last element. -     * -     * It is used to set the output index of WaitSynchronization when the thread is awakened. -     * -     * When a thread wakes up due to an object signal, the kernel will use the index of the last -     * matching object in the wait objects list in case of having multiple instances of the same -     * object in the list. -     * -     * @param object Object to query the index of. -     */ -    s32 GetSynchronizationObjectIndex(std::shared_ptr<SynchronizationObject> object) const; +    ResultCode GetSignalingResult() const { +        return signaling_result; +    }      /**       * Stops a thread, invalidating it from further use @@ -341,18 +333,22 @@ public:      std::shared_ptr<Common::Fiber>& GetHostContext(); -    ThreadStatus GetStatus() const { -        return status; +    ThreadState GetState() const { +        return thread_state & ThreadState::Mask; +    } + +    ThreadState GetRawState() const { +        return thread_state;      } -    void SetStatus(ThreadStatus new_status); +    void SetState(ThreadState state);      s64 GetLastScheduledTick() const { -        return this->last_scheduled_tick; +        return last_scheduled_tick;      }      void SetLastScheduledTick(s64 tick) { -        this->last_scheduled_tick = tick; +        last_scheduled_tick = tick;      }      u64 GetTotalCPUTimeTicks() const { @@ -387,98 +383,18 @@ public:          return owner_process;      } -    const ThreadSynchronizationObjects& GetSynchronizationObjects() const { -        return *wait_objects; -    } - -    void SetSynchronizationObjects(ThreadSynchronizationObjects* objects) { -        wait_objects = objects; -    } - -    void ClearSynchronizationObjects() { -        for (const auto& waiting_object : *wait_objects) { -            waiting_object->RemoveWaitingThread(SharedFrom(this)); -        } -        wait_objects->clear(); -    } - -    /// Determines whether all the objects this thread is waiting on are ready. -    bool AllSynchronizationObjectsReady() const; -      const MutexWaitingThreads& GetMutexWaitingThreads() const {          return wait_mutex_threads;      }      Thread* GetLockOwner() const { -        return lock_owner.get(); -    } - -    void SetLockOwner(std::shared_ptr<Thread> owner) { -        lock_owner = std::move(owner); -    } - -    VAddr GetCondVarWaitAddress() const { -        return condvar_wait_address; -    } - -    void SetCondVarWaitAddress(VAddr address) { -        condvar_wait_address = address; -    } - -    VAddr GetMutexWaitAddress() const { -        return mutex_wait_address; -    } - -    void SetMutexWaitAddress(VAddr address) { -        mutex_wait_address = address; -    } - -    Handle GetWaitHandle() const { -        return wait_handle; -    } - -    void SetWaitHandle(Handle handle) { -        wait_handle = handle; -    } - -    VAddr GetArbiterWaitAddress() const { -        return arb_wait_address; -    } - -    void SetArbiterWaitAddress(VAddr address) { -        arb_wait_address = address; -    } - -    bool HasHLECallback() const { -        return hle_callback != nullptr; -    } - -    void SetHLECallback(HLECallback callback) { -        hle_callback = std::move(callback); -    } - -    void SetHLETimeEvent(Handle time_event) { -        hle_time_event = time_event; -    } - -    void SetHLESyncObject(SynchronizationObject* object) { -        hle_object = object; -    } - -    Handle GetHLETimeEvent() const { -        return hle_time_event; -    } - -    SynchronizationObject* GetHLESyncObject() const { -        return hle_object; +        return lock_owner;      } -    void InvalidateHLECallback() { -        SetHLECallback(nullptr); +    void SetLockOwner(Thread* owner) { +        lock_owner = owner;      } -    bool InvokeHLECallback(std::shared_ptr<Thread> thread); -      u32 GetIdealCore() const {          return ideal_core;      } @@ -493,20 +409,11 @@ public:      ResultCode Sleep(s64 nanoseconds);      s64 GetYieldScheduleCount() const { -        return this->schedule_count; +        return schedule_count;      }      void SetYieldScheduleCount(s64 count) { -        this->schedule_count = count; -    } - -    ThreadSchedStatus GetSchedulingStatus() const { -        return static_cast<ThreadSchedStatus>(scheduling_state & -                                              static_cast<u32>(ThreadSchedMasks::LowMask)); -    } - -    bool IsRunnable() const { -        return scheduling_state == static_cast<u32>(ThreadSchedStatus::Runnable); +        schedule_count = count;      }      bool IsRunning() const { @@ -517,36 +424,32 @@ public:          is_running = value;      } -    bool IsSyncCancelled() const { +    bool IsWaitCancelled() const {          return is_sync_cancelled;      } -    void SetSyncCancelled(bool value) { -        is_sync_cancelled = value; +    void ClearWaitCancelled() { +        is_sync_cancelled = false;      }      Handle GetGlobalHandle() const {          return global_handle;      } -    bool IsWaitingForArbitration() const { -        return waiting_for_arbitration; +    bool IsCancellable() const { +        return is_cancellable;      } -    void WaitForArbitration(bool set) { -        waiting_for_arbitration = set; +    void SetCancellable() { +        is_cancellable = true;      } -    bool IsWaitingSync() const { -        return is_waiting_on_sync; +    void ClearCancellable() { +        is_cancellable = false;      } -    void SetWaitingSync(bool is_waiting) { -        is_waiting_on_sync = is_waiting; -    } - -    bool IsPendingTermination() const { -        return will_be_terminated || GetSchedulingStatus() == ThreadSchedStatus::Exited; +    bool IsTerminationRequested() const { +        return will_be_terminated || GetRawState() == ThreadState::Terminated;      }      bool IsPaused() const { @@ -578,21 +481,21 @@ public:          constexpr QueueEntry() = default;          constexpr void Initialize() { -            this->prev = nullptr; -            this->next = nullptr; +            prev = nullptr; +            next = nullptr;          }          constexpr Thread* GetPrev() const { -            return this->prev; +            return prev;          }          constexpr Thread* GetNext() const { -            return this->next; +            return next;          }          constexpr void SetPrev(Thread* thread) { -            this->prev = thread; +            prev = thread;          }          constexpr void SetNext(Thread* thread) { -            this->next = thread; +            next = thread;          }      private: @@ -601,11 +504,11 @@ public:      };      QueueEntry& GetPriorityQueueEntry(s32 core) { -        return this->per_core_priority_queue_entry[core]; +        return per_core_priority_queue_entry[core];      }      const QueueEntry& GetPriorityQueueEntry(s32 core) const { -        return this->per_core_priority_queue_entry[core]; +        return per_core_priority_queue_entry[core];      }      s32 GetDisableDispatchCount() const { @@ -622,24 +525,170 @@ public:          disable_count--;      } +    void SetWaitReasonForDebugging(ThreadWaitReasonForDebugging reason) { +        wait_reason_for_debugging = reason; +    } + +    [[nodiscard]] ThreadWaitReasonForDebugging GetWaitReasonForDebugging() const { +        return wait_reason_for_debugging; +    } + +    void SetWaitObjectsForDebugging(const std::span<KSynchronizationObject*>& objects) { +        wait_objects_for_debugging.clear(); +        wait_objects_for_debugging.reserve(objects.size()); +        for (const auto& object : objects) { +            wait_objects_for_debugging.emplace_back(object); +        } +    } + +    [[nodiscard]] const std::vector<KSynchronizationObject*>& GetWaitObjectsForDebugging() const { +        return wait_objects_for_debugging; +    } + +    void SetMutexWaitAddressForDebugging(VAddr address) { +        mutex_wait_address_for_debugging = address; +    } + +    [[nodiscard]] VAddr GetMutexWaitAddressForDebugging() const { +        return mutex_wait_address_for_debugging; +    } + +    void AddWaiter(Thread* thread); + +    void RemoveWaiter(Thread* thread); + +    [[nodiscard]] Thread* RemoveWaiterByKey(s32* out_num_waiters, VAddr key); + +    [[nodiscard]] VAddr GetAddressKey() const { +        return address_key; +    } + +    [[nodiscard]] u32 GetAddressKeyValue() const { +        return address_key_value; +    } + +    void SetAddressKey(VAddr key) { +        address_key = key; +    } + +    void SetAddressKey(VAddr key, u32 val) { +        address_key = key; +        address_key_value = val; +    } +  private: -    friend class GlobalSchedulerContext; -    friend class KScheduler; -    friend class Process; +    static constexpr size_t PriorityInheritanceCountMax = 10; +    union SyncObjectBuffer { +        std::array<KSynchronizationObject*, Svc::ArgumentHandleCountMax> sync_objects{}; +        std::array<Handle, +                   Svc::ArgumentHandleCountMax*(sizeof(KSynchronizationObject*) / sizeof(Handle))> +            handles; +        constexpr SyncObjectBuffer() {} +    }; +    static_assert(sizeof(SyncObjectBuffer::sync_objects) == sizeof(SyncObjectBuffer::handles)); + +    struct ConditionVariableComparator { +        struct LightCompareType { +            u64 cv_key{}; +            s32 priority{}; + +            [[nodiscard]] constexpr u64 GetConditionVariableKey() const { +                return cv_key; +            } + +            [[nodiscard]] constexpr s32 GetPriority() const { +                return priority; +            } +        }; + +        template <typename T> +        requires( +            std::same_as<T, Thread> || +            std::same_as<T, LightCompareType>) static constexpr int Compare(const T& lhs, +                                                                            const Thread& rhs) { +            const uintptr_t l_key = lhs.GetConditionVariableKey(); +            const uintptr_t r_key = rhs.GetConditionVariableKey(); + +            if (l_key < r_key) { +                // Sort first by key +                return -1; +            } else if (l_key == r_key && lhs.GetPriority() < rhs.GetPriority()) { +                // And then by priority. +                return -1; +            } else { +                return 1; +            } +        } +    }; + +    Common::IntrusiveRedBlackTreeNode condvar_arbiter_tree_node{}; + +    using ConditionVariableThreadTreeTraits = +        Common::IntrusiveRedBlackTreeMemberTraitsDeferredAssert<&Thread::condvar_arbiter_tree_node>; +    using ConditionVariableThreadTree = +        ConditionVariableThreadTreeTraits::TreeType<ConditionVariableComparator>; + +public: +    using ConditionVariableThreadTreeType = ConditionVariableThreadTree; + +    [[nodiscard]] uintptr_t GetConditionVariableKey() const { +        return condvar_key; +    } + +    [[nodiscard]] uintptr_t GetAddressArbiterKey() const { +        return condvar_key; +    } -    void SetSchedulingStatus(ThreadSchedStatus new_status); +    void SetConditionVariable(ConditionVariableThreadTree* tree, VAddr address, uintptr_t cv_key, +                              u32 value) { +        condvar_tree = tree; +        condvar_key = cv_key; +        address_key = address; +        address_key_value = value; +    } + +    void ClearConditionVariable() { +        condvar_tree = nullptr; +    } + +    [[nodiscard]] bool IsWaitingForConditionVariable() const { +        return condvar_tree != nullptr; +    } + +    void SetAddressArbiter(ConditionVariableThreadTree* tree, uintptr_t address) { +        condvar_tree = tree; +        condvar_key = address; +    } + +    void ClearAddressArbiter() { +        condvar_tree = nullptr; +    } + +    [[nodiscard]] bool IsWaitingForAddressArbiter() const { +        return condvar_tree != nullptr; +    } + +    [[nodiscard]] ConditionVariableThreadTree* GetConditionVariableTree() const { +        return condvar_tree; +    } + +    [[nodiscard]] bool HasWaiters() const { +        return !waiter_list.empty(); +    } + +private:      void AddSchedulingFlag(ThreadSchedFlags flag);      void RemoveSchedulingFlag(ThreadSchedFlags flag); - -    void SetCurrentPriority(u32 new_priority); +    void AddWaiterImpl(Thread* thread); +    void RemoveWaiterImpl(Thread* thread); +    static void RestorePriority(KernelCore& kernel, Thread* thread);      Common::SpinLock context_guard{};      ThreadContext32 context_32{};      ThreadContext64 context_64{};      std::shared_ptr<Common::Fiber> host_context{}; -    ThreadStatus status = ThreadStatus::Dormant; -    u32 scheduling_state = 0; +    ThreadState thread_state = ThreadState::Initialized;      u64 thread_id = 0; @@ -652,11 +701,11 @@ private:      /// Nominal thread priority, as set by the emulated application.      /// The nominal priority is the thread priority without priority      /// inheritance taken into account. -    u32 nominal_priority = 0; +    s32 base_priority{};      /// Current thread priority. This may change over the course of the      /// thread's lifetime in order to facilitate priority inheritance. -    u32 current_priority = 0; +    s32 current_priority{};      u64 total_cpu_time_ticks = 0; ///< Total CPU running ticks.      s64 schedule_count{}; @@ -671,37 +720,27 @@ private:      Process* owner_process;      /// Objects that the thread is waiting on, in the same order as they were -    /// passed to WaitSynchronization. -    ThreadSynchronizationObjects* wait_objects; +    /// passed to WaitSynchronization. This is used for debugging only. +    std::vector<KSynchronizationObject*> wait_objects_for_debugging; -    SynchronizationObject* signaling_object; +    /// The current mutex wait address. This is used for debugging only. +    VAddr mutex_wait_address_for_debugging{}; + +    /// The reason the thread is waiting. This is used for debugging only. +    ThreadWaitReasonForDebugging wait_reason_for_debugging{}; + +    KSynchronizationObject* signaling_object;      ResultCode signaling_result{RESULT_SUCCESS};      /// List of threads that are waiting for a mutex that is held by this thread.      MutexWaitingThreads wait_mutex_threads;      /// Thread that owns the lock that this thread is waiting for. -    std::shared_ptr<Thread> lock_owner; - -    /// If waiting on a ConditionVariable, this is the ConditionVariable address -    VAddr condvar_wait_address = 0; -    /// If waiting on a Mutex, this is the mutex address -    VAddr mutex_wait_address = 0; -    /// The handle used to wait for the mutex. -    Handle wait_handle = 0; - -    /// If waiting for an AddressArbiter, this is the address being waited on. -    VAddr arb_wait_address{0}; -    bool waiting_for_arbitration{}; +    Thread* lock_owner{};      /// Handle used as userdata to reference this object when inserting into the CoreTiming queue.      Handle global_handle = 0; -    /// Callback for HLE Events -    HLECallback hle_callback; -    Handle hle_time_event; -    SynchronizationObject* hle_object; -      KScheduler* scheduler = nullptr;      std::array<QueueEntry, Core::Hardware::NUM_CPU_CORES> per_core_priority_queue_entry{}; @@ -714,7 +753,7 @@ private:      u32 pausing_state = 0;      bool is_running = false; -    bool is_waiting_on_sync = false; +    bool is_cancellable = false;      bool is_sync_cancelled = false;      bool is_continuous_on_svc = false; @@ -725,6 +764,18 @@ private:      bool was_running = false; +    bool signaled{}; + +    ConditionVariableThreadTree* condvar_tree{}; +    uintptr_t condvar_key{}; +    VAddr address_key{}; +    u32 address_key_value{}; +    s32 num_kernel_waiters{}; + +    using WaiterList = boost::intrusive::list<Thread>; +    WaiterList waiter_list{}; +    WaiterList pinned_waiter_list{}; +      std::string name;  }; diff --git a/src/core/hle/kernel/time_manager.cpp b/src/core/hle/kernel/time_manager.cpp index 79628e2b4..832edd629 100644 --- a/src/core/hle/kernel/time_manager.cpp +++ b/src/core/hle/kernel/time_manager.cpp @@ -18,12 +18,10 @@ TimeManager::TimeManager(Core::System& system_) : system{system_} {      time_manager_event_type = Core::Timing::CreateEvent(          "Kernel::TimeManagerCallback",          [this](std::uintptr_t thread_handle, std::chrono::nanoseconds) { -            const KScopedSchedulerLock lock(system.Kernel()); -            const auto proper_handle = static_cast<Handle>(thread_handle); -              std::shared_ptr<Thread> thread;              {                  std::lock_guard lock{mutex}; +                const auto proper_handle = static_cast<Handle>(thread_handle);                  if (cancelled_events[proper_handle]) {                      return;                  } @@ -32,7 +30,7 @@ TimeManager::TimeManager(Core::System& system_) : system{system_} {              if (thread) {                  // Thread can be null if process has exited -                thread->OnWakeUp(); +                thread->Wakeup();              }          });  } @@ -42,8 +40,7 @@ void TimeManager::ScheduleTimeEvent(Handle& event_handle, Thread* timetask, s64      event_handle = timetask->GetGlobalHandle();      if (nanoseconds > 0) {          ASSERT(timetask); -        ASSERT(timetask->GetStatus() != ThreadStatus::Ready); -        ASSERT(timetask->GetStatus() != ThreadStatus::WaitMutex); +        ASSERT(timetask->GetState() != ThreadState::Runnable);          system.CoreTiming().ScheduleEvent(std::chrono::nanoseconds{nanoseconds},                                            time_manager_event_type, event_handle);      } else { diff --git a/src/core/hle/service/apm/interface.cpp b/src/core/hle/service/apm/interface.cpp index 298f6d520..0bff97a37 100644 --- a/src/core/hle/service/apm/interface.cpp +++ b/src/core/hle/service/apm/interface.cpp @@ -56,7 +56,7 @@ APM::APM(Core::System& system_, std::shared_ptr<Module> apm_, Controller& contro      static const FunctionInfo functions[] = {          {0, &APM::OpenSession, "OpenSession"},          {1, &APM::GetPerformanceMode, "GetPerformanceMode"}, -        {6, nullptr, "IsCpuOverclockEnabled"}, +        {6, &APM::IsCpuOverclockEnabled, "IsCpuOverclockEnabled"},      };      RegisterHandlers(functions);  } @@ -78,6 +78,14 @@ void APM::GetPerformanceMode(Kernel::HLERequestContext& ctx) {      rb.PushEnum(controller.GetCurrentPerformanceMode());  } +void APM::IsCpuOverclockEnabled(Kernel::HLERequestContext& ctx) { +    LOG_WARNING(Service_APM, "(STUBBED) called"); + +    IPC::ResponseBuilder rb{ctx, 3}; +    rb.Push(RESULT_SUCCESS); +    rb.Push(false); +} +  APM_Sys::APM_Sys(Core::System& system_, Controller& controller_)      : ServiceFramework{system_, "apm:sys"}, controller{controller_} {      // clang-format off diff --git a/src/core/hle/service/apm/interface.h b/src/core/hle/service/apm/interface.h index 7d57c4978..063ad5308 100644 --- a/src/core/hle/service/apm/interface.h +++ b/src/core/hle/service/apm/interface.h @@ -20,6 +20,7 @@ public:  private:      void OpenSession(Kernel::HLERequestContext& ctx);      void GetPerformanceMode(Kernel::HLERequestContext& ctx); +    void IsCpuOverclockEnabled(Kernel::HLERequestContext& ctx);      std::shared_ptr<Module> apm;      Controller& controller; diff --git a/src/core/hle/service/nfp/nfp.cpp b/src/core/hle/service/nfp/nfp.cpp index 5557da72e..641bcadea 100644 --- a/src/core/hle/service/nfp/nfp.cpp +++ b/src/core/hle/service/nfp/nfp.cpp @@ -190,12 +190,6 @@ private:      void GetDeviceState(Kernel::HLERequestContext& ctx) {          LOG_DEBUG(Service_NFP, "called"); -        auto nfc_event = nfp_interface.GetNFCEvent(); -        if (!nfc_event->ShouldWait(&ctx.GetThread()) && !has_attached_handle) { -            device_state = DeviceState::TagFound; -            nfc_event->Clear(); -        } -          IPC::ResponseBuilder rb{ctx, 3};          rb.Push(RESULT_SUCCESS);          rb.Push<u32>(static_cast<u32>(device_state)); diff --git a/src/core/hle/service/nvflinger/buffer_queue.cpp b/src/core/hle/service/nvflinger/buffer_queue.cpp index c68905e19..5578181a4 100644 --- a/src/core/hle/service/nvflinger/buffer_queue.cpp +++ b/src/core/hle/service/nvflinger/buffer_queue.cpp @@ -180,9 +180,11 @@ u32 BufferQueue::Query(QueryType type) {      switch (type) {      case QueryType::NativeWindowFormat:          return static_cast<u32>(PixelFormat::RGBA8888); +    case QueryType::NativeWindowWidth: +    case QueryType::NativeWindowHeight: +        break;      } - -    UNIMPLEMENTED(); +    UNIMPLEMENTED_MSG("Unimplemented query type={}", type);      return 0;  } diff --git a/src/core/hle/service/nvflinger/nvflinger.cpp b/src/core/hle/service/nvflinger/nvflinger.cpp index 4b3581949..ceaa93d28 100644 --- a/src/core/hle/service/nvflinger/nvflinger.cpp +++ b/src/core/hle/service/nvflinger/nvflinger.cpp @@ -38,6 +38,10 @@ void NVFlinger::SplitVSync() {      system.RegisterHostThread();      std::string name = "yuzu:VSyncThread";      MicroProfileOnThreadCreate(name.c_str()); + +    // Cleanup +    SCOPE_EXIT({ MicroProfileOnThreadExit(); }); +      Common::SetCurrentThreadName(name.c_str());      Common::SetCurrentThreadPriority(Common::ThreadPriority::High);      s64 delay = 0; diff --git a/src/core/hle/service/sm/sm.cpp b/src/core/hle/service/sm/sm.cpp index 4da69f503..2b91a89d1 100644 --- a/src/core/hle/service/sm/sm.cpp +++ b/src/core/hle/service/sm/sm.cpp @@ -139,9 +139,6 @@ void SM::GetService(Kernel::HLERequestContext& ctx) {          server_port->AppendPendingSession(server);      } -    // Wake the threads waiting on the ServerPort -    server_port->Signal(); -      LOG_DEBUG(Service_SM, "called service={} -> session={}", name, client->GetObjectId());      IPC::ResponseBuilder rb{ctx, 2, 0, 1, IPC::ResponseBuilder::Flags::AlwaysMoveHandles};      rb.Push(RESULT_SUCCESS); diff --git a/src/tests/common/ring_buffer.cpp b/src/tests/common/ring_buffer.cpp index c883c4d56..54def22da 100644 --- a/src/tests/common/ring_buffer.cpp +++ b/src/tests/common/ring_buffer.cpp @@ -20,60 +20,60 @@ TEST_CASE("RingBuffer: Basic Tests", "[common]") {      for (std::size_t i = 0; i < 4; i++) {          const char elem = static_cast<char>(i);          const std::size_t count = buf.Push(&elem, 1); -        REQUIRE(count == 1); +        REQUIRE(count == 1U);      } -    REQUIRE(buf.Size() == 4); +    REQUIRE(buf.Size() == 4U);      // Pushing values into a full ring buffer should fail.      {          const char elem = static_cast<char>(42);          const std::size_t count = buf.Push(&elem, 1); -        REQUIRE(count == 0); +        REQUIRE(count == 0U);      } -    REQUIRE(buf.Size() == 4); +    REQUIRE(buf.Size() == 4U);      // Popping multiple values from a ring buffer with values should succeed.      {          const std::vector<char> popped = buf.Pop(2); -        REQUIRE(popped.size() == 2); +        REQUIRE(popped.size() == 2U);          REQUIRE(popped[0] == 0);          REQUIRE(popped[1] == 1);      } -    REQUIRE(buf.Size() == 2); +    REQUIRE(buf.Size() == 2U);      // Popping a single value from a ring buffer with values should succeed.      {          const std::vector<char> popped = buf.Pop(1); -        REQUIRE(popped.size() == 1); +        REQUIRE(popped.size() == 1U);          REQUIRE(popped[0] == 2);      } -    REQUIRE(buf.Size() == 1); +    REQUIRE(buf.Size() == 1U);      // Pushing more values than space available should partially suceed.      {          std::vector<char> to_push(6);          std::iota(to_push.begin(), to_push.end(), 88);          const std::size_t count = buf.Push(to_push); -        REQUIRE(count == 3); +        REQUIRE(count == 3U);      } -    REQUIRE(buf.Size() == 4); +    REQUIRE(buf.Size() == 4U);      // Doing an unlimited pop should pop all values.      {          const std::vector<char> popped = buf.Pop(); -        REQUIRE(popped.size() == 4); +        REQUIRE(popped.size() == 4U);          REQUIRE(popped[0] == 3);          REQUIRE(popped[1] == 88);          REQUIRE(popped[2] == 89);          REQUIRE(popped[3] == 90);      } -    REQUIRE(buf.Size() == 0); +    REQUIRE(buf.Size() == 0U);  }  TEST_CASE("RingBuffer: Threaded Test", "[common]") { @@ -93,7 +93,7 @@ TEST_CASE("RingBuffer: Threaded Test", "[common]") {          std::size_t i = 0;          while (i < count) {              if (const std::size_t c = buf.Push(&value[0], 1); c > 0) { -                REQUIRE(c == 1); +                REQUIRE(c == 1U);                  i++;                  next_value(value);              } else { @@ -108,7 +108,7 @@ TEST_CASE("RingBuffer: Threaded Test", "[common]") {          std::size_t i = 0;          while (i < count) {              if (const std::vector<char> v = buf.Pop(1); v.size() > 0) { -                REQUIRE(v.size() == 2); +                REQUIRE(v.size() == 2U);                  REQUIRE(v[0] == value[0]);                  REQUIRE(v[1] == value[1]);                  i++; @@ -123,7 +123,7 @@ TEST_CASE("RingBuffer: Threaded Test", "[common]") {      producer.join();      consumer.join(); -    REQUIRE(buf.Size() == 0); +    REQUIRE(buf.Size() == 0U);      printf("RingBuffer: Threaded Test: full: %zu, empty: %zu\n", full, empty);  } diff --git a/src/video_core/renderer_vulkan/fixed_pipeline_state.cpp b/src/video_core/renderer_vulkan/fixed_pipeline_state.cpp index 67dd10500..5be6dabd9 100644 --- a/src/video_core/renderer_vulkan/fixed_pipeline_state.cpp +++ b/src/video_core/renderer_vulkan/fixed_pipeline_state.cpp @@ -76,7 +76,7 @@ void FixedPipelineState::Fill(const Maxwell& regs, bool has_extended_dynamic_sta              regs.instanced_arrays.IsInstancingEnabled(index) ? regs.vertex_array[index].divisor : 0;      } -    for (std::size_t index = 0; index < Maxwell::NumVertexAttributes; ++index) { +    for (size_t index = 0; index < Maxwell::NumVertexAttributes; ++index) {          const auto& input = regs.vertex_attrib_format[index];          auto& attribute = attributes[index];          attribute.raw = 0; @@ -85,6 +85,7 @@ void FixedPipelineState::Fill(const Maxwell& regs, bool has_extended_dynamic_sta          attribute.offset.Assign(input.offset);          attribute.type.Assign(static_cast<u32>(input.type.Value()));          attribute.size.Assign(static_cast<u32>(input.size.Value())); +        attribute.binding_index_enabled.Assign(regs.vertex_array[index].IsEnabled() ? 1 : 0);      }      for (std::size_t index = 0; index < std::size(attachments); ++index) { @@ -172,14 +173,9 @@ void FixedPipelineState::DynamicState::Fill(const Maxwell& regs) {      depth_test_func.Assign(PackComparisonOp(regs.depth_test_func));      cull_face.Assign(PackCullFace(regs.cull_face));      cull_enable.Assign(regs.cull_test_enabled != 0 ? 1 : 0); - -    for (std::size_t index = 0; index < Maxwell::NumVertexArrays; ++index) { -        const auto& input = regs.vertex_array[index]; -        VertexBinding& binding = vertex_bindings[index]; -        binding.raw = 0; -        binding.enabled.Assign(input.IsEnabled() ? 1 : 0); -        binding.stride.Assign(static_cast<u16>(input.stride.Value())); -    } +    std::ranges::transform(regs.vertex_array, vertex_strides.begin(), [](const auto& array) { +        return static_cast<u16>(array.stride.Value()); +    });  }  std::size_t FixedPipelineState::Hash() const noexcept { diff --git a/src/video_core/renderer_vulkan/fixed_pipeline_state.h b/src/video_core/renderer_vulkan/fixed_pipeline_state.h index 7e95e6fce..465a55fdb 100644 --- a/src/video_core/renderer_vulkan/fixed_pipeline_state.h +++ b/src/video_core/renderer_vulkan/fixed_pipeline_state.h @@ -96,6 +96,8 @@ struct FixedPipelineState {          BitField<6, 14, u32> offset;          BitField<20, 3, u32> type;          BitField<23, 6, u32> size; +        // Not really an element of a vertex attribute, but it can be packed here +        BitField<29, 1, u32> binding_index_enabled;          constexpr Maxwell::VertexAttribute::Type Type() const noexcept {              return static_cast<Maxwell::VertexAttribute::Type>(type.Value()); @@ -130,12 +132,6 @@ struct FixedPipelineState {          }      }; -    union VertexBinding { -        u16 raw; -        BitField<0, 12, u16> stride; -        BitField<12, 1, u16> enabled; -    }; -      struct DynamicState {          union {              u32 raw1; @@ -153,7 +149,8 @@ struct FixedPipelineState {              BitField<0, 2, u32> cull_face;              BitField<2, 1, u32> cull_enable;          }; -        std::array<VertexBinding, Maxwell::NumVertexArrays> vertex_bindings; +        // Vertex stride is a 12 bits value, we have 4 bits to spare per element +        std::array<u16, Maxwell::NumVertexArrays> vertex_strides;          void Fill(const Maxwell& regs); diff --git a/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp b/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp index 8a94464f6..a5214d0bc 100644 --- a/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp +++ b/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp @@ -212,11 +212,7 @@ vk::Pipeline VKGraphicsPipeline::CreatePipeline(const SPIRVProgram& program,          // state is ignored          dynamic.raw1 = 0;          dynamic.raw2 = 0; -        for (FixedPipelineState::VertexBinding& binding : dynamic.vertex_bindings) { -            // Enable all vertex bindings -            binding.raw = 0; -            binding.enabled.Assign(1); -        } +        dynamic.vertex_strides.fill(0);      } else {          dynamic = state.dynamic_state;      } @@ -224,19 +220,16 @@ vk::Pipeline VKGraphicsPipeline::CreatePipeline(const SPIRVProgram& program,      std::vector<VkVertexInputBindingDescription> vertex_bindings;      std::vector<VkVertexInputBindingDivisorDescriptionEXT> vertex_binding_divisors;      for (std::size_t index = 0; index < Maxwell::NumVertexArrays; ++index) { -        const auto& binding = dynamic.vertex_bindings[index]; -        if (!binding.enabled) { +        if (state.attributes[index].binding_index_enabled == 0) {              continue;          }          const bool instanced = state.binding_divisors[index] != 0;          const auto rate = instanced ? VK_VERTEX_INPUT_RATE_INSTANCE : VK_VERTEX_INPUT_RATE_VERTEX; -          vertex_bindings.push_back({              .binding = static_cast<u32>(index), -            .stride = binding.stride, +            .stride = dynamic.vertex_strides[index],              .inputRate = rate,          }); -          if (instanced) {              vertex_binding_divisors.push_back({                  .binding = static_cast<u32>(index), diff --git a/src/yuzu/configuration/config.cpp b/src/yuzu/configuration/config.cpp index 43cd11ba0..cda448718 100644 --- a/src/yuzu/configuration/config.cpp +++ b/src/yuzu/configuration/config.cpp @@ -514,7 +514,7 @@ void Config::ReadControlValues() {      Settings::values.emulate_analog_keyboard =          ReadSetting(QStringLiteral("emulate_analog_keyboard"), false).toBool(); -    ReadSettingGlobal(Settings::values.use_docked_mode, QStringLiteral("use_docked_mode"), false); +    ReadSettingGlobal(Settings::values.use_docked_mode, QStringLiteral("use_docked_mode"), true);      ReadSettingGlobal(Settings::values.vibration_enabled, QStringLiteral("vibration_enabled"),                        true);      ReadSettingGlobal(Settings::values.enable_accurate_vibrations, @@ -1176,7 +1176,7 @@ void Config::SaveControlValues() {      SaveTouchscreenValues();      SaveMotionTouchValues(); -    WriteSettingGlobal(QStringLiteral("use_docked_mode"), Settings::values.use_docked_mode, false); +    WriteSettingGlobal(QStringLiteral("use_docked_mode"), Settings::values.use_docked_mode, true);      WriteSettingGlobal(QStringLiteral("vibration_enabled"), Settings::values.vibration_enabled,                         true);      WriteSettingGlobal(QStringLiteral("enable_accurate_vibrations"), diff --git a/src/yuzu/configuration/configure_motion_touch.cpp b/src/yuzu/configuration/configure_motion_touch.cpp index c2a7113da..eb8eacbf9 100644 --- a/src/yuzu/configuration/configure_motion_touch.cpp +++ b/src/yuzu/configuration/configure_motion_touch.cpp @@ -51,6 +51,8 @@ CalibrationConfigurationDialog::CalibrationConfigurationDialog(QWidget* parent,              case CalibrationConfigurationJob::Status::Completed:                  text = tr("Configuration completed!");                  break; +            default: +                break;              }              QMetaObject::invokeMethod(this, "UpdateLabelText", Q_ARG(QString, text));              if (status == CalibrationConfigurationJob::Status::Completed) { diff --git a/src/yuzu/debugger/wait_tree.cpp b/src/yuzu/debugger/wait_tree.cpp index 0925c10b4..a93b5d3c2 100644 --- a/src/yuzu/debugger/wait_tree.cpp +++ b/src/yuzu/debugger/wait_tree.cpp @@ -14,10 +14,10 @@  #include "core/core.h"  #include "core/hle/kernel/handle_table.h"  #include "core/hle/kernel/k_scheduler.h" -#include "core/hle/kernel/mutex.h" +#include "core/hle/kernel/k_synchronization_object.h"  #include "core/hle/kernel/process.h"  #include "core/hle/kernel/readable_event.h" -#include "core/hle/kernel/synchronization_object.h" +#include "core/hle/kernel/svc_common.h"  #include "core/hle/kernel/thread.h"  #include "core/memory.h" @@ -116,7 +116,7 @@ QString WaitTreeText::GetText() const {  WaitTreeMutexInfo::WaitTreeMutexInfo(VAddr mutex_address, const Kernel::HandleTable& handle_table)      : mutex_address(mutex_address) {      mutex_value = Core::System::GetInstance().Memory().Read32(mutex_address); -    owner_handle = static_cast<Kernel::Handle>(mutex_value & Kernel::Mutex::MutexOwnerMask); +    owner_handle = static_cast<Kernel::Handle>(mutex_value & Kernel::Svc::HandleWaitMask);      owner = handle_table.Get<Kernel::Thread>(owner_handle);  } @@ -127,7 +127,7 @@ QString WaitTreeMutexInfo::GetText() const {  }  std::vector<std::unique_ptr<WaitTreeItem>> WaitTreeMutexInfo::GetChildren() const { -    const bool has_waiters = (mutex_value & Kernel::Mutex::MutexHasWaitersFlag) != 0; +    const bool has_waiters = (mutex_value & Kernel::Svc::HandleWaitMask) != 0;      std::vector<std::unique_ptr<WaitTreeItem>> list;      list.push_back(std::make_unique<WaitTreeText>(tr("has waiters: %1").arg(has_waiters))); @@ -169,7 +169,8 @@ std::vector<std::unique_ptr<WaitTreeItem>> WaitTreeCallstack::GetChildren() cons      return list;  } -WaitTreeSynchronizationObject::WaitTreeSynchronizationObject(const Kernel::SynchronizationObject& o) +WaitTreeSynchronizationObject::WaitTreeSynchronizationObject( +    const Kernel::KSynchronizationObject& o)      : object(o) {}  WaitTreeSynchronizationObject::~WaitTreeSynchronizationObject() = default; @@ -188,7 +189,7 @@ QString WaitTreeSynchronizationObject::GetText() const {  }  std::unique_ptr<WaitTreeSynchronizationObject> WaitTreeSynchronizationObject::make( -    const Kernel::SynchronizationObject& object) { +    const Kernel::KSynchronizationObject& object) {      switch (object.GetHandleType()) {      case Kernel::HandleType::ReadableEvent:          return std::make_unique<WaitTreeEvent>(static_cast<const Kernel::ReadableEvent&>(object)); @@ -202,7 +203,7 @@ std::unique_ptr<WaitTreeSynchronizationObject> WaitTreeSynchronizationObject::ma  std::vector<std::unique_ptr<WaitTreeItem>> WaitTreeSynchronizationObject::GetChildren() const {      std::vector<std::unique_ptr<WaitTreeItem>> list; -    const auto& threads = object.GetWaitingThreads(); +    const auto& threads = object.GetWaitingThreadsForDebugging();      if (threads.empty()) {          list.push_back(std::make_unique<WaitTreeText>(tr("waited by no thread")));      } else { @@ -211,8 +212,8 @@ std::vector<std::unique_ptr<WaitTreeItem>> WaitTreeSynchronizationObject::GetChi      return list;  } -WaitTreeObjectList::WaitTreeObjectList( -    const std::vector<std::shared_ptr<Kernel::SynchronizationObject>>& list, bool w_all) +WaitTreeObjectList::WaitTreeObjectList(const std::vector<Kernel::KSynchronizationObject*>& list, +                                       bool w_all)      : object_list(list), wait_all(w_all) {}  WaitTreeObjectList::~WaitTreeObjectList() = default; @@ -237,8 +238,8 @@ WaitTreeThread::~WaitTreeThread() = default;  QString WaitTreeThread::GetText() const {      const auto& thread = static_cast<const Kernel::Thread&>(object);      QString status; -    switch (thread.GetStatus()) { -    case Kernel::ThreadStatus::Ready: +    switch (thread.GetState()) { +    case Kernel::ThreadState::Runnable:          if (!thread.IsPaused()) {              if (thread.WasRunning()) {                  status = tr("running"); @@ -249,35 +250,39 @@ QString WaitTreeThread::GetText() const {              status = tr("paused");          }          break; -    case Kernel::ThreadStatus::Paused: -        status = tr("paused"); -        break; -    case Kernel::ThreadStatus::WaitHLEEvent: -        status = tr("waiting for HLE return"); -        break; -    case Kernel::ThreadStatus::WaitSleep: -        status = tr("sleeping"); -        break; -    case Kernel::ThreadStatus::WaitIPC: -        status = tr("waiting for IPC reply"); -        break; -    case Kernel::ThreadStatus::WaitSynch: -        status = tr("waiting for objects"); -        break; -    case Kernel::ThreadStatus::WaitMutex: -        status = tr("waiting for mutex"); -        break; -    case Kernel::ThreadStatus::WaitCondVar: -        status = tr("waiting for condition variable"); +    case Kernel::ThreadState::Waiting: +        switch (thread.GetWaitReasonForDebugging()) { +        case Kernel::ThreadWaitReasonForDebugging::Sleep: +            status = tr("sleeping"); +            break; +        case Kernel::ThreadWaitReasonForDebugging::IPC: +            status = tr("waiting for IPC reply"); +            break; +        case Kernel::ThreadWaitReasonForDebugging::Synchronization: +            status = tr("waiting for objects"); +            break; +        case Kernel::ThreadWaitReasonForDebugging::ConditionVar: +            status = tr("waiting for condition variable"); +            break; +        case Kernel::ThreadWaitReasonForDebugging::Arbitration: +            status = tr("waiting for address arbiter"); +            break; +        case Kernel::ThreadWaitReasonForDebugging::Suspended: +            status = tr("waiting for suspend resume"); +            break; +        default: +            status = tr("waiting"); +            break; +        }          break; -    case Kernel::ThreadStatus::WaitArb: -        status = tr("waiting for address arbiter"); +    case Kernel::ThreadState::Initialized: +        status = tr("initialized");          break; -    case Kernel::ThreadStatus::Dormant: -        status = tr("dormant"); +    case Kernel::ThreadState::Terminated: +        status = tr("terminated");          break; -    case Kernel::ThreadStatus::Dead: -        status = tr("dead"); +    default: +        status = tr("unknown");          break;      } @@ -293,8 +298,8 @@ QColor WaitTreeThread::GetColor() const {      const std::size_t color_index = IsDarkTheme() ? 1 : 0;      const auto& thread = static_cast<const Kernel::Thread&>(object); -    switch (thread.GetStatus()) { -    case Kernel::ThreadStatus::Ready: +    switch (thread.GetState()) { +    case Kernel::ThreadState::Runnable:          if (!thread.IsPaused()) {              if (thread.WasRunning()) {                  return QColor(WaitTreeColors[0][color_index]); @@ -304,21 +309,24 @@ QColor WaitTreeThread::GetColor() const {          } else {              return QColor(WaitTreeColors[2][color_index]);          } -    case Kernel::ThreadStatus::Paused: -        return QColor(WaitTreeColors[3][color_index]); -    case Kernel::ThreadStatus::WaitHLEEvent: -    case Kernel::ThreadStatus::WaitIPC: -        return QColor(WaitTreeColors[4][color_index]); -    case Kernel::ThreadStatus::WaitSleep: -        return QColor(WaitTreeColors[5][color_index]); -    case Kernel::ThreadStatus::WaitSynch: -    case Kernel::ThreadStatus::WaitMutex: -    case Kernel::ThreadStatus::WaitCondVar: -    case Kernel::ThreadStatus::WaitArb: -        return QColor(WaitTreeColors[6][color_index]); -    case Kernel::ThreadStatus::Dormant: +    case Kernel::ThreadState::Waiting: +        switch (thread.GetWaitReasonForDebugging()) { +        case Kernel::ThreadWaitReasonForDebugging::IPC: +            return QColor(WaitTreeColors[4][color_index]); +        case Kernel::ThreadWaitReasonForDebugging::Sleep: +            return QColor(WaitTreeColors[5][color_index]); +        case Kernel::ThreadWaitReasonForDebugging::Synchronization: +        case Kernel::ThreadWaitReasonForDebugging::ConditionVar: +        case Kernel::ThreadWaitReasonForDebugging::Arbitration: +        case Kernel::ThreadWaitReasonForDebugging::Suspended: +            return QColor(WaitTreeColors[6][color_index]); +            break; +        default: +            return QColor(WaitTreeColors[3][color_index]); +        } +    case Kernel::ThreadState::Initialized:          return QColor(WaitTreeColors[7][color_index]); -    case Kernel::ThreadStatus::Dead: +    case Kernel::ThreadState::Terminated:          return QColor(WaitTreeColors[8][color_index]);      default:          return WaitTreeItem::GetColor(); @@ -354,11 +362,11 @@ std::vector<std::unique_ptr<WaitTreeItem>> WaitTreeThread::GetChildren() const {      list.push_back(std::make_unique<WaitTreeText>(tr("thread id = %1").arg(thread.GetThreadID())));      list.push_back(std::make_unique<WaitTreeText>(tr("priority = %1(current) / %2(normal)")                                                        .arg(thread.GetPriority()) -                                                      .arg(thread.GetNominalPriority()))); +                                                      .arg(thread.GetBasePriority())));      list.push_back(std::make_unique<WaitTreeText>(          tr("last running ticks = %1").arg(thread.GetLastScheduledTick()))); -    const VAddr mutex_wait_address = thread.GetMutexWaitAddress(); +    const VAddr mutex_wait_address = thread.GetMutexWaitAddressForDebugging();      if (mutex_wait_address != 0) {          const auto& handle_table = thread.GetOwnerProcess()->GetHandleTable();          list.push_back(std::make_unique<WaitTreeMutexInfo>(mutex_wait_address, handle_table)); @@ -366,9 +374,11 @@ std::vector<std::unique_ptr<WaitTreeItem>> WaitTreeThread::GetChildren() const {          list.push_back(std::make_unique<WaitTreeText>(tr("not waiting for mutex")));      } -    if (thread.GetStatus() == Kernel::ThreadStatus::WaitSynch) { -        list.push_back(std::make_unique<WaitTreeObjectList>(thread.GetSynchronizationObjects(), -                                                            thread.IsWaitingSync())); +    if (thread.GetState() == Kernel::ThreadState::Waiting && +        thread.GetWaitReasonForDebugging() == +            Kernel::ThreadWaitReasonForDebugging::Synchronization) { +        list.push_back(std::make_unique<WaitTreeObjectList>(thread.GetWaitObjectsForDebugging(), +                                                            thread.IsCancellable()));      }      list.push_back(std::make_unique<WaitTreeCallstack>(thread)); @@ -380,7 +390,7 @@ WaitTreeEvent::WaitTreeEvent(const Kernel::ReadableEvent& object)      : WaitTreeSynchronizationObject(object) {}  WaitTreeEvent::~WaitTreeEvent() = default; -WaitTreeThreadList::WaitTreeThreadList(const std::vector<std::shared_ptr<Kernel::Thread>>& list) +WaitTreeThreadList::WaitTreeThreadList(const std::vector<Kernel::Thread*>& list)      : thread_list(list) {}  WaitTreeThreadList::~WaitTreeThreadList() = default; diff --git a/src/yuzu/debugger/wait_tree.h b/src/yuzu/debugger/wait_tree.h index 8e3bc4b24..cf96911ea 100644 --- a/src/yuzu/debugger/wait_tree.h +++ b/src/yuzu/debugger/wait_tree.h @@ -18,8 +18,8 @@ class EmuThread;  namespace Kernel {  class HandleTable; +class KSynchronizationObject;  class ReadableEvent; -class SynchronizationObject;  class Thread;  } // namespace Kernel @@ -102,30 +102,29 @@ private:  class WaitTreeSynchronizationObject : public WaitTreeExpandableItem {      Q_OBJECT  public: -    explicit WaitTreeSynchronizationObject(const Kernel::SynchronizationObject& object); +    explicit WaitTreeSynchronizationObject(const Kernel::KSynchronizationObject& object);      ~WaitTreeSynchronizationObject() override;      static std::unique_ptr<WaitTreeSynchronizationObject> make( -        const Kernel::SynchronizationObject& object); +        const Kernel::KSynchronizationObject& object);      QString GetText() const override;      std::vector<std::unique_ptr<WaitTreeItem>> GetChildren() const override;  protected: -    const Kernel::SynchronizationObject& object; +    const Kernel::KSynchronizationObject& object;  };  class WaitTreeObjectList : public WaitTreeExpandableItem {      Q_OBJECT  public: -    WaitTreeObjectList(const std::vector<std::shared_ptr<Kernel::SynchronizationObject>>& list, -                       bool wait_all); +    WaitTreeObjectList(const std::vector<Kernel::KSynchronizationObject*>& list, bool wait_all);      ~WaitTreeObjectList() override;      QString GetText() const override;      std::vector<std::unique_ptr<WaitTreeItem>> GetChildren() const override;  private: -    const std::vector<std::shared_ptr<Kernel::SynchronizationObject>>& object_list; +    const std::vector<Kernel::KSynchronizationObject*>& object_list;      bool wait_all;  }; @@ -150,14 +149,14 @@ public:  class WaitTreeThreadList : public WaitTreeExpandableItem {      Q_OBJECT  public: -    explicit WaitTreeThreadList(const std::vector<std::shared_ptr<Kernel::Thread>>& list); +    explicit WaitTreeThreadList(const std::vector<Kernel::Thread*>& list);      ~WaitTreeThreadList() override;      QString GetText() const override;      std::vector<std::unique_ptr<WaitTreeItem>> GetChildren() const override;  private: -    const std::vector<std::shared_ptr<Kernel::Thread>>& thread_list; +    const std::vector<Kernel::Thread*>& thread_list;  };  class WaitTreeModel : public QAbstractItemModel { diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp index 2c10160c8..2e74037d1 100644 --- a/src/yuzu/main.cpp +++ b/src/yuzu/main.cpp @@ -292,12 +292,48 @@ GMainWindow::GMainWindow()      connect(&mouse_hide_timer, &QTimer::timeout, this, &GMainWindow::HideMouseCursor);      connect(ui.menubar, &QMenuBar::hovered, this, &GMainWindow::ShowMouseCursor); +    MigrateConfigFiles(); + +    ui.action_Fullscreen->setChecked(false); +      QStringList args = QApplication::arguments(); -    if (args.length() >= 2) { -        BootGame(args[1]); + +    if (args.size() < 2) { +        return;      } -    MigrateConfigFiles(); +    QString game_path; + +    for (int i = 1; i < args.size(); ++i) { +        // Preserves drag/drop functionality +        if (args.size() == 2 && !args[1].startsWith(QChar::fromLatin1('-'))) { +            game_path = args[1]; +            break; +        } + +        // Launch game in fullscreen mode +        if (args[i] == QStringLiteral("-f")) { +            ui.action_Fullscreen->setChecked(true); +            continue; +        } + +        // Launch game at path +        if (args[i] == QStringLiteral("-g")) { +            if (i >= args.size() - 1) { +                continue; +            } + +            if (args[i + 1].startsWith(QChar::fromLatin1('-'))) { +                continue; +            } + +            game_path = args[++i]; +        } +    } + +    if (!game_path.isEmpty()) { +        BootGame(game_path); +    }  }  GMainWindow::~GMainWindow() { diff --git a/src/yuzu/util/url_request_interceptor.cpp b/src/yuzu/util/url_request_interceptor.cpp index 2d491d8c0..b637e771e 100644 --- a/src/yuzu/util/url_request_interceptor.cpp +++ b/src/yuzu/util/url_request_interceptor.cpp @@ -22,6 +22,8 @@ void UrlRequestInterceptor::interceptRequest(QWebEngineUrlRequestInfo& info) {      case QWebEngineUrlRequestInfo::ResourceTypeXhr:          emit FrameChanged();          break; +    default: +        break;      }  } diff --git a/src/yuzu_cmd/config.cpp b/src/yuzu_cmd/config.cpp index 38075c345..41ef6f6b8 100644 --- a/src/yuzu_cmd/config.cpp +++ b/src/yuzu_cmd/config.cpp @@ -344,7 +344,7 @@ void Config::ReadValues() {      // System      Settings::values.use_docked_mode.SetValue( -        sdl2_config->GetBoolean("System", "use_docked_mode", false)); +        sdl2_config->GetBoolean("System", "use_docked_mode", true));      Settings::values.current_user = std::clamp<int>(          sdl2_config->GetInteger("System", "current_user", 0), 0, Service::Account::MAX_USERS - 1); diff --git a/src/yuzu_cmd/default_ini.h b/src/yuzu_cmd/default_ini.h index 2d4b98d9a..3ee0e037d 100644 --- a/src/yuzu_cmd/default_ini.h +++ b/src/yuzu_cmd/default_ini.h @@ -274,7 +274,7 @@ gamecard_path =  [System]  # Whether the system is docked -# 1: Yes, 0 (default): No +# 1 (default): Yes, 0: No  use_docked_mode =  # Allow the use of NFC in games diff --git a/src/yuzu_cmd/yuzu.cpp b/src/yuzu_cmd/yuzu.cpp index 39e0d35aa..4faf62ede 100644 --- a/src/yuzu_cmd/yuzu.cpp +++ b/src/yuzu_cmd/yuzu.cpp @@ -95,8 +95,6 @@ int main(int argc, char** argv) {      int option_index = 0;      InitializeLogging(); - -    char* endarg;  #ifdef _WIN32      int argc_w;      auto argv_w = CommandLineToArgvW(GetCommandLineW(), &argc_w); diff --git a/src/yuzu_tester/config.cpp b/src/yuzu_tester/config.cpp index 91684e96e..0aa143e1f 100644 --- a/src/yuzu_tester/config.cpp +++ b/src/yuzu_tester/config.cpp @@ -86,7 +86,7 @@ void Config::ReadValues() {      Settings::values.touchscreen.diameter_y = 15;      Settings::values.use_docked_mode.SetValue( -        sdl2_config->GetBoolean("Controls", "use_docked_mode", false)); +        sdl2_config->GetBoolean("Controls", "use_docked_mode", true));      // Data Storage      Settings::values.use_virtual_sd = diff --git a/src/yuzu_tester/default_ini.h b/src/yuzu_tester/default_ini.h index 3eb64e9d7..779c3791b 100644 --- a/src/yuzu_tester/default_ini.h +++ b/src/yuzu_tester/default_ini.h @@ -116,7 +116,7 @@ use_virtual_sd =  [System]  # Whether the system is docked -# 1: Yes, 0 (default): No +# 1 (default): Yes, 0: No  use_docked_mode =  # Allow the use of NFC in games | 
