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/thread.h" | ||||
| #include "core/hle/kernel/timer.h" | ||||
| #include "core/hle/kernel/wait_object.h" | ||||
| 
 | ||||
| WaitTreeItem::~WaitTreeItem() {} | ||||
| 
 | ||||
|  | ||||
| @ -4,12 +4,10 @@ | ||||
| 
 | ||||
| #pragma once | ||||
| 
 | ||||
| #include <boost/container/flat_set.hpp> | ||||
| 
 | ||||
| #include <QAbstractItemModel> | ||||
| #include <QDockWidget> | ||||
| #include <QTreeView> | ||||
| 
 | ||||
| #include <boost/container/flat_set.hpp> | ||||
| #include "core/core.h" | ||||
| #include "core/hle/kernel/kernel.h" | ||||
| 
 | ||||
|  | ||||
| @ -45,6 +45,7 @@ set(SRCS | ||||
|             hle/kernel/client_port.cpp | ||||
|             hle/kernel/client_session.cpp | ||||
|             hle/kernel/event.cpp | ||||
|             hle/kernel/handle_table.cpp | ||||
|             hle/kernel/kernel.cpp | ||||
|             hle/kernel/memory.cpp | ||||
|             hle/kernel/mutex.cpp | ||||
| @ -57,6 +58,7 @@ set(SRCS | ||||
|             hle/kernel/thread.cpp | ||||
|             hle/kernel/timer.cpp | ||||
|             hle/kernel/vm_manager.cpp | ||||
|             hle/kernel/wait_object.cpp | ||||
|             hle/service/ac/ac.cpp | ||||
|             hle/service/ac/ac_i.cpp | ||||
|             hle/service/ac/ac_u.cpp | ||||
| @ -236,6 +238,7 @@ set(HEADERS | ||||
|             hle/kernel/client_session.h | ||||
|             hle/kernel/errors.h | ||||
|             hle/kernel/event.h | ||||
|             hle/kernel/handle_table.h | ||||
|             hle/kernel/kernel.h | ||||
|             hle/kernel/memory.h | ||||
|             hle/kernel/mutex.h | ||||
| @ -249,6 +252,7 @@ set(HEADERS | ||||
|             hle/kernel/thread.h | ||||
|             hle/kernel/timer.h | ||||
|             hle/kernel/vm_manager.h | ||||
|             hle/kernel/wait_object.h | ||||
|             hle/result.h | ||||
|             hle/service/ac/ac.h | ||||
|             hle/service/ac/ac_i.h | ||||
|  | ||||
| @ -3,7 +3,9 @@ | ||||
| // Refer to the license.txt file included.
 | ||||
| 
 | ||||
| #pragma once | ||||
| 
 | ||||
| #include "core/hle/ipc.h" | ||||
| #include "core/hle/kernel/handle_table.h" | ||||
| #include "core/hle/kernel/kernel.h" | ||||
| 
 | ||||
| namespace IPC { | ||||
|  | ||||
| @ -6,6 +6,7 @@ | ||||
| 
 | ||||
| #include "common/common_types.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
 | ||||
| // supervisor calls (SVCs). They function as sort of a global lock. Typically, games/other CTR
 | ||||
|  | ||||
| @ -7,6 +7,7 @@ | ||||
| #include <string> | ||||
| #include "common/common_types.h" | ||||
| #include "core/hle/kernel/kernel.h" | ||||
| #include "core/hle/result.h" | ||||
| 
 | ||||
| namespace Kernel { | ||||
| 
 | ||||
|  | ||||
| @ -6,10 +6,9 @@ | ||||
| 
 | ||||
| #include <memory> | ||||
| #include <string> | ||||
| 
 | ||||
| #include "common/common_types.h" | ||||
| 
 | ||||
| #include "core/hle/kernel/kernel.h" | ||||
| #include "core/hle/result.h" | ||||
| 
 | ||||
| namespace Kernel { | ||||
| 
 | ||||
|  | ||||
| @ -6,6 +6,7 @@ | ||||
| 
 | ||||
| #include "common/common_types.h" | ||||
| #include "core/hle/kernel/kernel.h" | ||||
| #include "core/hle/kernel/wait_object.h" | ||||
| 
 | ||||
| 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
 | ||||
| // 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/handle_table.h" | ||||
| #include "core/hle/kernel/kernel.h" | ||||
| #include "core/hle/kernel/memory.h" | ||||
| #include "core/hle/kernel/process.h" | ||||
| @ -18,165 +15,6 @@ | ||||
| namespace Kernel { | ||||
| 
 | ||||
| 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
 | ||||
| void Init(u32 system_mode) { | ||||
|  | ||||
| @ -4,26 +4,16 @@ | ||||
| 
 | ||||
| #pragma once | ||||
| 
 | ||||
| #include <algorithm> | ||||
| #include <array> | ||||
| #include <cstddef> | ||||
| #include <string> | ||||
| #include <vector> | ||||
| #include <utility> | ||||
| #include <boost/smart_ptr/intrusive_ptr.hpp> | ||||
| #include "common/common_types.h" | ||||
| #include "core/hle/result.h" | ||||
| 
 | ||||
| namespace Kernel { | ||||
| 
 | ||||
| using Handle = u32; | ||||
| 
 | ||||
| class Thread; | ||||
| 
 | ||||
| enum KernelHandle : Handle { | ||||
|     CurrentThread = 0xFFFF8000, | ||||
|     CurrentProcess = 0xFFFF8001, | ||||
| }; | ||||
| 
 | ||||
| enum class HandleType : u32 { | ||||
|     Unknown, | ||||
|     Event, | ||||
| @ -121,170 +111,17 @@ inline void intrusive_ptr_release(Object* object) { | ||||
| template <typename 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 | ||||
|  * 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. | ||||
|  * Attempts to downcast the given Object pointer to a pointer to T. | ||||
|  * @return Derived pointer to the object, or `nullptr` if `object` isn't of type T. | ||||
|  */ | ||||
| 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 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) { | ||||
|             return boost::static_pointer_cast<T>(std::move(object)); | ||||
|         } | ||||
|         return nullptr; | ||||
| template <typename T> | ||||
| inline SharedPtr<T> DynamicObjectCast(SharedPtr<Object> object) { | ||||
|     if (object != nullptr && object->GetHandleType() == T::HANDLE_TYPE) { | ||||
|         return boost::static_pointer_cast<T>(std::move(object)); | ||||
|     } | ||||
| 
 | ||||
|     /**
 | ||||
|      * 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; | ||||
|     return nullptr; | ||||
| } | ||||
| 
 | ||||
| /// Initialize the kernel with the specified system mode.
 | ||||
| void Init(u32 system_mode); | ||||
|  | ||||
| @ -2,6 +2,7 @@ | ||||
| // Licensed under GPLv2 or any later version
 | ||||
| // Refer to the license.txt file included.
 | ||||
| 
 | ||||
| #include <algorithm> | ||||
| #include <cinttypes> | ||||
| #include <map> | ||||
| #include <memory> | ||||
|  | ||||
| @ -7,6 +7,7 @@ | ||||
| #include <string> | ||||
| #include "common/common_types.h" | ||||
| #include "core/hle/kernel/kernel.h" | ||||
| #include "core/hle/kernel/wait_object.h" | ||||
| 
 | ||||
| namespace Kernel { | ||||
| 
 | ||||
|  | ||||
| @ -3,6 +3,7 @@ | ||||
| // Refer to the license.txt file included.
 | ||||
| 
 | ||||
| #include <cstring> | ||||
| #include "common/assert.h" | ||||
| #include "common/logging/log.h" | ||||
| #include "core/hle/kernel/resource_limit.h" | ||||
| 
 | ||||
|  | ||||
| @ -8,6 +8,8 @@ | ||||
| #include <string> | ||||
| #include "common/common_types.h" | ||||
| #include "core/hle/kernel/kernel.h" | ||||
| #include "core/hle/kernel/wait_object.h" | ||||
| #include "core/hle/result.h" | ||||
| 
 | ||||
| namespace Kernel { | ||||
| 
 | ||||
|  | ||||
| @ -9,6 +9,7 @@ | ||||
| #include <tuple> | ||||
| #include "common/common_types.h" | ||||
| #include "core/hle/kernel/kernel.h" | ||||
| #include "core/hle/kernel/wait_object.h" | ||||
| 
 | ||||
| namespace Service { | ||||
| class SessionRequestHandler; | ||||
|  | ||||
| @ -10,7 +10,7 @@ | ||||
| #include "common/common_types.h" | ||||
| #include "core/hle/kernel/kernel.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/service/service.h" | ||||
| #include "core/memory.h" | ||||
| @ -20,6 +20,7 @@ namespace Kernel { | ||||
| class ClientSession; | ||||
| class ClientPort; | ||||
| class ServerSession; | ||||
| class Thread; | ||||
| 
 | ||||
| /**
 | ||||
|  * 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_timing.h" | ||||
| #include "core/hle/kernel/errors.h" | ||||
| #include "core/hle/kernel/handle_table.h" | ||||
| #include "core/hle/kernel/kernel.h" | ||||
| #include "core/hle/kernel/memory.h" | ||||
| #include "core/hle/kernel/mutex.h" | ||||
|  | ||||
| @ -12,6 +12,7 @@ | ||||
| #include "common/common_types.h" | ||||
| #include "core/arm/arm_interface.h" | ||||
| #include "core/hle/kernel/kernel.h" | ||||
| #include "core/hle/kernel/wait_object.h" | ||||
| #include "core/hle/result.h" | ||||
| 
 | ||||
| enum ThreadPriority : s32 { | ||||
|  | ||||
| @ -6,6 +6,7 @@ | ||||
| #include "common/assert.h" | ||||
| #include "common/logging/log.h" | ||||
| #include "core/core_timing.h" | ||||
| #include "core/hle/kernel/handle_table.h" | ||||
| #include "core/hle/kernel/kernel.h" | ||||
| #include "core/hle/kernel/thread.h" | ||||
| #include "core/hle/kernel/timer.h" | ||||
|  | ||||
| @ -6,6 +6,7 @@ | ||||
| 
 | ||||
| #include "common/common_types.h" | ||||
| #include "core/hle/kernel/kernel.h" | ||||
| #include "core/hle/kernel/wait_object.h" | ||||
| 
 | ||||
| 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 | ||||
| 
 | ||||
| #include <vector> | ||||
| #include "common/common_funcs.h" | ||||
| #include "common/common_types.h" | ||||
| #include "common/swap.h" | ||||
| #include "core/hle/kernel/kernel.h" | ||||
|  | ||||
| @ -12,7 +12,6 @@ | ||||
| #include "core/hle/ipc.h" | ||||
| #include "core/hle/ipc_helpers.h" | ||||
| #include "core/hle/kernel/client_port.h" | ||||
| #include "core/hle/kernel/thread.h" | ||||
| #include "core/hle/result.h" | ||||
| #include "core/memory.h" | ||||
| 
 | ||||
|  | ||||
| @ -2,6 +2,7 @@ | ||||
| // Licensed under GPLv2 or any later version
 | ||||
| // Refer to the license.txt file included.
 | ||||
| 
 | ||||
| #include <algorithm> | ||||
| #include <cinttypes> | ||||
| #include <map> | ||||
| #include "common/logging/log.h" | ||||
| @ -16,6 +17,7 @@ | ||||
| #include "core/hle/kernel/client_session.h" | ||||
| #include "core/hle/kernel/errors.h" | ||||
| #include "core/hle/kernel/event.h" | ||||
| #include "core/hle/kernel/handle_table.h" | ||||
| #include "core/hle/kernel/memory.h" | ||||
| #include "core/hle/kernel/mutex.h" | ||||
| #include "core/hle/kernel/process.h" | ||||
| @ -27,6 +29,7 @@ | ||||
| #include "core/hle/kernel/thread.h" | ||||
| #include "core/hle/kernel/timer.h" | ||||
| #include "core/hle/kernel/vm_manager.h" | ||||
| #include "core/hle/kernel/wait_object.h" | ||||
| #include "core/hle/result.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
 | ||||
| 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(); | ||||
| 
 | ||||
|     if (object == nullptr) | ||||
| @ -299,7 +302,7 @@ static ResultCode WaitSynchronizationN(s32* out, Kernel::Handle* handles, s32 ha | ||||
|     std::vector<ObjectPtr> objects(handle_count); | ||||
| 
 | ||||
|     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) | ||||
|             return ERR_INVALID_HANDLE; | ||||
|         objects[i] = object; | ||||
|  | ||||
		Loading…
	
		Reference in New Issue
	
	Block a user
	 bunnei
						bunnei