mirror of
				https://git.zaroz.cloud/nintendo-back-up/yuzu/yuzu-mainline.git
				synced 2025-03-21 01:53:15 +00:00 
			
		
		
		
	
						commit
						af1ff4d3ce
					
				| @ -10,6 +10,7 @@ | |||||||
| #include "core/hle/kernel/semaphore.h" | #include "core/hle/kernel/semaphore.h" | ||||||
| #include "core/hle/kernel/thread.h" | #include "core/hle/kernel/thread.h" | ||||||
| #include "core/hle/kernel/timer.h" | #include "core/hle/kernel/timer.h" | ||||||
|  | #include "core/hle/kernel/wait_object.h" | ||||||
| 
 | 
 | ||||||
| WaitTreeItem::~WaitTreeItem() {} | WaitTreeItem::~WaitTreeItem() {} | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -4,12 +4,10 @@ | |||||||
| 
 | 
 | ||||||
| #pragma once | #pragma once | ||||||
| 
 | 
 | ||||||
| #include <boost/container/flat_set.hpp> |  | ||||||
| 
 |  | ||||||
| #include <QAbstractItemModel> | #include <QAbstractItemModel> | ||||||
| #include <QDockWidget> | #include <QDockWidget> | ||||||
| #include <QTreeView> | #include <QTreeView> | ||||||
| 
 | #include <boost/container/flat_set.hpp> | ||||||
| #include "core/core.h" | #include "core/core.h" | ||||||
| #include "core/hle/kernel/kernel.h" | #include "core/hle/kernel/kernel.h" | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -45,6 +45,7 @@ set(SRCS | |||||||
|             hle/kernel/client_port.cpp |             hle/kernel/client_port.cpp | ||||||
|             hle/kernel/client_session.cpp |             hle/kernel/client_session.cpp | ||||||
|             hle/kernel/event.cpp |             hle/kernel/event.cpp | ||||||
|  |             hle/kernel/handle_table.cpp | ||||||
|             hle/kernel/kernel.cpp |             hle/kernel/kernel.cpp | ||||||
|             hle/kernel/memory.cpp |             hle/kernel/memory.cpp | ||||||
|             hle/kernel/mutex.cpp |             hle/kernel/mutex.cpp | ||||||
| @ -57,6 +58,7 @@ set(SRCS | |||||||
|             hle/kernel/thread.cpp |             hle/kernel/thread.cpp | ||||||
|             hle/kernel/timer.cpp |             hle/kernel/timer.cpp | ||||||
|             hle/kernel/vm_manager.cpp |             hle/kernel/vm_manager.cpp | ||||||
|  |             hle/kernel/wait_object.cpp | ||||||
|             hle/service/ac/ac.cpp |             hle/service/ac/ac.cpp | ||||||
|             hle/service/ac/ac_i.cpp |             hle/service/ac/ac_i.cpp | ||||||
|             hle/service/ac/ac_u.cpp |             hle/service/ac/ac_u.cpp | ||||||
| @ -236,6 +238,7 @@ set(HEADERS | |||||||
|             hle/kernel/client_session.h |             hle/kernel/client_session.h | ||||||
|             hle/kernel/errors.h |             hle/kernel/errors.h | ||||||
|             hle/kernel/event.h |             hle/kernel/event.h | ||||||
|  |             hle/kernel/handle_table.h | ||||||
|             hle/kernel/kernel.h |             hle/kernel/kernel.h | ||||||
|             hle/kernel/memory.h |             hle/kernel/memory.h | ||||||
|             hle/kernel/mutex.h |             hle/kernel/mutex.h | ||||||
| @ -249,6 +252,7 @@ set(HEADERS | |||||||
|             hle/kernel/thread.h |             hle/kernel/thread.h | ||||||
|             hle/kernel/timer.h |             hle/kernel/timer.h | ||||||
|             hle/kernel/vm_manager.h |             hle/kernel/vm_manager.h | ||||||
|  |             hle/kernel/wait_object.h | ||||||
|             hle/result.h |             hle/result.h | ||||||
|             hle/service/ac/ac.h |             hle/service/ac/ac.h | ||||||
|             hle/service/ac/ac_i.h |             hle/service/ac/ac_i.h | ||||||
|  | |||||||
| @ -3,7 +3,9 @@ | |||||||
| // Refer to the license.txt file included.
 | // Refer to the license.txt file included.
 | ||||||
| 
 | 
 | ||||||
| #pragma once | #pragma once | ||||||
|  | 
 | ||||||
| #include "core/hle/ipc.h" | #include "core/hle/ipc.h" | ||||||
|  | #include "core/hle/kernel/handle_table.h" | ||||||
| #include "core/hle/kernel/kernel.h" | #include "core/hle/kernel/kernel.h" | ||||||
| 
 | 
 | ||||||
| namespace IPC { | namespace IPC { | ||||||
|  | |||||||
| @ -6,6 +6,7 @@ | |||||||
| 
 | 
 | ||||||
| #include "common/common_types.h" | #include "common/common_types.h" | ||||||
| #include "core/hle/kernel/kernel.h" | #include "core/hle/kernel/kernel.h" | ||||||
|  | #include "core/hle/result.h" | ||||||
| 
 | 
 | ||||||
| // Address arbiters are an underlying kernel synchronization object that can be created/used via
 | // Address arbiters are an underlying kernel synchronization object that can be created/used via
 | ||||||
| // supervisor calls (SVCs). They function as sort of a global lock. Typically, games/other CTR
 | // supervisor calls (SVCs). They function as sort of a global lock. Typically, games/other CTR
 | ||||||
|  | |||||||
| @ -7,6 +7,7 @@ | |||||||
| #include <string> | #include <string> | ||||||
| #include "common/common_types.h" | #include "common/common_types.h" | ||||||
| #include "core/hle/kernel/kernel.h" | #include "core/hle/kernel/kernel.h" | ||||||
|  | #include "core/hle/result.h" | ||||||
| 
 | 
 | ||||||
| namespace Kernel { | namespace Kernel { | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -6,10 +6,9 @@ | |||||||
| 
 | 
 | ||||||
| #include <memory> | #include <memory> | ||||||
| #include <string> | #include <string> | ||||||
| 
 |  | ||||||
| #include "common/common_types.h" | #include "common/common_types.h" | ||||||
| 
 |  | ||||||
| #include "core/hle/kernel/kernel.h" | #include "core/hle/kernel/kernel.h" | ||||||
|  | #include "core/hle/result.h" | ||||||
| 
 | 
 | ||||||
| namespace Kernel { | namespace Kernel { | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -6,6 +6,7 @@ | |||||||
| 
 | 
 | ||||||
| #include "common/common_types.h" | #include "common/common_types.h" | ||||||
| #include "core/hle/kernel/kernel.h" | #include "core/hle/kernel/kernel.h" | ||||||
|  | #include "core/hle/kernel/wait_object.h" | ||||||
| 
 | 
 | ||||||
| namespace Kernel { | namespace Kernel { | ||||||
| 
 | 
 | ||||||
|  | |||||||
							
								
								
									
										97
									
								
								src/core/hle/kernel/handle_table.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										97
									
								
								src/core/hle/kernel/handle_table.cpp
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,97 @@ | |||||||
|  | // Copyright 2014 Citra Emulator Project
 | ||||||
|  | // Licensed under GPLv2 or any later version
 | ||||||
|  | // Refer to the license.txt file included.
 | ||||||
|  | 
 | ||||||
|  | #include <utility> | ||||||
|  | #include "common/assert.h" | ||||||
|  | #include "common/logging/log.h" | ||||||
|  | #include "core/hle/kernel/errors.h" | ||||||
|  | #include "core/hle/kernel/handle_table.h" | ||||||
|  | #include "core/hle/kernel/kernel.h" | ||||||
|  | #include "core/hle/kernel/process.h" | ||||||
|  | #include "core/hle/kernel/thread.h" | ||||||
|  | 
 | ||||||
|  | namespace Kernel { | ||||||
|  | 
 | ||||||
|  | HandleTable g_handle_table; | ||||||
|  | 
 | ||||||
|  | HandleTable::HandleTable() { | ||||||
|  |     next_generation = 1; | ||||||
|  |     Clear(); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | ResultVal<Handle> HandleTable::Create(SharedPtr<Object> obj) { | ||||||
|  |     DEBUG_ASSERT(obj != nullptr); | ||||||
|  | 
 | ||||||
|  |     u16 slot = next_free_slot; | ||||||
|  |     if (slot >= generations.size()) { | ||||||
|  |         LOG_ERROR(Kernel, "Unable to allocate Handle, too many slots in use."); | ||||||
|  |         return ERR_OUT_OF_HANDLES; | ||||||
|  |     } | ||||||
|  |     next_free_slot = generations[slot]; | ||||||
|  | 
 | ||||||
|  |     u16 generation = next_generation++; | ||||||
|  | 
 | ||||||
|  |     // Overflow count so it fits in the 15 bits dedicated to the generation in the handle.
 | ||||||
|  |     // CTR-OS doesn't use generation 0, so skip straight to 1.
 | ||||||
|  |     if (next_generation >= (1 << 15)) | ||||||
|  |         next_generation = 1; | ||||||
|  | 
 | ||||||
|  |     generations[slot] = generation; | ||||||
|  |     objects[slot] = std::move(obj); | ||||||
|  | 
 | ||||||
|  |     Handle handle = generation | (slot << 15); | ||||||
|  |     return MakeResult<Handle>(handle); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | ResultVal<Handle> HandleTable::Duplicate(Handle handle) { | ||||||
|  |     SharedPtr<Object> object = GetGeneric(handle); | ||||||
|  |     if (object == nullptr) { | ||||||
|  |         LOG_ERROR(Kernel, "Tried to duplicate invalid handle: %08X", handle); | ||||||
|  |         return ERR_INVALID_HANDLE; | ||||||
|  |     } | ||||||
|  |     return Create(std::move(object)); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | ResultCode HandleTable::Close(Handle handle) { | ||||||
|  |     if (!IsValid(handle)) | ||||||
|  |         return ERR_INVALID_HANDLE; | ||||||
|  | 
 | ||||||
|  |     u16 slot = GetSlot(handle); | ||||||
|  | 
 | ||||||
|  |     objects[slot] = nullptr; | ||||||
|  | 
 | ||||||
|  |     generations[slot] = next_free_slot; | ||||||
|  |     next_free_slot = slot; | ||||||
|  |     return RESULT_SUCCESS; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool HandleTable::IsValid(Handle handle) const { | ||||||
|  |     size_t slot = GetSlot(handle); | ||||||
|  |     u16 generation = GetGeneration(handle); | ||||||
|  | 
 | ||||||
|  |     return slot < MAX_COUNT && objects[slot] != nullptr && generations[slot] == generation; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | SharedPtr<Object> HandleTable::GetGeneric(Handle handle) const { | ||||||
|  |     if (handle == CurrentThread) { | ||||||
|  |         return GetCurrentThread(); | ||||||
|  |     } else if (handle == CurrentProcess) { | ||||||
|  |         return g_current_process; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     if (!IsValid(handle)) { | ||||||
|  |         return nullptr; | ||||||
|  |     } | ||||||
|  |     return objects[GetSlot(handle)]; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void HandleTable::Clear() { | ||||||
|  |     for (u16 i = 0; i < MAX_COUNT; ++i) { | ||||||
|  |         generations[i] = i + 1; | ||||||
|  |         objects[i] = nullptr; | ||||||
|  |     } | ||||||
|  |     next_free_slot = 0; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | } // namespace
 | ||||||
							
								
								
									
										126
									
								
								src/core/hle/kernel/handle_table.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										126
									
								
								src/core/hle/kernel/handle_table.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,126 @@ | |||||||
|  | // Copyright 2014 Citra Emulator Project
 | ||||||
|  | // Licensed under GPLv2 or any later version
 | ||||||
|  | // Refer to the license.txt file included.
 | ||||||
|  | 
 | ||||||
|  | #pragma once | ||||||
|  | 
 | ||||||
|  | #include <array> | ||||||
|  | #include <cstddef> | ||||||
|  | #include "common/common_types.h" | ||||||
|  | #include "core/hle/kernel/kernel.h" | ||||||
|  | #include "core/hle/result.h" | ||||||
|  | 
 | ||||||
|  | namespace Kernel { | ||||||
|  | 
 | ||||||
|  | enum KernelHandle : Handle { | ||||||
|  |     CurrentThread = 0xFFFF8000, | ||||||
|  |     CurrentProcess = 0xFFFF8001, | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * This class allows the creation of Handles, which are references to objects that can be tested | ||||||
|  |  * for validity and looked up. Here they are used to pass references to kernel objects to/from the | ||||||
|  |  * emulated process. it has been designed so that it follows the same handle format and has | ||||||
|  |  * approximately the same restrictions as the handle manager in the CTR-OS. | ||||||
|  |  * | ||||||
|  |  * Handles contain two sub-fields: a slot index (bits 31:15) and a generation value (bits 14:0). | ||||||
|  |  * The slot index is used to index into the arrays in this class to access the data corresponding | ||||||
|  |  * to the Handle. | ||||||
|  |  * | ||||||
|  |  * To prevent accidental use of a freed Handle whose slot has already been reused, a global counter | ||||||
|  |  * is kept and incremented every time a Handle is created. This is the Handle's "generation". The | ||||||
|  |  * value of the counter is stored into the Handle as well as in the handle table (in the | ||||||
|  |  * "generations" array). When looking up a handle, the Handle's generation must match with the | ||||||
|  |  * value stored on the class, otherwise the Handle is considered invalid. | ||||||
|  |  * | ||||||
|  |  * To find free slots when allocating a Handle without needing to scan the entire object array, the | ||||||
|  |  * generations field of unallocated slots is re-purposed as a linked list of indices to free slots. | ||||||
|  |  * When a Handle is created, an index is popped off the list and used for the new Handle. When it | ||||||
|  |  * is destroyed, it is again pushed onto the list to be re-used by the next allocation. It is | ||||||
|  |  * likely that this allocation strategy differs from the one used in CTR-OS, but this hasn't been | ||||||
|  |  * verified and isn't likely to cause any problems. | ||||||
|  |  */ | ||||||
|  | class HandleTable final : NonCopyable { | ||||||
|  | public: | ||||||
|  |     HandleTable(); | ||||||
|  | 
 | ||||||
|  |     /**
 | ||||||
|  |      * Allocates a handle for the given object. | ||||||
|  |      * @return The created Handle or one of the following errors: | ||||||
|  |      *           - `ERR_OUT_OF_HANDLES`: the maximum number of handles has been exceeded. | ||||||
|  |      */ | ||||||
|  |     ResultVal<Handle> Create(SharedPtr<Object> obj); | ||||||
|  | 
 | ||||||
|  |     /**
 | ||||||
|  |      * Returns a new handle that points to the same object as the passed in handle. | ||||||
|  |      * @return The duplicated Handle or one of the following errors: | ||||||
|  |      *           - `ERR_INVALID_HANDLE`: an invalid handle was passed in. | ||||||
|  |      *           - Any errors returned by `Create()`. | ||||||
|  |      */ | ||||||
|  |     ResultVal<Handle> Duplicate(Handle handle); | ||||||
|  | 
 | ||||||
|  |     /**
 | ||||||
|  |      * Closes a handle, removing it from the table and decreasing the object's ref-count. | ||||||
|  |      * @return `RESULT_SUCCESS` or one of the following errors: | ||||||
|  |      *           - `ERR_INVALID_HANDLE`: an invalid handle was passed in. | ||||||
|  |      */ | ||||||
|  |     ResultCode Close(Handle handle); | ||||||
|  | 
 | ||||||
|  |     /// Checks if a handle is valid and points to an existing object.
 | ||||||
|  |     bool IsValid(Handle handle) const; | ||||||
|  | 
 | ||||||
|  |     /**
 | ||||||
|  |      * Looks up a handle. | ||||||
|  |      * @return Pointer to the looked-up object, or `nullptr` if the handle is not valid. | ||||||
|  |      */ | ||||||
|  |     SharedPtr<Object> GetGeneric(Handle handle) const; | ||||||
|  | 
 | ||||||
|  |     /**
 | ||||||
|  |      * Looks up a handle while verifying its type. | ||||||
|  |      * @return Pointer to the looked-up object, or `nullptr` if the handle is not valid or its | ||||||
|  |      *         type differs from the requested one. | ||||||
|  |      */ | ||||||
|  |     template <class T> | ||||||
|  |     SharedPtr<T> Get(Handle handle) const { | ||||||
|  |         return DynamicObjectCast<T>(GetGeneric(handle)); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     /// Closes all handles held in this table.
 | ||||||
|  |     void Clear(); | ||||||
|  | 
 | ||||||
|  | private: | ||||||
|  |     /**
 | ||||||
|  |      * This is the maximum limit of handles allowed per process in CTR-OS. It can be further | ||||||
|  |      * reduced by ExHeader values, but this is not emulated here. | ||||||
|  |      */ | ||||||
|  |     static const size_t MAX_COUNT = 4096; | ||||||
|  | 
 | ||||||
|  |     static u16 GetSlot(Handle handle) { | ||||||
|  |         return handle >> 15; | ||||||
|  |     } | ||||||
|  |     static u16 GetGeneration(Handle handle) { | ||||||
|  |         return handle & 0x7FFF; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     /// Stores the Object referenced by the handle or null if the slot is empty.
 | ||||||
|  |     std::array<SharedPtr<Object>, MAX_COUNT> objects; | ||||||
|  | 
 | ||||||
|  |     /**
 | ||||||
|  |      * The value of `next_generation` when the handle was created, used to check for validity. For | ||||||
|  |      * empty slots, contains the index of the next free slot in the list. | ||||||
|  |      */ | ||||||
|  |     std::array<u16, MAX_COUNT> generations; | ||||||
|  | 
 | ||||||
|  |     /**
 | ||||||
|  |      * Global counter of the number of created handles. Stored in `generations` when a handle is | ||||||
|  |      * created, and wraps around to 1 when it hits 0x8000. | ||||||
|  |      */ | ||||||
|  |     u16 next_generation; | ||||||
|  | 
 | ||||||
|  |     /// Head of the free slots linked list.
 | ||||||
|  |     u16 next_free_slot; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | extern HandleTable g_handle_table; | ||||||
|  | 
 | ||||||
|  | } // namespace
 | ||||||
| @ -2,11 +2,8 @@ | |||||||
| // Licensed under GPLv2 or any later version
 | // Licensed under GPLv2 or any later version
 | ||||||
| // Refer to the license.txt file included.
 | // Refer to the license.txt file included.
 | ||||||
| 
 | 
 | ||||||
| #include <algorithm> |  | ||||||
| #include "common/assert.h" |  | ||||||
| #include "common/logging/log.h" |  | ||||||
| #include "core/hle/config_mem.h" | #include "core/hle/config_mem.h" | ||||||
| #include "core/hle/kernel/errors.h" | #include "core/hle/kernel/handle_table.h" | ||||||
| #include "core/hle/kernel/kernel.h" | #include "core/hle/kernel/kernel.h" | ||||||
| #include "core/hle/kernel/memory.h" | #include "core/hle/kernel/memory.h" | ||||||
| #include "core/hle/kernel/process.h" | #include "core/hle/kernel/process.h" | ||||||
| @ -18,165 +15,6 @@ | |||||||
| namespace Kernel { | namespace Kernel { | ||||||
| 
 | 
 | ||||||
| unsigned int Object::next_object_id; | unsigned int Object::next_object_id; | ||||||
| HandleTable g_handle_table; |  | ||||||
| 
 |  | ||||||
| void WaitObject::AddWaitingThread(SharedPtr<Thread> thread) { |  | ||||||
|     auto itr = std::find(waiting_threads.begin(), waiting_threads.end(), thread); |  | ||||||
|     if (itr == waiting_threads.end()) |  | ||||||
|         waiting_threads.push_back(std::move(thread)); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| void WaitObject::RemoveWaitingThread(Thread* thread) { |  | ||||||
|     auto itr = std::find(waiting_threads.begin(), waiting_threads.end(), thread); |  | ||||||
|     // If a thread passed multiple handles to the same object,
 |  | ||||||
|     // the kernel might attempt to remove the thread from the object's
 |  | ||||||
|     // waiting threads list multiple times.
 |  | ||||||
|     if (itr != waiting_threads.end()) |  | ||||||
|         waiting_threads.erase(itr); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| SharedPtr<Thread> WaitObject::GetHighestPriorityReadyThread() { |  | ||||||
|     Thread* candidate = nullptr; |  | ||||||
|     s32 candidate_priority = THREADPRIO_LOWEST + 1; |  | ||||||
| 
 |  | ||||||
|     for (const auto& thread : waiting_threads) { |  | ||||||
|         // The list of waiting threads must not contain threads that are not waiting to be awakened.
 |  | ||||||
|         ASSERT_MSG(thread->status == THREADSTATUS_WAIT_SYNCH_ANY || |  | ||||||
|                        thread->status == THREADSTATUS_WAIT_SYNCH_ALL, |  | ||||||
|                    "Inconsistent thread statuses in waiting_threads"); |  | ||||||
| 
 |  | ||||||
|         if (thread->current_priority >= candidate_priority) |  | ||||||
|             continue; |  | ||||||
| 
 |  | ||||||
|         if (ShouldWait(thread.get())) |  | ||||||
|             continue; |  | ||||||
| 
 |  | ||||||
|         // A thread is ready to run if it's either in THREADSTATUS_WAIT_SYNCH_ANY or
 |  | ||||||
|         // in THREADSTATUS_WAIT_SYNCH_ALL and the rest of the objects it is waiting on are ready.
 |  | ||||||
|         bool ready_to_run = true; |  | ||||||
|         if (thread->status == THREADSTATUS_WAIT_SYNCH_ALL) { |  | ||||||
|             ready_to_run = std::none_of(thread->wait_objects.begin(), thread->wait_objects.end(), |  | ||||||
|                                         [&thread](const SharedPtr<WaitObject>& object) { |  | ||||||
|                                             return object->ShouldWait(thread.get()); |  | ||||||
|                                         }); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         if (ready_to_run) { |  | ||||||
|             candidate = thread.get(); |  | ||||||
|             candidate_priority = thread->current_priority; |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     return candidate; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| void WaitObject::WakeupAllWaitingThreads() { |  | ||||||
|     while (auto thread = GetHighestPriorityReadyThread()) { |  | ||||||
|         if (!thread->IsSleepingOnWaitAll()) { |  | ||||||
|             Acquire(thread.get()); |  | ||||||
|             // Set the output index of the WaitSynchronizationN call to the index of this object.
 |  | ||||||
|             if (thread->wait_set_output) { |  | ||||||
|                 thread->SetWaitSynchronizationOutput(thread->GetWaitObjectIndex(this)); |  | ||||||
|                 thread->wait_set_output = false; |  | ||||||
|             } |  | ||||||
|         } else { |  | ||||||
|             for (auto& object : thread->wait_objects) { |  | ||||||
|                 object->Acquire(thread.get()); |  | ||||||
|             } |  | ||||||
|             // Note: This case doesn't update the output index of WaitSynchronizationN.
 |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         for (auto& object : thread->wait_objects) |  | ||||||
|             object->RemoveWaitingThread(thread.get()); |  | ||||||
|         thread->wait_objects.clear(); |  | ||||||
| 
 |  | ||||||
|         thread->SetWaitSynchronizationResult(RESULT_SUCCESS); |  | ||||||
|         thread->ResumeFromWait(); |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| const std::vector<SharedPtr<Thread>>& WaitObject::GetWaitingThreads() const { |  | ||||||
|     return waiting_threads; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| HandleTable::HandleTable() { |  | ||||||
|     next_generation = 1; |  | ||||||
|     Clear(); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| ResultVal<Handle> HandleTable::Create(SharedPtr<Object> obj) { |  | ||||||
|     DEBUG_ASSERT(obj != nullptr); |  | ||||||
| 
 |  | ||||||
|     u16 slot = next_free_slot; |  | ||||||
|     if (slot >= generations.size()) { |  | ||||||
|         LOG_ERROR(Kernel, "Unable to allocate Handle, too many slots in use."); |  | ||||||
|         return ERR_OUT_OF_HANDLES; |  | ||||||
|     } |  | ||||||
|     next_free_slot = generations[slot]; |  | ||||||
| 
 |  | ||||||
|     u16 generation = next_generation++; |  | ||||||
| 
 |  | ||||||
|     // Overflow count so it fits in the 15 bits dedicated to the generation in the handle.
 |  | ||||||
|     // CTR-OS doesn't use generation 0, so skip straight to 1.
 |  | ||||||
|     if (next_generation >= (1 << 15)) |  | ||||||
|         next_generation = 1; |  | ||||||
| 
 |  | ||||||
|     generations[slot] = generation; |  | ||||||
|     objects[slot] = std::move(obj); |  | ||||||
| 
 |  | ||||||
|     Handle handle = generation | (slot << 15); |  | ||||||
|     return MakeResult<Handle>(handle); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| ResultVal<Handle> HandleTable::Duplicate(Handle handle) { |  | ||||||
|     SharedPtr<Object> object = GetGeneric(handle); |  | ||||||
|     if (object == nullptr) { |  | ||||||
|         LOG_ERROR(Kernel, "Tried to duplicate invalid handle: %08X", handle); |  | ||||||
|         return ERR_INVALID_HANDLE; |  | ||||||
|     } |  | ||||||
|     return Create(std::move(object)); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| ResultCode HandleTable::Close(Handle handle) { |  | ||||||
|     if (!IsValid(handle)) |  | ||||||
|         return ERR_INVALID_HANDLE; |  | ||||||
| 
 |  | ||||||
|     u16 slot = GetSlot(handle); |  | ||||||
| 
 |  | ||||||
|     objects[slot] = nullptr; |  | ||||||
| 
 |  | ||||||
|     generations[slot] = next_free_slot; |  | ||||||
|     next_free_slot = slot; |  | ||||||
|     return RESULT_SUCCESS; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| bool HandleTable::IsValid(Handle handle) const { |  | ||||||
|     size_t slot = GetSlot(handle); |  | ||||||
|     u16 generation = GetGeneration(handle); |  | ||||||
| 
 |  | ||||||
|     return slot < MAX_COUNT && objects[slot] != nullptr && generations[slot] == generation; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| SharedPtr<Object> HandleTable::GetGeneric(Handle handle) const { |  | ||||||
|     if (handle == CurrentThread) { |  | ||||||
|         return GetCurrentThread(); |  | ||||||
|     } else if (handle == CurrentProcess) { |  | ||||||
|         return g_current_process; |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     if (!IsValid(handle)) { |  | ||||||
|         return nullptr; |  | ||||||
|     } |  | ||||||
|     return objects[GetSlot(handle)]; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| void HandleTable::Clear() { |  | ||||||
|     for (u16 i = 0; i < MAX_COUNT; ++i) { |  | ||||||
|         generations[i] = i + 1; |  | ||||||
|         objects[i] = nullptr; |  | ||||||
|     } |  | ||||||
|     next_free_slot = 0; |  | ||||||
| } |  | ||||||
| 
 | 
 | ||||||
| /// Initialize the kernel
 | /// Initialize the kernel
 | ||||||
| void Init(u32 system_mode) { | void Init(u32 system_mode) { | ||||||
|  | |||||||
| @ -4,26 +4,16 @@ | |||||||
| 
 | 
 | ||||||
| #pragma once | #pragma once | ||||||
| 
 | 
 | ||||||
| #include <algorithm> |  | ||||||
| #include <array> |  | ||||||
| #include <cstddef> | #include <cstddef> | ||||||
| #include <string> | #include <string> | ||||||
| #include <vector> | #include <utility> | ||||||
| #include <boost/smart_ptr/intrusive_ptr.hpp> | #include <boost/smart_ptr/intrusive_ptr.hpp> | ||||||
| #include "common/common_types.h" | #include "common/common_types.h" | ||||||
| #include "core/hle/result.h" |  | ||||||
| 
 | 
 | ||||||
| namespace Kernel { | namespace Kernel { | ||||||
| 
 | 
 | ||||||
| using Handle = u32; | using Handle = u32; | ||||||
| 
 | 
 | ||||||
| class Thread; |  | ||||||
| 
 |  | ||||||
| enum KernelHandle : Handle { |  | ||||||
|     CurrentThread = 0xFFFF8000, |  | ||||||
|     CurrentProcess = 0xFFFF8001, |  | ||||||
| }; |  | ||||||
| 
 |  | ||||||
| enum class HandleType : u32 { | enum class HandleType : u32 { | ||||||
|     Unknown, |     Unknown, | ||||||
|     Event, |     Event, | ||||||
| @ -121,170 +111,17 @@ inline void intrusive_ptr_release(Object* object) { | |||||||
| template <typename T> | template <typename T> | ||||||
| using SharedPtr = boost::intrusive_ptr<T>; | using SharedPtr = boost::intrusive_ptr<T>; | ||||||
| 
 | 
 | ||||||
| /// Class that represents a Kernel object that a thread can be waiting on
 |  | ||||||
| class WaitObject : public Object { |  | ||||||
| public: |  | ||||||
|     /**
 |  | ||||||
|      * Check if the specified thread should wait until the object is available |  | ||||||
|      * @param thread The thread about which we're deciding. |  | ||||||
|      * @return True if the current thread should wait due to this object being unavailable |  | ||||||
|      */ |  | ||||||
|     virtual bool ShouldWait(Thread* thread) const = 0; |  | ||||||
| 
 |  | ||||||
|     /// Acquire/lock the object for the specified thread if it is available
 |  | ||||||
|     virtual void Acquire(Thread* thread) = 0; |  | ||||||
| 
 |  | ||||||
|     /**
 |  | ||||||
|      * Add a thread to wait on this object |  | ||||||
|      * @param thread Pointer to thread to add |  | ||||||
|      */ |  | ||||||
|     virtual void AddWaitingThread(SharedPtr<Thread> thread); |  | ||||||
| 
 |  | ||||||
|     /**
 |  | ||||||
|      * Removes a thread from waiting on this object (e.g. if it was resumed already) |  | ||||||
|      * @param thread Pointer to thread to remove |  | ||||||
|      */ |  | ||||||
|     virtual void RemoveWaitingThread(Thread* thread); |  | ||||||
| 
 |  | ||||||
|     /**
 |  | ||||||
|      * Wake up all threads waiting on this object that can be awoken, in priority order, |  | ||||||
|      * and set the synchronization result and output of the thread. |  | ||||||
|      */ |  | ||||||
|     virtual void WakeupAllWaitingThreads(); |  | ||||||
| 
 |  | ||||||
|     /// Obtains the highest priority thread that is ready to run from this object's waiting list.
 |  | ||||||
|     SharedPtr<Thread> GetHighestPriorityReadyThread(); |  | ||||||
| 
 |  | ||||||
|     /// Get a const reference to the waiting threads list for debug use
 |  | ||||||
|     const std::vector<SharedPtr<Thread>>& GetWaitingThreads() const; |  | ||||||
| 
 |  | ||||||
| private: |  | ||||||
|     /// Threads waiting for this object to become available
 |  | ||||||
|     std::vector<SharedPtr<Thread>> waiting_threads; |  | ||||||
| }; |  | ||||||
| 
 |  | ||||||
| /**
 | /**
 | ||||||
|  * This class allows the creation of Handles, which are references to objects that can be tested |  * Attempts to downcast the given Object pointer to a pointer to T. | ||||||
|  * for validity and looked up. Here they are used to pass references to kernel objects to/from the |  * @return Derived pointer to the object, or `nullptr` if `object` isn't of type T. | ||||||
|  * emulated process. it has been designed so that it follows the same handle format and has |  | ||||||
|  * approximately the same restrictions as the handle manager in the CTR-OS. |  | ||||||
|  * |  | ||||||
|  * Handles contain two sub-fields: a slot index (bits 31:15) and a generation value (bits 14:0). |  | ||||||
|  * The slot index is used to index into the arrays in this class to access the data corresponding |  | ||||||
|  * to the Handle. |  | ||||||
|  * |  | ||||||
|  * To prevent accidental use of a freed Handle whose slot has already been reused, a global counter |  | ||||||
|  * is kept and incremented every time a Handle is created. This is the Handle's "generation". The |  | ||||||
|  * value of the counter is stored into the Handle as well as in the handle table (in the |  | ||||||
|  * "generations" array). When looking up a handle, the Handle's generation must match with the |  | ||||||
|  * value stored on the class, otherwise the Handle is considered invalid. |  | ||||||
|  * |  | ||||||
|  * To find free slots when allocating a Handle without needing to scan the entire object array, the |  | ||||||
|  * generations field of unallocated slots is re-purposed as a linked list of indices to free slots. |  | ||||||
|  * When a Handle is created, an index is popped off the list and used for the new Handle. When it |  | ||||||
|  * is destroyed, it is again pushed onto the list to be re-used by the next allocation. It is |  | ||||||
|  * likely that this allocation strategy differs from the one used in CTR-OS, but this hasn't been |  | ||||||
|  * verified and isn't likely to cause any problems. |  | ||||||
|  */ |  */ | ||||||
| class HandleTable final : NonCopyable { | template <typename T> | ||||||
| public: | inline SharedPtr<T> DynamicObjectCast(SharedPtr<Object> object) { | ||||||
|     HandleTable(); |  | ||||||
| 
 |  | ||||||
|     /**
 |  | ||||||
|      * Allocates a handle for the given object. |  | ||||||
|      * @return The created Handle or one of the following errors: |  | ||||||
|      *           - `ERR_OUT_OF_HANDLES`: the maximum number of handles has been exceeded. |  | ||||||
|      */ |  | ||||||
|     ResultVal<Handle> Create(SharedPtr<Object> obj); |  | ||||||
| 
 |  | ||||||
|     /**
 |  | ||||||
|      * Returns a new handle that points to the same object as the passed in handle. |  | ||||||
|      * @return The duplicated Handle or one of the following errors: |  | ||||||
|      *           - `ERR_INVALID_HANDLE`: an invalid handle was passed in. |  | ||||||
|      *           - Any errors returned by `Create()`. |  | ||||||
|      */ |  | ||||||
|     ResultVal<Handle> Duplicate(Handle handle); |  | ||||||
| 
 |  | ||||||
|     /**
 |  | ||||||
|      * Closes a handle, removing it from the table and decreasing the object's ref-count. |  | ||||||
|      * @return `RESULT_SUCCESS` or one of the following errors: |  | ||||||
|      *           - `ERR_INVALID_HANDLE`: an invalid handle was passed in. |  | ||||||
|      */ |  | ||||||
|     ResultCode Close(Handle handle); |  | ||||||
| 
 |  | ||||||
|     /// Checks if a handle is valid and points to an existing object.
 |  | ||||||
|     bool IsValid(Handle handle) const; |  | ||||||
| 
 |  | ||||||
|     /**
 |  | ||||||
|      * Looks up a handle. |  | ||||||
|      * @return Pointer to the looked-up object, or `nullptr` if the handle is not valid. |  | ||||||
|      */ |  | ||||||
|     SharedPtr<Object> GetGeneric(Handle handle) const; |  | ||||||
| 
 |  | ||||||
|     /**
 |  | ||||||
|      * Looks up a handle while verifying its type. |  | ||||||
|      * @return Pointer to the looked-up object, or `nullptr` if the handle is not valid or its |  | ||||||
|      *         type differs from the handle type `T::HANDLE_TYPE`. |  | ||||||
|      */ |  | ||||||
|     template <class T> |  | ||||||
|     SharedPtr<T> Get(Handle handle) const { |  | ||||||
|         SharedPtr<Object> object = GetGeneric(handle); |  | ||||||
|     if (object != nullptr && object->GetHandleType() == T::HANDLE_TYPE) { |     if (object != nullptr && object->GetHandleType() == T::HANDLE_TYPE) { | ||||||
|         return boost::static_pointer_cast<T>(std::move(object)); |         return boost::static_pointer_cast<T>(std::move(object)); | ||||||
|     } |     } | ||||||
|     return nullptr; |     return nullptr; | ||||||
|     } | } | ||||||
| 
 |  | ||||||
|     /**
 |  | ||||||
|      * Looks up a handle while verifying that it is an object that a thread can wait on |  | ||||||
|      * @return Pointer to the looked-up object, or `nullptr` if the handle is not valid or it is |  | ||||||
|      *         not a waitable object. |  | ||||||
|      */ |  | ||||||
|     SharedPtr<WaitObject> GetWaitObject(Handle handle) const { |  | ||||||
|         SharedPtr<Object> object = GetGeneric(handle); |  | ||||||
|         if (object != nullptr && object->IsWaitable()) { |  | ||||||
|             return boost::static_pointer_cast<WaitObject>(std::move(object)); |  | ||||||
|         } |  | ||||||
|         return nullptr; |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     /// Closes all handles held in this table.
 |  | ||||||
|     void Clear(); |  | ||||||
| 
 |  | ||||||
| private: |  | ||||||
|     /**
 |  | ||||||
|      * This is the maximum limit of handles allowed per process in CTR-OS. It can be further |  | ||||||
|      * reduced by ExHeader values, but this is not emulated here. |  | ||||||
|      */ |  | ||||||
|     static const size_t MAX_COUNT = 4096; |  | ||||||
| 
 |  | ||||||
|     static u16 GetSlot(Handle handle) { |  | ||||||
|         return handle >> 15; |  | ||||||
|     } |  | ||||||
|     static u16 GetGeneration(Handle handle) { |  | ||||||
|         return handle & 0x7FFF; |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     /// Stores the Object referenced by the handle or null if the slot is empty.
 |  | ||||||
|     std::array<SharedPtr<Object>, MAX_COUNT> objects; |  | ||||||
| 
 |  | ||||||
|     /**
 |  | ||||||
|      * The value of `next_generation` when the handle was created, used to check for validity. For |  | ||||||
|      * empty slots, contains the index of the next free slot in the list. |  | ||||||
|      */ |  | ||||||
|     std::array<u16, MAX_COUNT> generations; |  | ||||||
| 
 |  | ||||||
|     /**
 |  | ||||||
|      * Global counter of the number of created handles. Stored in `generations` when a handle is |  | ||||||
|      * created, and wraps around to 1 when it hits 0x8000. |  | ||||||
|      */ |  | ||||||
|     u16 next_generation; |  | ||||||
| 
 |  | ||||||
|     /// Head of the free slots linked list.
 |  | ||||||
|     u16 next_free_slot; |  | ||||||
| }; |  | ||||||
| 
 |  | ||||||
| extern HandleTable g_handle_table; |  | ||||||
| 
 | 
 | ||||||
| /// Initialize the kernel with the specified system mode.
 | /// Initialize the kernel with the specified system mode.
 | ||||||
| void Init(u32 system_mode); | void Init(u32 system_mode); | ||||||
|  | |||||||
| @ -2,6 +2,7 @@ | |||||||
| // Licensed under GPLv2 or any later version
 | // Licensed under GPLv2 or any later version
 | ||||||
| // Refer to the license.txt file included.
 | // Refer to the license.txt file included.
 | ||||||
| 
 | 
 | ||||||
|  | #include <algorithm> | ||||||
| #include <cinttypes> | #include <cinttypes> | ||||||
| #include <map> | #include <map> | ||||||
| #include <memory> | #include <memory> | ||||||
|  | |||||||
| @ -7,6 +7,7 @@ | |||||||
| #include <string> | #include <string> | ||||||
| #include "common/common_types.h" | #include "common/common_types.h" | ||||||
| #include "core/hle/kernel/kernel.h" | #include "core/hle/kernel/kernel.h" | ||||||
|  | #include "core/hle/kernel/wait_object.h" | ||||||
| 
 | 
 | ||||||
| namespace Kernel { | namespace Kernel { | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -3,6 +3,7 @@ | |||||||
| // Refer to the license.txt file included.
 | // Refer to the license.txt file included.
 | ||||||
| 
 | 
 | ||||||
| #include <cstring> | #include <cstring> | ||||||
|  | #include "common/assert.h" | ||||||
| #include "common/logging/log.h" | #include "common/logging/log.h" | ||||||
| #include "core/hle/kernel/resource_limit.h" | #include "core/hle/kernel/resource_limit.h" | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -8,6 +8,8 @@ | |||||||
| #include <string> | #include <string> | ||||||
| #include "common/common_types.h" | #include "common/common_types.h" | ||||||
| #include "core/hle/kernel/kernel.h" | #include "core/hle/kernel/kernel.h" | ||||||
|  | #include "core/hle/kernel/wait_object.h" | ||||||
|  | #include "core/hle/result.h" | ||||||
| 
 | 
 | ||||||
| namespace Kernel { | namespace Kernel { | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -9,6 +9,7 @@ | |||||||
| #include <tuple> | #include <tuple> | ||||||
| #include "common/common_types.h" | #include "common/common_types.h" | ||||||
| #include "core/hle/kernel/kernel.h" | #include "core/hle/kernel/kernel.h" | ||||||
|  | #include "core/hle/kernel/wait_object.h" | ||||||
| 
 | 
 | ||||||
| namespace Service { | namespace Service { | ||||||
| class SessionRequestHandler; | class SessionRequestHandler; | ||||||
|  | |||||||
| @ -10,7 +10,7 @@ | |||||||
| #include "common/common_types.h" | #include "common/common_types.h" | ||||||
| #include "core/hle/kernel/kernel.h" | #include "core/hle/kernel/kernel.h" | ||||||
| #include "core/hle/kernel/session.h" | #include "core/hle/kernel/session.h" | ||||||
| #include "core/hle/kernel/thread.h" | #include "core/hle/kernel/wait_object.h" | ||||||
| #include "core/hle/result.h" | #include "core/hle/result.h" | ||||||
| #include "core/hle/service/service.h" | #include "core/hle/service/service.h" | ||||||
| #include "core/memory.h" | #include "core/memory.h" | ||||||
| @ -20,6 +20,7 @@ namespace Kernel { | |||||||
| class ClientSession; | class ClientSession; | ||||||
| class ClientPort; | class ClientPort; | ||||||
| class ServerSession; | class ServerSession; | ||||||
|  | class Thread; | ||||||
| 
 | 
 | ||||||
| /**
 | /**
 | ||||||
|  * Kernel object representing the server endpoint of an IPC session. Sessions are the basic CTR-OS |  * Kernel object representing the server endpoint of an IPC session. Sessions are the basic CTR-OS | ||||||
|  | |||||||
| @ -15,6 +15,7 @@ | |||||||
| #include "core/core.h" | #include "core/core.h" | ||||||
| #include "core/core_timing.h" | #include "core/core_timing.h" | ||||||
| #include "core/hle/kernel/errors.h" | #include "core/hle/kernel/errors.h" | ||||||
|  | #include "core/hle/kernel/handle_table.h" | ||||||
| #include "core/hle/kernel/kernel.h" | #include "core/hle/kernel/kernel.h" | ||||||
| #include "core/hle/kernel/memory.h" | #include "core/hle/kernel/memory.h" | ||||||
| #include "core/hle/kernel/mutex.h" | #include "core/hle/kernel/mutex.h" | ||||||
|  | |||||||
| @ -12,6 +12,7 @@ | |||||||
| #include "common/common_types.h" | #include "common/common_types.h" | ||||||
| #include "core/arm/arm_interface.h" | #include "core/arm/arm_interface.h" | ||||||
| #include "core/hle/kernel/kernel.h" | #include "core/hle/kernel/kernel.h" | ||||||
|  | #include "core/hle/kernel/wait_object.h" | ||||||
| #include "core/hle/result.h" | #include "core/hle/result.h" | ||||||
| 
 | 
 | ||||||
| enum ThreadPriority : s32 { | enum ThreadPriority : s32 { | ||||||
|  | |||||||
| @ -6,6 +6,7 @@ | |||||||
| #include "common/assert.h" | #include "common/assert.h" | ||||||
| #include "common/logging/log.h" | #include "common/logging/log.h" | ||||||
| #include "core/core_timing.h" | #include "core/core_timing.h" | ||||||
|  | #include "core/hle/kernel/handle_table.h" | ||||||
| #include "core/hle/kernel/kernel.h" | #include "core/hle/kernel/kernel.h" | ||||||
| #include "core/hle/kernel/thread.h" | #include "core/hle/kernel/thread.h" | ||||||
| #include "core/hle/kernel/timer.h" | #include "core/hle/kernel/timer.h" | ||||||
|  | |||||||
| @ -6,6 +6,7 @@ | |||||||
| 
 | 
 | ||||||
| #include "common/common_types.h" | #include "common/common_types.h" | ||||||
| #include "core/hle/kernel/kernel.h" | #include "core/hle/kernel/kernel.h" | ||||||
|  | #include "core/hle/kernel/wait_object.h" | ||||||
| 
 | 
 | ||||||
| namespace Kernel { | namespace Kernel { | ||||||
| 
 | 
 | ||||||
|  | |||||||
							
								
								
									
										99
									
								
								src/core/hle/kernel/wait_object.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										99
									
								
								src/core/hle/kernel/wait_object.cpp
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,99 @@ | |||||||
|  | // Copyright 2014 Citra Emulator Project
 | ||||||
|  | // Licensed under GPLv2 or any later version
 | ||||||
|  | // Refer to the license.txt file included.
 | ||||||
|  | 
 | ||||||
|  | #include <algorithm> | ||||||
|  | #include "common/assert.h" | ||||||
|  | #include "common/logging/log.h" | ||||||
|  | #include "core/hle/config_mem.h" | ||||||
|  | #include "core/hle/kernel/errors.h" | ||||||
|  | #include "core/hle/kernel/kernel.h" | ||||||
|  | #include "core/hle/kernel/memory.h" | ||||||
|  | #include "core/hle/kernel/process.h" | ||||||
|  | #include "core/hle/kernel/resource_limit.h" | ||||||
|  | #include "core/hle/kernel/thread.h" | ||||||
|  | #include "core/hle/kernel/timer.h" | ||||||
|  | #include "core/hle/shared_page.h" | ||||||
|  | 
 | ||||||
|  | namespace Kernel { | ||||||
|  | 
 | ||||||
|  | void WaitObject::AddWaitingThread(SharedPtr<Thread> thread) { | ||||||
|  |     auto itr = std::find(waiting_threads.begin(), waiting_threads.end(), thread); | ||||||
|  |     if (itr == waiting_threads.end()) | ||||||
|  |         waiting_threads.push_back(std::move(thread)); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void WaitObject::RemoveWaitingThread(Thread* thread) { | ||||||
|  |     auto itr = std::find(waiting_threads.begin(), waiting_threads.end(), thread); | ||||||
|  |     // If a thread passed multiple handles to the same object,
 | ||||||
|  |     // the kernel might attempt to remove the thread from the object's
 | ||||||
|  |     // waiting threads list multiple times.
 | ||||||
|  |     if (itr != waiting_threads.end()) | ||||||
|  |         waiting_threads.erase(itr); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | SharedPtr<Thread> WaitObject::GetHighestPriorityReadyThread() { | ||||||
|  |     Thread* candidate = nullptr; | ||||||
|  |     s32 candidate_priority = THREADPRIO_LOWEST + 1; | ||||||
|  | 
 | ||||||
|  |     for (const auto& thread : waiting_threads) { | ||||||
|  |         // The list of waiting threads must not contain threads that are not waiting to be awakened.
 | ||||||
|  |         ASSERT_MSG(thread->status == THREADSTATUS_WAIT_SYNCH_ANY || | ||||||
|  |                        thread->status == THREADSTATUS_WAIT_SYNCH_ALL, | ||||||
|  |                    "Inconsistent thread statuses in waiting_threads"); | ||||||
|  | 
 | ||||||
|  |         if (thread->current_priority >= candidate_priority) | ||||||
|  |             continue; | ||||||
|  | 
 | ||||||
|  |         if (ShouldWait(thread.get())) | ||||||
|  |             continue; | ||||||
|  | 
 | ||||||
|  |         // A thread is ready to run if it's either in THREADSTATUS_WAIT_SYNCH_ANY or
 | ||||||
|  |         // in THREADSTATUS_WAIT_SYNCH_ALL and the rest of the objects it is waiting on are ready.
 | ||||||
|  |         bool ready_to_run = true; | ||||||
|  |         if (thread->status == THREADSTATUS_WAIT_SYNCH_ALL) { | ||||||
|  |             ready_to_run = std::none_of(thread->wait_objects.begin(), thread->wait_objects.end(), | ||||||
|  |                                         [&thread](const SharedPtr<WaitObject>& object) { | ||||||
|  |                                             return object->ShouldWait(thread.get()); | ||||||
|  |                                         }); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         if (ready_to_run) { | ||||||
|  |             candidate = thread.get(); | ||||||
|  |             candidate_priority = thread->current_priority; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     return candidate; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void WaitObject::WakeupAllWaitingThreads() { | ||||||
|  |     while (auto thread = GetHighestPriorityReadyThread()) { | ||||||
|  |         if (!thread->IsSleepingOnWaitAll()) { | ||||||
|  |             Acquire(thread.get()); | ||||||
|  |             // Set the output index of the WaitSynchronizationN call to the index of this object.
 | ||||||
|  |             if (thread->wait_set_output) { | ||||||
|  |                 thread->SetWaitSynchronizationOutput(thread->GetWaitObjectIndex(this)); | ||||||
|  |                 thread->wait_set_output = false; | ||||||
|  |             } | ||||||
|  |         } else { | ||||||
|  |             for (auto& object : thread->wait_objects) { | ||||||
|  |                 object->Acquire(thread.get()); | ||||||
|  |             } | ||||||
|  |             // Note: This case doesn't update the output index of WaitSynchronizationN.
 | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         for (auto& object : thread->wait_objects) | ||||||
|  |             object->RemoveWaitingThread(thread.get()); | ||||||
|  |         thread->wait_objects.clear(); | ||||||
|  | 
 | ||||||
|  |         thread->SetWaitSynchronizationResult(RESULT_SUCCESS); | ||||||
|  |         thread->ResumeFromWait(); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | const std::vector<SharedPtr<Thread>>& WaitObject::GetWaitingThreads() const { | ||||||
|  |     return waiting_threads; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | } // namespace Kernel
 | ||||||
							
								
								
									
										67
									
								
								src/core/hle/kernel/wait_object.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										67
									
								
								src/core/hle/kernel/wait_object.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,67 @@ | |||||||
|  | // Copyright 2014 Citra Emulator Project
 | ||||||
|  | // Licensed under GPLv2 or any later version
 | ||||||
|  | // Refer to the license.txt file included.
 | ||||||
|  | 
 | ||||||
|  | #pragma once | ||||||
|  | 
 | ||||||
|  | #include <vector> | ||||||
|  | #include <boost/smart_ptr/intrusive_ptr.hpp> | ||||||
|  | #include "common/common_types.h" | ||||||
|  | #include "core/hle/kernel/kernel.h" | ||||||
|  | 
 | ||||||
|  | namespace Kernel { | ||||||
|  | 
 | ||||||
|  | class Thread; | ||||||
|  | 
 | ||||||
|  | /// Class that represents a Kernel object that a thread can be waiting on
 | ||||||
|  | class WaitObject : public Object { | ||||||
|  | public: | ||||||
|  |     /**
 | ||||||
|  |      * Check if the specified thread should wait until the object is available | ||||||
|  |      * @param thread The thread about which we're deciding. | ||||||
|  |      * @return True if the current thread should wait due to this object being unavailable | ||||||
|  |      */ | ||||||
|  |     virtual bool ShouldWait(Thread* thread) const = 0; | ||||||
|  | 
 | ||||||
|  |     /// Acquire/lock the object for the specified thread if it is available
 | ||||||
|  |     virtual void Acquire(Thread* thread) = 0; | ||||||
|  | 
 | ||||||
|  |     /**
 | ||||||
|  |      * Add a thread to wait on this object | ||||||
|  |      * @param thread Pointer to thread to add | ||||||
|  |      */ | ||||||
|  |     virtual void AddWaitingThread(SharedPtr<Thread> thread); | ||||||
|  | 
 | ||||||
|  |     /**
 | ||||||
|  |      * Removes a thread from waiting on this object (e.g. if it was resumed already) | ||||||
|  |      * @param thread Pointer to thread to remove | ||||||
|  |      */ | ||||||
|  |     virtual void RemoveWaitingThread(Thread* thread); | ||||||
|  | 
 | ||||||
|  |     /**
 | ||||||
|  |      * Wake up all threads waiting on this object that can be awoken, in priority order, | ||||||
|  |      * and set the synchronization result and output of the thread. | ||||||
|  |      */ | ||||||
|  |     virtual void WakeupAllWaitingThreads(); | ||||||
|  | 
 | ||||||
|  |     /// Obtains the highest priority thread that is ready to run from this object's waiting list.
 | ||||||
|  |     SharedPtr<Thread> GetHighestPriorityReadyThread(); | ||||||
|  | 
 | ||||||
|  |     /// Get a const reference to the waiting threads list for debug use
 | ||||||
|  |     const std::vector<SharedPtr<Thread>>& GetWaitingThreads() const; | ||||||
|  | 
 | ||||||
|  | private: | ||||||
|  |     /// Threads waiting for this object to become available
 | ||||||
|  |     std::vector<SharedPtr<Thread>> waiting_threads; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | // Specialization of DynamicObjectCast for WaitObjects
 | ||||||
|  | template <> | ||||||
|  | inline SharedPtr<WaitObject> DynamicObjectCast<WaitObject>(SharedPtr<Object> object) { | ||||||
|  |     if (object != nullptr && object->IsWaitable()) { | ||||||
|  |         return boost::static_pointer_cast<WaitObject>(std::move(object)); | ||||||
|  |     } | ||||||
|  |     return nullptr; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | } // namespace Kernel
 | ||||||
| @ -4,6 +4,8 @@ | |||||||
| 
 | 
 | ||||||
| #pragma once | #pragma once | ||||||
| 
 | 
 | ||||||
|  | #include <vector> | ||||||
|  | #include "common/common_funcs.h" | ||||||
| #include "common/common_types.h" | #include "common/common_types.h" | ||||||
| #include "common/swap.h" | #include "common/swap.h" | ||||||
| #include "core/hle/kernel/kernel.h" | #include "core/hle/kernel/kernel.h" | ||||||
|  | |||||||
| @ -12,7 +12,6 @@ | |||||||
| #include "core/hle/ipc.h" | #include "core/hle/ipc.h" | ||||||
| #include "core/hle/ipc_helpers.h" | #include "core/hle/ipc_helpers.h" | ||||||
| #include "core/hle/kernel/client_port.h" | #include "core/hle/kernel/client_port.h" | ||||||
| #include "core/hle/kernel/thread.h" |  | ||||||
| #include "core/hle/result.h" | #include "core/hle/result.h" | ||||||
| #include "core/memory.h" | #include "core/memory.h" | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -2,6 +2,7 @@ | |||||||
| // Licensed under GPLv2 or any later version
 | // Licensed under GPLv2 or any later version
 | ||||||
| // Refer to the license.txt file included.
 | // Refer to the license.txt file included.
 | ||||||
| 
 | 
 | ||||||
|  | #include <algorithm> | ||||||
| #include <cinttypes> | #include <cinttypes> | ||||||
| #include <map> | #include <map> | ||||||
| #include "common/logging/log.h" | #include "common/logging/log.h" | ||||||
| @ -16,6 +17,7 @@ | |||||||
| #include "core/hle/kernel/client_session.h" | #include "core/hle/kernel/client_session.h" | ||||||
| #include "core/hle/kernel/errors.h" | #include "core/hle/kernel/errors.h" | ||||||
| #include "core/hle/kernel/event.h" | #include "core/hle/kernel/event.h" | ||||||
|  | #include "core/hle/kernel/handle_table.h" | ||||||
| #include "core/hle/kernel/memory.h" | #include "core/hle/kernel/memory.h" | ||||||
| #include "core/hle/kernel/mutex.h" | #include "core/hle/kernel/mutex.h" | ||||||
| #include "core/hle/kernel/process.h" | #include "core/hle/kernel/process.h" | ||||||
| @ -27,6 +29,7 @@ | |||||||
| #include "core/hle/kernel/thread.h" | #include "core/hle/kernel/thread.h" | ||||||
| #include "core/hle/kernel/timer.h" | #include "core/hle/kernel/timer.h" | ||||||
| #include "core/hle/kernel/vm_manager.h" | #include "core/hle/kernel/vm_manager.h" | ||||||
|  | #include "core/hle/kernel/wait_object.h" | ||||||
| #include "core/hle/result.h" | #include "core/hle/result.h" | ||||||
| #include "core/hle/service/service.h" | #include "core/hle/service/service.h" | ||||||
| 
 | 
 | ||||||
| @ -244,7 +247,7 @@ static ResultCode CloseHandle(Kernel::Handle handle) { | |||||||
| 
 | 
 | ||||||
| /// Wait for a handle to synchronize, timeout after the specified nanoseconds
 | /// Wait for a handle to synchronize, timeout after the specified nanoseconds
 | ||||||
| static ResultCode WaitSynchronization1(Kernel::Handle handle, s64 nano_seconds) { | static ResultCode WaitSynchronization1(Kernel::Handle handle, s64 nano_seconds) { | ||||||
|     auto object = Kernel::g_handle_table.GetWaitObject(handle); |     auto object = Kernel::g_handle_table.Get<Kernel::WaitObject>(handle); | ||||||
|     Kernel::Thread* thread = Kernel::GetCurrentThread(); |     Kernel::Thread* thread = Kernel::GetCurrentThread(); | ||||||
| 
 | 
 | ||||||
|     if (object == nullptr) |     if (object == nullptr) | ||||||
| @ -299,7 +302,7 @@ static ResultCode WaitSynchronizationN(s32* out, Kernel::Handle* handles, s32 ha | |||||||
|     std::vector<ObjectPtr> objects(handle_count); |     std::vector<ObjectPtr> objects(handle_count); | ||||||
| 
 | 
 | ||||||
|     for (int i = 0; i < handle_count; ++i) { |     for (int i = 0; i < handle_count; ++i) { | ||||||
|         auto object = Kernel::g_handle_table.GetWaitObject(handles[i]); |         auto object = Kernel::g_handle_table.Get<Kernel::WaitObject>(handles[i]); | ||||||
|         if (object == nullptr) |         if (object == nullptr) | ||||||
|             return ERR_INVALID_HANDLE; |             return ERR_INVALID_HANDLE; | ||||||
|         objects[i] = object; |         objects[i] = object; | ||||||
|  | |||||||
		Loading…
	
		Reference in New Issue
	
	Block a user
	 bunnei
						bunnei