early-access version 1272
This commit is contained in:
		| @@ -1,7 +1,7 @@ | ||||
| yuzu emulator early access | ||||
| ============= | ||||
|  | ||||
| This is the source code for early-access 1269. | ||||
| This is the source code for early-access 1272. | ||||
|  | ||||
| ## Legal Notice | ||||
|  | ||||
|   | ||||
| @@ -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 | ||||
| @@ -145,6 +146,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 | ||||
| @@ -169,6 +171,7 @@ add_library(common STATIC | ||||
|     time_zone.h | ||||
|     timer.cpp | ||||
|     timer.h | ||||
|     tree.h | ||||
|     uint128.cpp | ||||
|     uint128.h | ||||
|     uuid.cpp | ||||
|   | ||||
| @@ -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) { | ||||
|   | ||||
							
								
								
									
										647
									
								
								src/common/intrusive_red_black_tree.h
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										647
									
								
								src/common/intrusive_red_black_tree.h
									
									
									
									
									
										Executable file
									
								
							| @@ -0,0 +1,647 @@ | ||||
| /* | ||||
|  * Copyright (c) 2018-2020 Atmosph<70>re-NX | ||||
|  * | ||||
|  * This program is free software; you can redistribute it and/or modify it | ||||
|  * under the terms and conditions of the GNU General Public License, | ||||
|  * version 2, as published by the Free Software Foundation. | ||||
|  * | ||||
|  * This program is distributed in the hope it will be useful, but WITHOUT | ||||
|  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | ||||
|  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for | ||||
|  * more details. | ||||
|  * | ||||
|  * You should have received a copy of the GNU General Public License | ||||
|  * along with this program.  If not, see <http://www.gnu.org/licenses/>. | ||||
|  */ | ||||
|  | ||||
| #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() : entry() { /* ... */ | ||||
|     } | ||||
| }; | ||||
|  | ||||
| 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 = typename std::conditional<Const, IntrusiveRedBlackTreeImpl::const_pointer, | ||||
|                                                   IntrusiveRedBlackTreeImpl::pointer>::type; | ||||
|         using reference = | ||||
|             typename std::conditional<Const, IntrusiveRedBlackTreeImpl::const_reference, | ||||
|                                       IntrusiveRedBlackTreeImpl::reference>::type; | ||||
|  | ||||
|     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(IntrusiveRedBlackTreeNode const* node) { | ||||
|         return static_cast<const IntrusiveRedBlackTreeNode*>( | ||||
|             GetNext(const_cast<IntrusiveRedBlackTreeNode*>(node))); | ||||
|     } | ||||
|  | ||||
|     static IntrusiveRedBlackTreeNode const* GetPrev(IntrusiveRedBlackTreeNode const* 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 = | ||||
|     typename std::remove_pointer<decltype(impl::GetLightCompareType<T, Default>())>::type; | ||||
|  | ||||
| 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 = | ||||
|             typename std::conditional<Const, ImplType::const_iterator, ImplType::iterator>::type; | ||||
|  | ||||
|         using iterator_category = std::bidirectional_iterator_tag; | ||||
|         using value_type = typename IntrusiveRedBlackTree::value_type; | ||||
|         using difference_type = typename IntrusiveRedBlackTree::difference_type; | ||||
|         using pointer = typename std::conditional<Const, IntrusiveRedBlackTree::const_pointer, | ||||
|                                                   IntrusiveRedBlackTree::pointer>::type; | ||||
|         using reference = typename std::conditional<Const, IntrusiveRedBlackTree::const_reference, | ||||
|                                                     IntrusiveRedBlackTree::reference>::type; | ||||
|  | ||||
|     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(IntrusiveRedBlackTreeNode const* node) const { | ||||
|         return RB_FIND( | ||||
|             IntrusiveRedBlackTreeRootWithCompare, | ||||
|             const_cast<IntrusiveRedBlackTreeRootWithCompare*>( | ||||
|                 static_cast<const IntrusiveRedBlackTreeRootWithCompare*>(&this->impl.root)), | ||||
|             const_cast<IntrusiveRedBlackTreeNode*>(node)); | ||||
|     } | ||||
|  | ||||
|     IntrusiveRedBlackTreeNode* NFindImpl(IntrusiveRedBlackTreeNode const* 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() : impl() { /* ... */ | ||||
|     } | ||||
|  | ||||
|     /* 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(IntrusiveRedBlackTreeNode const* node) { | ||||
|         return GetParentPointer<Member, Derived>(node); | ||||
|     } | ||||
|  | ||||
| private: | ||||
|     static constexpr TYPED_STORAGE(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() { | ||||
|         TYPED_STORAGE(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(IntrusiveRedBlackTreeNode const* 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(IntrusiveRedBlackTreeNode const* node) { | ||||
|         return static_cast<const Derived*>(node); | ||||
|     } | ||||
| }; | ||||
|  | ||||
| } // namespace Common | ||||
							
								
								
									
										204
									
								
								src/common/parent_of_member.h
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										204
									
								
								src/common/parent_of_member.h
									
									
									
									
									
										Executable file
									
								
							| @@ -0,0 +1,204 @@ | ||||
| /* | ||||
|  * Copyright (c) 2018-2020 Atmosph<70>re-NX | ||||
|  * | ||||
|  * This program is free software; you can redistribute it and/or modify it | ||||
|  * under the terms and conditions of the GNU General Public License, | ||||
|  * version 2, as published by the Free Software Foundation. | ||||
|  * | ||||
|  * This program is distributed in the hope it will be useful, but WITHOUT | ||||
|  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | ||||
|  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for | ||||
|  * more details. | ||||
|  * | ||||
|  * You should have received a copy of the GNU General Public License | ||||
|  * along with this program.  If not, see <http://www.gnu.org/licenses/>. | ||||
|  */ | ||||
|  | ||||
| #pragma once | ||||
|  | ||||
| #include <type_traits> | ||||
|  | ||||
| #include "common/assert.h" | ||||
| #include "common/common_types.h" | ||||
|  | ||||
| namespace Common { | ||||
|  | ||||
| template <typename T, size_t Size, size_t Align> | ||||
| struct TypedStorage { | ||||
|     typename std::aligned_storage<Size, Align>::type _storage; | ||||
| }; | ||||
|  | ||||
| #define TYPED_STORAGE(...) TypedStorage<__VA_ARGS__, sizeof(__VA_ARGS__), alignof(__VA_ARGS__)> | ||||
|  | ||||
| template <typename T> | ||||
| static constexpr T* GetPointer(TYPED_STORAGE(T) & ts) { | ||||
|     return static_cast<T*>(static_cast<void*>(std::addressof(ts._storage))); | ||||
| } | ||||
|  | ||||
| template <typename T> | ||||
| static constexpr const T* GetPointer(const TYPED_STORAGE(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> { /* Empty ... */ | ||||
|     }; | ||||
| }; | ||||
|  | ||||
| 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; | ||||
|         TYPED_STORAGE(ParentType) parent; | ||||
|  | ||||
|         /* This coerces the active member to be c. */ | ||||
|         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 | ||||
							
								
								
									
										822
									
								
								src/common/tree.h
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										822
									
								
								src/common/tree.h
									
									
									
									
									
										Executable file
									
								
							| @@ -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_ */ | ||||
| @@ -141,8 +141,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 | ||||
| @@ -156,13 +154,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 | ||||
| @@ -182,8 +186,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 | ||||
| @@ -209,12 +211,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 | ||||
|   | ||||
| @@ -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_) { | ||||
|   | ||||
| @@ -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)); | ||||
| } | ||||
|  | ||||
|   | ||||
| @@ -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; | ||||
|   | ||||
| @@ -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: | ||||
|   | ||||
| @@ -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}; | ||||
|   | ||||
							
								
								
									
										367
									
								
								src/core/hle/kernel/k_address_arbiter.cpp
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										367
									
								
								src/core/hle/kernel/k_address_arbiter.cpp
									
									
									
									
									
										Executable file
									
								
							| @@ -0,0 +1,367 @@ | ||||
| // Copyright 2020 yuzu Emulator Project | ||||
| // Licensed under GPLv2 or any later version | ||||
| // Refer to the license.txt file included. | ||||
|  | ||||
| // This file references various implementation details from Atmosphere, an open-source firmware for | ||||
| // the Nintendo Switch. Copyright 2018-2020 Atmosphere-NX. | ||||
|  | ||||
| #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); | ||||
|     } | ||||
|  | ||||
|     // 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); | ||||
|     } | ||||
|  | ||||
|     // 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 | ||||
							
								
								
									
										73
									
								
								src/core/hle/kernel/k_address_arbiter.h
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										73
									
								
								src/core/hle/kernel/k_address_arbiter.h
									
									
									
									
									
										Executable file
									
								
							| @@ -0,0 +1,73 @@ | ||||
| // Copyright 2020 yuzu Emulator Project | ||||
| // Licensed under GPLv2 or any later version | ||||
| // Refer to the license.txt file included. | ||||
|  | ||||
| // This file references various implementation details from Atmosphere, an open-source firmware for | ||||
| // the Nintendo Switch. Copyright 2018-2020 Atmosphere-NX. | ||||
|  | ||||
| #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 | ||||
							
								
								
									
										350
									
								
								src/core/hle/kernel/k_condition_variable.cpp
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										350
									
								
								src/core/hle/kernel/k_condition_variable.cpp
									
									
									
									
									
										Executable file
									
								
							| @@ -0,0 +1,350 @@ | ||||
| // Copyright 2020 yuzu Emulator Project | ||||
| // Licensed under GPLv2 or any later version | ||||
| // Refer to the license.txt file included. | ||||
|  | ||||
| // This file references various implementation details from Atmosphere, an open-source firmware for | ||||
| // the Nintendo Switch. Copyright 2018-2020 Atmosphere-NX. | ||||
|  | ||||
| #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->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->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 | ||||
							
								
								
									
										62
									
								
								src/core/hle/kernel/k_condition_variable.h
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										62
									
								
								src/core/hle/kernel/k_condition_variable.h
									
									
									
									
									
										Executable file
									
								
							| @@ -0,0 +1,62 @@ | ||||
| // Copyright 2020 yuzu Emulator Project | ||||
| // Licensed under GPLv2 or any later version | ||||
| // Refer to the license.txt file included. | ||||
|  | ||||
| // This file references various implementation details from Atmosphere, an open-source firmware for | ||||
| // the Nintendo Switch. Copyright 2018-2020 Atmosphere-NX. | ||||
|  | ||||
| #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 | ||||
| @@ -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); | ||||
|     } | ||||
| } | ||||
|  | ||||
|   | ||||
| @@ -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, | ||||
|   | ||||
							
								
								
									
										174
									
								
								src/core/hle/kernel/k_synchronization_object.cpp
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										174
									
								
								src/core/hle/kernel/k_synchronization_object.cpp
									
									
									
									
									
										Executable file
									
								
							| @@ -0,0 +1,174 @@ | ||||
| // Copyright 2020 yuzu Emulator Project | ||||
| // Licensed under GPLv2 or any later version | ||||
| // Refer to the license.txt file included. | ||||
|  | ||||
| // This file references various implementation details from Atmosphere, an open-source firmware for | ||||
| // the Nintendo Switch. Copyright 2018-2020 Atmosphere-NX. | ||||
|  | ||||
| #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, num_objects); | ||||
|  | ||||
|         // Mark the thread as waiting. | ||||
|         thread->SetCancellable(); | ||||
|         thread->SetSyncedObject(nullptr, Svc::ResultTimedOut); | ||||
|         thread->SetState(ThreadState::Waiting); | ||||
|     } | ||||
|  | ||||
|     // 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(nullptr, 0); | ||||
|  | ||||
|     // 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 | ||||
							
								
								
									
										61
									
								
								src/core/hle/kernel/k_synchronization_object.h
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										61
									
								
								src/core/hle/kernel/k_synchronization_object.h
									
									
									
									
									
										Executable file
									
								
							| @@ -0,0 +1,61 @@ | ||||
| // Copyright 2020 yuzu Emulator Project | ||||
| // Licensed under GPLv2 or any later version | ||||
| // Refer to the license.txt file included. | ||||
|  | ||||
| // This file references various implementation details from Atmosphere, an open-source firmware for | ||||
| // the Nintendo Switch. Copyright 2018-2020 Atmosphere-NX. | ||||
|  | ||||
| #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 | ||||
| @@ -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,9 @@ 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); | ||||
|         } | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -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(); | ||||
|  | ||||
|   | ||||
| @@ -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; | ||||
|  | ||||
|   | ||||
| @@ -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 | ||||
|   | ||||
| @@ -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) { | ||||
|   | ||||
| @@ -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); | ||||
|  | ||||
|     virtual 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; | ||||
| }; | ||||
|   | ||||
| @@ -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() { | ||||
|   | ||||
| @@ -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(); | ||||
|  | ||||
|     virtual bool IsSignaled() const override; | ||||
|  | ||||
| private: | ||||
|     explicit ReadableEvent(KernelCore& kernel); | ||||
|  | ||||
|     bool is_signaled{}; | ||||
|     std::string name; ///< Name of event (optional) | ||||
| }; | ||||
|  | ||||
|   | ||||
| @@ -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 { | ||||
|   | ||||
| @@ -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,10 +79,7 @@ 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; | ||||
|     virtual bool IsSignaled() const override; | ||||
|  | ||||
| private: | ||||
|     /// ServerSessions waiting to be accepted by the port | ||||
|   | ||||
| @@ -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); | ||||
|         } | ||||
|     } | ||||
|   | ||||
| @@ -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; | ||||
|     } | ||||
|  | ||||
|     virtual bool IsSignaled() const override; | ||||
|  | ||||
| private: | ||||
|     /// Queues a sync request from the emulated application. | ||||
|     ResultCode QueueSyncRequest(std::shared_ptr<Thread> thread, Core::Memory::Memory& memory); | ||||
|   | ||||
| @@ -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 | ||||
|   | ||||
| @@ -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; | ||||
|   | ||||
| @@ -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,10 @@ 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); | ||||
|         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 +422,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 +444,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 +488,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); | ||||
| /// 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 (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; | ||||
|     } | ||||
|     // Validate the input address. | ||||
|     R_UNLESS(!Memory::IsKernelAddress(address), Svc::ResultInvalidCurrentMemory); | ||||
|     R_UNLESS(Common::IsAligned(address, sizeof(u32)), Svc::ResultInvalidAddress); | ||||
|  | ||||
|     if (!Common::IsWordAligned(mutex_addr)) { | ||||
|         LOG_ERROR(Kernel_SVC, "Mutex Address is not word aligned, mutex_addr={:016X}", mutex_addr); | ||||
|         return ERR_INVALID_ADDRESS; | ||||
|     } | ||||
|  | ||||
|     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); | ||||
| static ResultCode ArbitrateUnlock(Core::System& system, VAddr address) { | ||||
|     LOG_TRACE(Kernel_SVC, "called address=0x{:X}", address); | ||||
|  | ||||
|     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; | ||||
|     } | ||||
|     // Validate the input address. | ||||
|     R_UNLESS(!Memory::IsKernelAddress(address), Svc::ResultInvalidCurrentMemory); | ||||
|     R_UNLESS(Common::IsAligned(address, sizeof(u32)), Svc::ResultInvalidAddress); | ||||
|  | ||||
|     if (!Common::IsWordAligned(mutex_addr)) { | ||||
|         LOG_ERROR(Kernel_SVC, "Mutex Address is not word aligned, mutex_addr={:016X}", mutex_addr); | ||||
|         return ERR_INVALID_ADDRESS; | ||||
|     } | ||||
|  | ||||
|     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 +1145,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 +1524,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 +1585,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); | ||||
| 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); | ||||
|  | ||||
|     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; | ||||
|     } | ||||
|     // Validate input. | ||||
|     R_UNLESS(!Memory::IsKernelAddress(address), Svc::ResultInvalidCurrentMemory); | ||||
|     R_UNLESS(Common::IsAligned(address, sizeof(int32_t)), Svc::ResultInvalidAddress); | ||||
|  | ||||
|     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; | ||||
|     // 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(); | ||||
|         } | ||||
|  | ||||
|         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)); | ||||
|     } else { | ||||
|         timeout = timeout_ns; | ||||
|     } | ||||
|  | ||||
|     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)); | ||||
|         } | ||||
|  | ||||
|         current_process->RemoveConditionVariableThread(SharedFrom(current_thread)); | ||||
|     } | ||||
|     // 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); | ||||
| static void SignalProcessWideKey32(Core::System& system, u32 cv_key, s32 count) { | ||||
|     SignalProcessWideKey(system, cv_key, count); | ||||
| } | ||||
|  | ||||
|     // 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]; | ||||
| namespace { | ||||
|  | ||||
|         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); | ||||
|             } | ||||
|  | ||||
|             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); | ||||
|             } | ||||
|  | ||||
|             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; | ||||
|     } | ||||
| } | ||||
|  | ||||
| } // namespace | ||||
|  | ||||
| // 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); | ||||
| 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); | ||||
|  | ||||
|     // 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; | ||||
|     // 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; | ||||
|     } | ||||
|  | ||||
|     // 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; | ||||
|     } | ||||
|  | ||||
|     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); | ||||
| 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 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; | ||||
|     } | ||||
|     // 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); | ||||
|  | ||||
|     // 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; | ||||
|     } | ||||
|  | ||||
|     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, | ||||
|   | ||||
							
								
								
									
										14
									
								
								src/core/hle/kernel/svc_common.h
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										14
									
								
								src/core/hle/kernel/svc_common.h
									
									
									
									
									
										Executable file
									
								
							| @@ -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 | ||||
							
								
								
									
										20
									
								
								src/core/hle/kernel/svc_results.h
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										20
									
								
								src/core/hle/kernel/svc_results.h
									
									
									
									
									
										Executable file
									
								
							| @@ -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 | ||||
| @@ -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 | ||||
|   | ||||
| @@ -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; | ||||
|   | ||||
| @@ -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; | ||||
|     return signaled; | ||||
| } | ||||
|  | ||||
| void Thread::Acquire(Thread* thread) { | ||||
|     ASSERT_MSG(!ShouldWait(thread), "object unavailable!"); | ||||
| } | ||||
|  | ||||
| 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,182 @@ 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; | ||||
|     SetMutexWaitAddressForDebugging(0); | ||||
|     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); | ||||
|     } | ||||
|  | ||||
|     status = new_status; | ||||
| } | ||||
|  | ||||
| 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()); | ||||
|  | ||||
|     // 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); | ||||
|  | ||||
|     UpdatePriority(); | ||||
|     // Insert the waiter. | ||||
|     waiter_list.insert(it, *thread); | ||||
|     thread->SetLockOwner(this); | ||||
| } | ||||
|  | ||||
| void Thread::RemoveMutexWaiter(std::shared_ptr<Thread> thread) { | ||||
|     ASSERT(thread->lock_owner.get() == this); | ||||
| void Thread::RemoveWaiterImpl(Thread* thread) { | ||||
|     ASSERT(kernel.GlobalSchedulerContext().IsLocked()); | ||||
|  | ||||
|     // 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()); | ||||
|     // Keep track of how many kernel waiters we have. | ||||
|     if (Memory::IsKernelAddressKey(thread->GetAddressKey())) { | ||||
|         ASSERT((num_kernel_waiters--) > 0); | ||||
|     } | ||||
|  | ||||
|     wait_mutex_threads.erase(iter); | ||||
|  | ||||
|     thread->lock_owner = nullptr; | ||||
|     UpdatePriority(); | ||||
|     // Remove the waiter. | ||||
|     waiter_list.erase(waiter_list.iterator_to(*thread)); | ||||
|     thread->SetLockOwner(nullptr); | ||||
| } | ||||
|  | ||||
| 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; | ||||
| void Thread::RestorePriority(KernelCore& kernel, Thread* thread) { | ||||
|     ASSERT(kernel.GlobalSchedulerContext().IsLocked()); | ||||
|  | ||||
|     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()); | ||||
|         } | ||||
|  | ||||
|         // If the priority we would inherit is not different from ours, don't do anything. | ||||
|         if (new_priority == thread->GetPriority()) { | ||||
|             return; | ||||
|         } | ||||
|  | ||||
|         // 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); | ||||
|         } | ||||
|  | ||||
|         // Change the priority. | ||||
|         const s32 old_priority = thread->GetPriority(); | ||||
|         thread->SetPriority(new_priority); | ||||
|  | ||||
|         // Restore the condition variable, if relevant. | ||||
|         if (auto* cv_tree = thread->GetConditionVariableTree(); cv_tree != nullptr) { | ||||
|             AfterUpdatePriority(kernel, cv_tree, thread); | ||||
|         } | ||||
|  | ||||
|         // Update the scheduler. | ||||
|         KScheduler::OnThreadPriorityChanged(kernel, thread, old_priority); | ||||
|  | ||||
|         // Keep the lock owner up to date. | ||||
|         Thread* lock_owner = thread->GetLockOwner(); | ||||
|         if (lock_owner == nullptr) { | ||||
|             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; | ||||
|     } | ||||
| } | ||||
|  | ||||
| void Thread::AddWaiter(Thread* thread) { | ||||
|     AddWaiterImpl(thread); | ||||
|     RestorePriority(kernel, this); | ||||
| } | ||||
|  | ||||
| void Thread::RemoveWaiter(Thread* thread) { | ||||
|     RemoveWaiterImpl(thread); | ||||
|     RestorePriority(kernel, this); | ||||
| } | ||||
|  | ||||
| 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++; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     if (new_priority == current_priority) { | ||||
|         return; | ||||
|     // Do priority updates, if we have a next owner. | ||||
|     if (next_lock_owner) { | ||||
|         RestorePriority(kernel, this); | ||||
|         RestorePriority(kernel, next_lock_owner); | ||||
|     } | ||||
|  | ||||
|     if (GetStatus() == ThreadStatus::WaitCondVar) { | ||||
|         owner_process->RemoveConditionVariableThread(SharedFrom(this)); | ||||
|     } | ||||
|  | ||||
|     SetCurrentPriority(new_priority); | ||||
|  | ||||
|     if (GetStatus() == ThreadStatus::WaitCondVar) { | ||||
|         owner_process->InsertConditionVariableThread(SharedFrom(this)); | ||||
|     } | ||||
|  | ||||
|     if (!lock_owner) { | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     // 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(); | ||||
| } | ||||
|  | ||||
| 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); | ||||
|                         }); | ||||
| } | ||||
|  | ||||
| bool Thread::InvokeHLECallback(std::shared_ptr<Thread> thread) { | ||||
|     ASSERT(hle_callback); | ||||
|     return hle_callback(std::move(thread)); | ||||
|     // 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 +385,7 @@ ResultCode Thread::Sleep(s64 nanoseconds) { | ||||
|     Handle event_handle{}; | ||||
|     { | ||||
|         KScopedSchedulerLockAndSleep lock(kernel, event_handle, this, nanoseconds); | ||||
|         SetStatus(ThreadStatus::WaitSleep); | ||||
|         SetState(ThreadState::Waiting); | ||||
|     } | ||||
|  | ||||
|     if (event_handle != InvalidHandle) { | ||||
| @@ -405,34 +396,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) { | ||||
|   | ||||
| @@ -10,12 +10,16 @@ | ||||
| #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 +77,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 +106,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 +113,10 @@ enum class ThreadSchedFlags : u32 { | ||||
|     KernelInitPauseFlag = 1 << 8, | ||||
| }; | ||||
|  | ||||
| enum class ThreadSchedMasks : u32 { | ||||
|     LowMask = 0x000f, | ||||
|     HighMask = 0xfff0, | ||||
|     ForcePauseMask = 0x0070, | ||||
| }; | ||||
| class Thread final : public KSynchronizationObject, public boost::intrusive::list_base_hook<> { | ||||
|     friend class KScheduler; | ||||
|     friend class Process; | ||||
|  | ||||
| class Thread final : public SynchronizationObject { | ||||
| public: | ||||
|     explicit Thread(KernelCore& kernel); | ||||
|     ~Thread() override; | ||||
| @@ -127,10 +126,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 +181,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,30 +237,21 @@ 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 GetWaitResult(KSynchronizationObject** out) const { | ||||
|         *out = signaling_object; | ||||
|         return signaling_result; | ||||
|     } | ||||
|  | ||||
|     ResultCode GetSignalingResult() const { | ||||
|         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; | ||||
|  | ||||
|     /** | ||||
|      * Stops a thread, invalidating it from further use | ||||
|      */ | ||||
| @@ -341,18 +322,22 @@ public: | ||||
|  | ||||
|     std::shared_ptr<Common::Fiber>& GetHostContext(); | ||||
|  | ||||
|     ThreadStatus GetStatus() const { | ||||
|         return status; | ||||
|     ThreadState GetState() const { | ||||
|         return thread_state & ThreadState::Mask; | ||||
|     } | ||||
|  | ||||
|     void SetStatus(ThreadStatus new_status); | ||||
|     ThreadState GetRawState() const { | ||||
|         return thread_state; | ||||
|     } | ||||
|  | ||||
|     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 +372,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(); | ||||
|         return lock_owner; | ||||
|     } | ||||
|  | ||||
|     void SetLockOwner(std::shared_ptr<Thread> owner) { | ||||
|         lock_owner = std::move(owner); | ||||
|     void SetLockOwner(Thread* owner) { | ||||
|         lock_owner = 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; | ||||
|     } | ||||
|  | ||||
|     void InvalidateHLECallback() { | ||||
|         SetHLECallback(nullptr); | ||||
|     } | ||||
|  | ||||
|     bool InvokeHLECallback(std::shared_ptr<Thread> thread); | ||||
|  | ||||
|     u32 GetIdealCore() const { | ||||
|         return ideal_core; | ||||
|     } | ||||
| @@ -493,20 +398,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 +413,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 +470,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 +493,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 +514,162 @@ public: | ||||
|         disable_count--; | ||||
|     } | ||||
|  | ||||
| private: | ||||
|     friend class GlobalSchedulerContext; | ||||
|     friend class KScheduler; | ||||
|     friend class Process; | ||||
|     void SetWaitObjectsForDebugging(KSynchronizationObject** objects, s32 num_objects) { | ||||
|         wait_objects_for_debugging.clear(); | ||||
|         wait_objects_for_debugging.reserve(num_objects); | ||||
|         for (auto i = 0; i < num_objects; ++i) { | ||||
|             wait_objects_for_debugging.emplace_back(objects[i]); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     void SetSchedulingStatus(ThreadSchedStatus new_status); | ||||
|     [[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; | ||||
|     } | ||||
|  | ||||
|     [[nodiscard]] void SetAddressKey(VAddr key) { | ||||
|         address_key = key; | ||||
|     } | ||||
|  | ||||
|     [[nodiscard]] void SetAddressKey(VAddr key, u32 val) { | ||||
|         address_key = key; | ||||
|         address_key_value = val; | ||||
|     } | ||||
|  | ||||
| private: | ||||
|     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() : sync_objects() {} | ||||
|     }; | ||||
|     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 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 +682,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 +701,24 @@ 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{}; | ||||
|  | ||||
|     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 +731,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 +742,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; | ||||
| }; | ||||
|  | ||||
|   | ||||
| @@ -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 { | ||||
|   | ||||
| @@ -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)); | ||||
|   | ||||
| @@ -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; | ||||
|   | ||||
| @@ -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); | ||||
|   | ||||
| @@ -1030,11 +1030,44 @@ public: | ||||
|         } | ||||
|         return {}; | ||||
|     } | ||||
|     [[nodiscard]] std::optional<Common::ParamPackage> FromEvent(const SDL_Event& event) const { | ||||
|     [[nodiscard]] std::optional<Common::ParamPackage> FromEvent(SDL_Event& event) { | ||||
|         switch (event.type) { | ||||
|         case SDL_JOYAXISMOTION: | ||||
|             if (std::abs(event.jaxis.value / 32767.0) < 0.5) { | ||||
|             if (!axis_memory.count(event.jaxis.which) || | ||||
|                 !axis_memory[event.jaxis.which].count(event.jaxis.axis)) { | ||||
|                 axis_memory[event.jaxis.which][event.jaxis.axis] = event.jaxis.value; | ||||
|                 axis_event_count[event.jaxis.which][event.jaxis.axis] = 1; | ||||
|                 break; | ||||
|             } else { | ||||
|                 axis_event_count[event.jaxis.which][event.jaxis.axis]++; | ||||
|                 // The joystick and axis exist in our map if we take this branch, so no checks | ||||
|                 // needed | ||||
|                 if (std::abs( | ||||
|                         (event.jaxis.value - axis_memory[event.jaxis.which][event.jaxis.axis]) / | ||||
|                         32767.0) < 0.5) { | ||||
|                     break; | ||||
|                 } else { | ||||
|                     if (axis_event_count[event.jaxis.which][event.jaxis.axis] == 2 && | ||||
|                         IsAxisAtPole(event.jaxis.value) && | ||||
|                         IsAxisAtPole(axis_memory[event.jaxis.which][event.jaxis.axis])) { | ||||
|                         // If we have exactly two events and both are near a pole, this is | ||||
|                         // likely a digital input masquerading as an analog axis; Instead of | ||||
|                         // trying to look at the direction the axis travelled, assume the first | ||||
|                         // event was press and the second was release; This should handle most | ||||
|                         // digital axes while deferring to the direction of travel for analog | ||||
|                         // axes | ||||
|                         event.jaxis.value = static_cast<Sint16>( | ||||
|                             std::copysign(32767, axis_memory[event.jaxis.which][event.jaxis.axis])); | ||||
|                     } else { | ||||
|                         // There are more than two events, so this is likely a true analog axis, | ||||
|                         // check the direction it travelled | ||||
|                         event.jaxis.value = static_cast<Sint16>(std::copysign( | ||||
|                             32767, | ||||
|                             event.jaxis.value - axis_memory[event.jaxis.which][event.jaxis.axis])); | ||||
|                     } | ||||
|                     axis_memory.clear(); | ||||
|                     axis_event_count.clear(); | ||||
|                 } | ||||
|             } | ||||
|             [[fallthrough]]; | ||||
|         case SDL_JOYBUTTONUP: | ||||
| @@ -1043,6 +1076,16 @@ public: | ||||
|         } | ||||
|         return std::nullopt; | ||||
|     } | ||||
|  | ||||
| private: | ||||
|     // Determine whether an axis value is close to an extreme or center | ||||
|     // Some controllers have a digital D-Pad as a pair of analog sticks, with 3 possible values per | ||||
|     // axis, which is why the center must be considered a pole | ||||
|     bool IsAxisAtPole(int16_t value) const { | ||||
|         return std::abs(value) >= 32767 || std::abs(value) < 327; | ||||
|     } | ||||
|     std::unordered_map<SDL_JoystickID, std::unordered_map<uint8_t, int16_t>> axis_memory; | ||||
|     std::unordered_map<SDL_JoystickID, std::unordered_map<uint8_t, uint32_t>> axis_event_count; | ||||
| }; | ||||
|  | ||||
| class SDLMotionPoller final : public SDLPoller { | ||||
|   | ||||
| @@ -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,14 @@ QString WaitTreeThread::GetText() const { | ||||
|             status = tr("paused"); | ||||
|         } | ||||
|         break; | ||||
|     case Kernel::ThreadStatus::Paused: | ||||
|         status = tr("paused"); | ||||
|     case Kernel::ThreadState::Waiting: | ||||
|         status = tr("waiting"); | ||||
|         break; | ||||
|     case Kernel::ThreadStatus::WaitHLEEvent: | ||||
|         status = tr("waiting for HLE return"); | ||||
|     case Kernel::ThreadState::Initialized: | ||||
|         status = tr("initialized"); | ||||
|         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"); | ||||
|         break; | ||||
|     case Kernel::ThreadStatus::WaitArb: | ||||
|         status = tr("waiting for address arbiter"); | ||||
|         break; | ||||
|     case Kernel::ThreadStatus::Dormant: | ||||
|         status = tr("dormant"); | ||||
|         break; | ||||
|     case Kernel::ThreadStatus::Dead: | ||||
|         status = tr("dead"); | ||||
|     case Kernel::ThreadState::Terminated: | ||||
|         status = tr("terminated"); | ||||
|         break; | ||||
|     } | ||||
|  | ||||
| @@ -293,8 +273,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 +284,11 @@ QColor WaitTreeThread::GetColor() const { | ||||
|         } else { | ||||
|             return QColor(WaitTreeColors[2][color_index]); | ||||
|         } | ||||
|     case Kernel::ThreadStatus::Paused: | ||||
|     case Kernel::ThreadState::Waiting: | ||||
|         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::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 +324,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 +336,9 @@ 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) { | ||||
|         list.push_back(std::make_unique<WaitTreeObjectList>(thread.GetWaitObjectsForDebugging(), | ||||
|                                                             thread.IsCancellable())); | ||||
|     } | ||||
|  | ||||
|     list.push_back(std::make_unique<WaitTreeCallstack>(thread)); | ||||
| @@ -380,7 +350,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; | ||||
|  | ||||
|   | ||||
| @@ -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 { | ||||
|   | ||||
		Reference in New Issue
	
	Block a user