diff options
Diffstat (limited to 'src')
27 files changed, 430 insertions, 344 deletions
diff --git a/src/citra_qt/debugger/wait_tree.cpp b/src/citra_qt/debugger/wait_tree.cpp index b6ecf3819..8c244b6b2 100644 --- a/src/citra_qt/debugger/wait_tree.cpp +++ b/src/citra_qt/debugger/wait_tree.cpp | |||
| @@ -10,6 +10,7 @@ | |||
| 10 | #include "core/hle/kernel/semaphore.h" | 10 | #include "core/hle/kernel/semaphore.h" |
| 11 | #include "core/hle/kernel/thread.h" | 11 | #include "core/hle/kernel/thread.h" |
| 12 | #include "core/hle/kernel/timer.h" | 12 | #include "core/hle/kernel/timer.h" |
| 13 | #include "core/hle/kernel/wait_object.h" | ||
| 13 | 14 | ||
| 14 | WaitTreeItem::~WaitTreeItem() {} | 15 | WaitTreeItem::~WaitTreeItem() {} |
| 15 | 16 | ||
diff --git a/src/citra_qt/debugger/wait_tree.h b/src/citra_qt/debugger/wait_tree.h index ee9708fc1..06ef58ea7 100644 --- a/src/citra_qt/debugger/wait_tree.h +++ b/src/citra_qt/debugger/wait_tree.h | |||
| @@ -4,12 +4,10 @@ | |||
| 4 | 4 | ||
| 5 | #pragma once | 5 | #pragma once |
| 6 | 6 | ||
| 7 | #include <boost/container/flat_set.hpp> | ||
| 8 | |||
| 9 | #include <QAbstractItemModel> | 7 | #include <QAbstractItemModel> |
| 10 | #include <QDockWidget> | 8 | #include <QDockWidget> |
| 11 | #include <QTreeView> | 9 | #include <QTreeView> |
| 12 | 10 | #include <boost/container/flat_set.hpp> | |
| 13 | #include "core/core.h" | 11 | #include "core/core.h" |
| 14 | #include "core/hle/kernel/kernel.h" | 12 | #include "core/hle/kernel/kernel.h" |
| 15 | 13 | ||
diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index 3cdb2b817..d66139c9c 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt | |||
| @@ -45,6 +45,7 @@ set(SRCS | |||
| 45 | hle/kernel/client_port.cpp | 45 | hle/kernel/client_port.cpp |
| 46 | hle/kernel/client_session.cpp | 46 | hle/kernel/client_session.cpp |
| 47 | hle/kernel/event.cpp | 47 | hle/kernel/event.cpp |
| 48 | hle/kernel/handle_table.cpp | ||
| 48 | hle/kernel/kernel.cpp | 49 | hle/kernel/kernel.cpp |
| 49 | hle/kernel/memory.cpp | 50 | hle/kernel/memory.cpp |
| 50 | hle/kernel/mutex.cpp | 51 | hle/kernel/mutex.cpp |
| @@ -57,6 +58,7 @@ set(SRCS | |||
| 57 | hle/kernel/thread.cpp | 58 | hle/kernel/thread.cpp |
| 58 | hle/kernel/timer.cpp | 59 | hle/kernel/timer.cpp |
| 59 | hle/kernel/vm_manager.cpp | 60 | hle/kernel/vm_manager.cpp |
| 61 | hle/kernel/wait_object.cpp | ||
| 60 | hle/service/ac/ac.cpp | 62 | hle/service/ac/ac.cpp |
| 61 | hle/service/ac/ac_i.cpp | 63 | hle/service/ac/ac_i.cpp |
| 62 | hle/service/ac/ac_u.cpp | 64 | hle/service/ac/ac_u.cpp |
| @@ -236,6 +238,7 @@ set(HEADERS | |||
| 236 | hle/kernel/client_session.h | 238 | hle/kernel/client_session.h |
| 237 | hle/kernel/errors.h | 239 | hle/kernel/errors.h |
| 238 | hle/kernel/event.h | 240 | hle/kernel/event.h |
| 241 | hle/kernel/handle_table.h | ||
| 239 | hle/kernel/kernel.h | 242 | hle/kernel/kernel.h |
| 240 | hle/kernel/memory.h | 243 | hle/kernel/memory.h |
| 241 | hle/kernel/mutex.h | 244 | hle/kernel/mutex.h |
| @@ -249,6 +252,7 @@ set(HEADERS | |||
| 249 | hle/kernel/thread.h | 252 | hle/kernel/thread.h |
| 250 | hle/kernel/timer.h | 253 | hle/kernel/timer.h |
| 251 | hle/kernel/vm_manager.h | 254 | hle/kernel/vm_manager.h |
| 255 | hle/kernel/wait_object.h | ||
| 252 | hle/result.h | 256 | hle/result.h |
| 253 | hle/service/ac/ac.h | 257 | hle/service/ac/ac.h |
| 254 | hle/service/ac/ac_i.h | 258 | hle/service/ac/ac_i.h |
diff --git a/src/core/hle/ipc_helpers.h b/src/core/hle/ipc_helpers.h index 06c4c5a85..d7348c09d 100644 --- a/src/core/hle/ipc_helpers.h +++ b/src/core/hle/ipc_helpers.h | |||
| @@ -3,7 +3,9 @@ | |||
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| 5 | #pragma once | 5 | #pragma once |
| 6 | |||
| 6 | #include "core/hle/ipc.h" | 7 | #include "core/hle/ipc.h" |
| 8 | #include "core/hle/kernel/handle_table.h" | ||
| 7 | #include "core/hle/kernel/kernel.h" | 9 | #include "core/hle/kernel/kernel.h" |
| 8 | 10 | ||
| 9 | namespace IPC { | 11 | namespace IPC { |
diff --git a/src/core/hle/kernel/address_arbiter.h b/src/core/hle/kernel/address_arbiter.h index 6a7af93a9..1d24401b1 100644 --- a/src/core/hle/kernel/address_arbiter.h +++ b/src/core/hle/kernel/address_arbiter.h | |||
| @@ -6,6 +6,7 @@ | |||
| 6 | 6 | ||
| 7 | #include "common/common_types.h" | 7 | #include "common/common_types.h" |
| 8 | #include "core/hle/kernel/kernel.h" | 8 | #include "core/hle/kernel/kernel.h" |
| 9 | #include "core/hle/result.h" | ||
| 9 | 10 | ||
| 10 | // Address arbiters are an underlying kernel synchronization object that can be created/used via | 11 | // Address arbiters are an underlying kernel synchronization object that can be created/used via |
| 11 | // supervisor calls (SVCs). They function as sort of a global lock. Typically, games/other CTR | 12 | // supervisor calls (SVCs). They function as sort of a global lock. Typically, games/other CTR |
diff --git a/src/core/hle/kernel/client_port.h b/src/core/hle/kernel/client_port.h index 511490c7c..8f7d6ac44 100644 --- a/src/core/hle/kernel/client_port.h +++ b/src/core/hle/kernel/client_port.h | |||
| @@ -7,6 +7,7 @@ | |||
| 7 | #include <string> | 7 | #include <string> |
| 8 | #include "common/common_types.h" | 8 | #include "common/common_types.h" |
| 9 | #include "core/hle/kernel/kernel.h" | 9 | #include "core/hle/kernel/kernel.h" |
| 10 | #include "core/hle/result.h" | ||
| 10 | 11 | ||
| 11 | namespace Kernel { | 12 | namespace Kernel { |
| 12 | 13 | ||
diff --git a/src/core/hle/kernel/client_session.h b/src/core/hle/kernel/client_session.h index 9f3adb72b..2de379c09 100644 --- a/src/core/hle/kernel/client_session.h +++ b/src/core/hle/kernel/client_session.h | |||
| @@ -6,10 +6,9 @@ | |||
| 6 | 6 | ||
| 7 | #include <memory> | 7 | #include <memory> |
| 8 | #include <string> | 8 | #include <string> |
| 9 | |||
| 10 | #include "common/common_types.h" | 9 | #include "common/common_types.h" |
| 11 | |||
| 12 | #include "core/hle/kernel/kernel.h" | 10 | #include "core/hle/kernel/kernel.h" |
| 11 | #include "core/hle/result.h" | ||
| 13 | 12 | ||
| 14 | namespace Kernel { | 13 | namespace Kernel { |
| 15 | 14 | ||
diff --git a/src/core/hle/kernel/event.h b/src/core/hle/kernel/event.h index 3e3673508..cc41abb85 100644 --- a/src/core/hle/kernel/event.h +++ b/src/core/hle/kernel/event.h | |||
| @@ -6,6 +6,7 @@ | |||
| 6 | 6 | ||
| 7 | #include "common/common_types.h" | 7 | #include "common/common_types.h" |
| 8 | #include "core/hle/kernel/kernel.h" | 8 | #include "core/hle/kernel/kernel.h" |
| 9 | #include "core/hle/kernel/wait_object.h" | ||
| 9 | 10 | ||
| 10 | namespace Kernel { | 11 | namespace Kernel { |
| 11 | 12 | ||
diff --git a/src/core/hle/kernel/handle_table.cpp b/src/core/hle/kernel/handle_table.cpp new file mode 100644 index 000000000..c7322d883 --- /dev/null +++ b/src/core/hle/kernel/handle_table.cpp | |||
| @@ -0,0 +1,97 @@ | |||
| 1 | // Copyright 2014 Citra Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #include <utility> | ||
| 6 | #include "common/assert.h" | ||
| 7 | #include "common/logging/log.h" | ||
| 8 | #include "core/hle/kernel/errors.h" | ||
| 9 | #include "core/hle/kernel/handle_table.h" | ||
| 10 | #include "core/hle/kernel/kernel.h" | ||
| 11 | #include "core/hle/kernel/process.h" | ||
| 12 | #include "core/hle/kernel/thread.h" | ||
| 13 | |||
| 14 | namespace Kernel { | ||
| 15 | |||
| 16 | HandleTable g_handle_table; | ||
| 17 | |||
| 18 | HandleTable::HandleTable() { | ||
| 19 | next_generation = 1; | ||
| 20 | Clear(); | ||
| 21 | } | ||
| 22 | |||
| 23 | ResultVal<Handle> HandleTable::Create(SharedPtr<Object> obj) { | ||
| 24 | DEBUG_ASSERT(obj != nullptr); | ||
| 25 | |||
| 26 | u16 slot = next_free_slot; | ||
| 27 | if (slot >= generations.size()) { | ||
| 28 | LOG_ERROR(Kernel, "Unable to allocate Handle, too many slots in use."); | ||
| 29 | return ERR_OUT_OF_HANDLES; | ||
| 30 | } | ||
| 31 | next_free_slot = generations[slot]; | ||
| 32 | |||
| 33 | u16 generation = next_generation++; | ||
| 34 | |||
| 35 | // Overflow count so it fits in the 15 bits dedicated to the generation in the handle. | ||
| 36 | // CTR-OS doesn't use generation 0, so skip straight to 1. | ||
| 37 | if (next_generation >= (1 << 15)) | ||
| 38 | next_generation = 1; | ||
| 39 | |||
| 40 | generations[slot] = generation; | ||
| 41 | objects[slot] = std::move(obj); | ||
| 42 | |||
| 43 | Handle handle = generation | (slot << 15); | ||
| 44 | return MakeResult<Handle>(handle); | ||
| 45 | } | ||
| 46 | |||
| 47 | ResultVal<Handle> HandleTable::Duplicate(Handle handle) { | ||
| 48 | SharedPtr<Object> object = GetGeneric(handle); | ||
| 49 | if (object == nullptr) { | ||
| 50 | LOG_ERROR(Kernel, "Tried to duplicate invalid handle: %08X", handle); | ||
| 51 | return ERR_INVALID_HANDLE; | ||
| 52 | } | ||
| 53 | return Create(std::move(object)); | ||
| 54 | } | ||
| 55 | |||
| 56 | ResultCode HandleTable::Close(Handle handle) { | ||
| 57 | if (!IsValid(handle)) | ||
| 58 | return ERR_INVALID_HANDLE; | ||
| 59 | |||
| 60 | u16 slot = GetSlot(handle); | ||
| 61 | |||
| 62 | objects[slot] = nullptr; | ||
| 63 | |||
| 64 | generations[slot] = next_free_slot; | ||
| 65 | next_free_slot = slot; | ||
| 66 | return RESULT_SUCCESS; | ||
| 67 | } | ||
| 68 | |||
| 69 | bool HandleTable::IsValid(Handle handle) const { | ||
| 70 | size_t slot = GetSlot(handle); | ||
| 71 | u16 generation = GetGeneration(handle); | ||
| 72 | |||
| 73 | return slot < MAX_COUNT && objects[slot] != nullptr && generations[slot] == generation; | ||
| 74 | } | ||
| 75 | |||
| 76 | SharedPtr<Object> HandleTable::GetGeneric(Handle handle) const { | ||
| 77 | if (handle == CurrentThread) { | ||
| 78 | return GetCurrentThread(); | ||
| 79 | } else if (handle == CurrentProcess) { | ||
| 80 | return g_current_process; | ||
| 81 | } | ||
| 82 | |||
| 83 | if (!IsValid(handle)) { | ||
| 84 | return nullptr; | ||
| 85 | } | ||
| 86 | return objects[GetSlot(handle)]; | ||
| 87 | } | ||
| 88 | |||
| 89 | void HandleTable::Clear() { | ||
| 90 | for (u16 i = 0; i < MAX_COUNT; ++i) { | ||
| 91 | generations[i] = i + 1; | ||
| 92 | objects[i] = nullptr; | ||
| 93 | } | ||
| 94 | next_free_slot = 0; | ||
| 95 | } | ||
| 96 | |||
| 97 | } // namespace | ||
diff --git a/src/core/hle/kernel/handle_table.h b/src/core/hle/kernel/handle_table.h new file mode 100644 index 000000000..d6aaefbf7 --- /dev/null +++ b/src/core/hle/kernel/handle_table.h | |||
| @@ -0,0 +1,126 @@ | |||
| 1 | // Copyright 2014 Citra Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #pragma once | ||
| 6 | |||
| 7 | #include <array> | ||
| 8 | #include <cstddef> | ||
| 9 | #include "common/common_types.h" | ||
| 10 | #include "core/hle/kernel/kernel.h" | ||
| 11 | #include "core/hle/result.h" | ||
| 12 | |||
| 13 | namespace Kernel { | ||
| 14 | |||
| 15 | enum KernelHandle : Handle { | ||
| 16 | CurrentThread = 0xFFFF8000, | ||
| 17 | CurrentProcess = 0xFFFF8001, | ||
| 18 | }; | ||
| 19 | |||
| 20 | /** | ||
| 21 | * This class allows the creation of Handles, which are references to objects that can be tested | ||
| 22 | * for validity and looked up. Here they are used to pass references to kernel objects to/from the | ||
| 23 | * emulated process. it has been designed so that it follows the same handle format and has | ||
| 24 | * approximately the same restrictions as the handle manager in the CTR-OS. | ||
| 25 | * | ||
| 26 | * Handles contain two sub-fields: a slot index (bits 31:15) and a generation value (bits 14:0). | ||
| 27 | * The slot index is used to index into the arrays in this class to access the data corresponding | ||
| 28 | * to the Handle. | ||
| 29 | * | ||
| 30 | * To prevent accidental use of a freed Handle whose slot has already been reused, a global counter | ||
| 31 | * is kept and incremented every time a Handle is created. This is the Handle's "generation". The | ||
| 32 | * value of the counter is stored into the Handle as well as in the handle table (in the | ||
| 33 | * "generations" array). When looking up a handle, the Handle's generation must match with the | ||
| 34 | * value stored on the class, otherwise the Handle is considered invalid. | ||
| 35 | * | ||
| 36 | * To find free slots when allocating a Handle without needing to scan the entire object array, the | ||
| 37 | * generations field of unallocated slots is re-purposed as a linked list of indices to free slots. | ||
| 38 | * When a Handle is created, an index is popped off the list and used for the new Handle. When it | ||
| 39 | * is destroyed, it is again pushed onto the list to be re-used by the next allocation. It is | ||
| 40 | * likely that this allocation strategy differs from the one used in CTR-OS, but this hasn't been | ||
| 41 | * verified and isn't likely to cause any problems. | ||
| 42 | */ | ||
| 43 | class HandleTable final : NonCopyable { | ||
| 44 | public: | ||
| 45 | HandleTable(); | ||
| 46 | |||
| 47 | /** | ||
| 48 | * Allocates a handle for the given object. | ||
| 49 | * @return The created Handle or one of the following errors: | ||
| 50 | * - `ERR_OUT_OF_HANDLES`: the maximum number of handles has been exceeded. | ||
| 51 | */ | ||
| 52 | ResultVal<Handle> Create(SharedPtr<Object> obj); | ||
| 53 | |||
| 54 | /** | ||
| 55 | * Returns a new handle that points to the same object as the passed in handle. | ||
| 56 | * @return The duplicated Handle or one of the following errors: | ||
| 57 | * - `ERR_INVALID_HANDLE`: an invalid handle was passed in. | ||
| 58 | * - Any errors returned by `Create()`. | ||
| 59 | */ | ||
| 60 | ResultVal<Handle> Duplicate(Handle handle); | ||
| 61 | |||
| 62 | /** | ||
| 63 | * Closes a handle, removing it from the table and decreasing the object's ref-count. | ||
| 64 | * @return `RESULT_SUCCESS` or one of the following errors: | ||
| 65 | * - `ERR_INVALID_HANDLE`: an invalid handle was passed in. | ||
| 66 | */ | ||
| 67 | ResultCode Close(Handle handle); | ||
| 68 | |||
| 69 | /// Checks if a handle is valid and points to an existing object. | ||
| 70 | bool IsValid(Handle handle) const; | ||
| 71 | |||
| 72 | /** | ||
| 73 | * Looks up a handle. | ||
| 74 | * @return Pointer to the looked-up object, or `nullptr` if the handle is not valid. | ||
| 75 | */ | ||
| 76 | SharedPtr<Object> GetGeneric(Handle handle) const; | ||
| 77 | |||
| 78 | /** | ||
| 79 | * Looks up a handle while verifying its type. | ||
| 80 | * @return Pointer to the looked-up object, or `nullptr` if the handle is not valid or its | ||
| 81 | * type differs from the requested one. | ||
| 82 | */ | ||
| 83 | template <class T> | ||
| 84 | SharedPtr<T> Get(Handle handle) const { | ||
| 85 | return DynamicObjectCast<T>(GetGeneric(handle)); | ||
| 86 | } | ||
| 87 | |||
| 88 | /// Closes all handles held in this table. | ||
| 89 | void Clear(); | ||
| 90 | |||
| 91 | private: | ||
| 92 | /** | ||
| 93 | * This is the maximum limit of handles allowed per process in CTR-OS. It can be further | ||
| 94 | * reduced by ExHeader values, but this is not emulated here. | ||
| 95 | */ | ||
| 96 | static const size_t MAX_COUNT = 4096; | ||
| 97 | |||
| 98 | static u16 GetSlot(Handle handle) { | ||
| 99 | return handle >> 15; | ||
| 100 | } | ||
| 101 | static u16 GetGeneration(Handle handle) { | ||
| 102 | return handle & 0x7FFF; | ||
| 103 | } | ||
| 104 | |||
| 105 | /// Stores the Object referenced by the handle or null if the slot is empty. | ||
| 106 | std::array<SharedPtr<Object>, MAX_COUNT> objects; | ||
| 107 | |||
| 108 | /** | ||
| 109 | * The value of `next_generation` when the handle was created, used to check for validity. For | ||
| 110 | * empty slots, contains the index of the next free slot in the list. | ||
| 111 | */ | ||
| 112 | std::array<u16, MAX_COUNT> generations; | ||
| 113 | |||
| 114 | /** | ||
| 115 | * Global counter of the number of created handles. Stored in `generations` when a handle is | ||
| 116 | * created, and wraps around to 1 when it hits 0x8000. | ||
| 117 | */ | ||
| 118 | u16 next_generation; | ||
| 119 | |||
| 120 | /// Head of the free slots linked list. | ||
| 121 | u16 next_free_slot; | ||
| 122 | }; | ||
| 123 | |||
| 124 | extern HandleTable g_handle_table; | ||
| 125 | |||
| 126 | } // namespace | ||
diff --git a/src/core/hle/kernel/kernel.cpp b/src/core/hle/kernel/kernel.cpp index 7f84e01aa..7470a97ca 100644 --- a/src/core/hle/kernel/kernel.cpp +++ b/src/core/hle/kernel/kernel.cpp | |||
| @@ -2,11 +2,8 @@ | |||
| 2 | // Licensed under GPLv2 or any later version | 2 | // Licensed under GPLv2 or any later version |
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| 5 | #include <algorithm> | ||
| 6 | #include "common/assert.h" | ||
| 7 | #include "common/logging/log.h" | ||
| 8 | #include "core/hle/config_mem.h" | 5 | #include "core/hle/config_mem.h" |
| 9 | #include "core/hle/kernel/errors.h" | 6 | #include "core/hle/kernel/handle_table.h" |
| 10 | #include "core/hle/kernel/kernel.h" | 7 | #include "core/hle/kernel/kernel.h" |
| 11 | #include "core/hle/kernel/memory.h" | 8 | #include "core/hle/kernel/memory.h" |
| 12 | #include "core/hle/kernel/process.h" | 9 | #include "core/hle/kernel/process.h" |
| @@ -18,165 +15,6 @@ | |||
| 18 | namespace Kernel { | 15 | namespace Kernel { |
| 19 | 16 | ||
| 20 | unsigned int Object::next_object_id; | 17 | unsigned int Object::next_object_id; |
| 21 | HandleTable g_handle_table; | ||
| 22 | |||
| 23 | void WaitObject::AddWaitingThread(SharedPtr<Thread> thread) { | ||
| 24 | auto itr = std::find(waiting_threads.begin(), waiting_threads.end(), thread); | ||
| 25 | if (itr == waiting_threads.end()) | ||
| 26 | waiting_threads.push_back(std::move(thread)); | ||
| 27 | } | ||
| 28 | |||
| 29 | void WaitObject::RemoveWaitingThread(Thread* thread) { | ||
| 30 | auto itr = std::find(waiting_threads.begin(), waiting_threads.end(), thread); | ||
| 31 | // If a thread passed multiple handles to the same object, | ||
| 32 | // the kernel might attempt to remove the thread from the object's | ||
| 33 | // waiting threads list multiple times. | ||
| 34 | if (itr != waiting_threads.end()) | ||
| 35 | waiting_threads.erase(itr); | ||
| 36 | } | ||
| 37 | |||
| 38 | SharedPtr<Thread> WaitObject::GetHighestPriorityReadyThread() { | ||
| 39 | Thread* candidate = nullptr; | ||
| 40 | s32 candidate_priority = THREADPRIO_LOWEST + 1; | ||
| 41 | |||
| 42 | for (const auto& thread : waiting_threads) { | ||
| 43 | // The list of waiting threads must not contain threads that are not waiting to be awakened. | ||
| 44 | ASSERT_MSG(thread->status == THREADSTATUS_WAIT_SYNCH_ANY || | ||
| 45 | thread->status == THREADSTATUS_WAIT_SYNCH_ALL, | ||
| 46 | "Inconsistent thread statuses in waiting_threads"); | ||
| 47 | |||
| 48 | if (thread->current_priority >= candidate_priority) | ||
| 49 | continue; | ||
| 50 | |||
| 51 | if (ShouldWait(thread.get())) | ||
| 52 | continue; | ||
| 53 | |||
| 54 | // A thread is ready to run if it's either in THREADSTATUS_WAIT_SYNCH_ANY or | ||
| 55 | // in THREADSTATUS_WAIT_SYNCH_ALL and the rest of the objects it is waiting on are ready. | ||
| 56 | bool ready_to_run = true; | ||
| 57 | if (thread->status == THREADSTATUS_WAIT_SYNCH_ALL) { | ||
| 58 | ready_to_run = std::none_of(thread->wait_objects.begin(), thread->wait_objects.end(), | ||
| 59 | [&thread](const SharedPtr<WaitObject>& object) { | ||
| 60 | return object->ShouldWait(thread.get()); | ||
| 61 | }); | ||
| 62 | } | ||
| 63 | |||
| 64 | if (ready_to_run) { | ||
| 65 | candidate = thread.get(); | ||
| 66 | candidate_priority = thread->current_priority; | ||
| 67 | } | ||
| 68 | } | ||
| 69 | |||
| 70 | return candidate; | ||
| 71 | } | ||
| 72 | |||
| 73 | void WaitObject::WakeupAllWaitingThreads() { | ||
| 74 | while (auto thread = GetHighestPriorityReadyThread()) { | ||
| 75 | if (!thread->IsSleepingOnWaitAll()) { | ||
| 76 | Acquire(thread.get()); | ||
| 77 | // Set the output index of the WaitSynchronizationN call to the index of this object. | ||
| 78 | if (thread->wait_set_output) { | ||
| 79 | thread->SetWaitSynchronizationOutput(thread->GetWaitObjectIndex(this)); | ||
| 80 | thread->wait_set_output = false; | ||
| 81 | } | ||
| 82 | } else { | ||
| 83 | for (auto& object : thread->wait_objects) { | ||
| 84 | object->Acquire(thread.get()); | ||
| 85 | } | ||
| 86 | // Note: This case doesn't update the output index of WaitSynchronizationN. | ||
| 87 | } | ||
| 88 | |||
| 89 | for (auto& object : thread->wait_objects) | ||
| 90 | object->RemoveWaitingThread(thread.get()); | ||
| 91 | thread->wait_objects.clear(); | ||
| 92 | |||
| 93 | thread->SetWaitSynchronizationResult(RESULT_SUCCESS); | ||
| 94 | thread->ResumeFromWait(); | ||
| 95 | } | ||
| 96 | } | ||
| 97 | |||
| 98 | const std::vector<SharedPtr<Thread>>& WaitObject::GetWaitingThreads() const { | ||
| 99 | return waiting_threads; | ||
| 100 | } | ||
| 101 | |||
| 102 | HandleTable::HandleTable() { | ||
| 103 | next_generation = 1; | ||
| 104 | Clear(); | ||
| 105 | } | ||
| 106 | |||
| 107 | ResultVal<Handle> HandleTable::Create(SharedPtr<Object> obj) { | ||
| 108 | DEBUG_ASSERT(obj != nullptr); | ||
| 109 | |||
| 110 | u16 slot = next_free_slot; | ||
| 111 | if (slot >= generations.size()) { | ||
| 112 | LOG_ERROR(Kernel, "Unable to allocate Handle, too many slots in use."); | ||
| 113 | return ERR_OUT_OF_HANDLES; | ||
| 114 | } | ||
| 115 | next_free_slot = generations[slot]; | ||
| 116 | |||
| 117 | u16 generation = next_generation++; | ||
| 118 | |||
| 119 | // Overflow count so it fits in the 15 bits dedicated to the generation in the handle. | ||
| 120 | // CTR-OS doesn't use generation 0, so skip straight to 1. | ||
| 121 | if (next_generation >= (1 << 15)) | ||
| 122 | next_generation = 1; | ||
| 123 | |||
| 124 | generations[slot] = generation; | ||
| 125 | objects[slot] = std::move(obj); | ||
| 126 | |||
| 127 | Handle handle = generation | (slot << 15); | ||
| 128 | return MakeResult<Handle>(handle); | ||
| 129 | } | ||
| 130 | |||
| 131 | ResultVal<Handle> HandleTable::Duplicate(Handle handle) { | ||
| 132 | SharedPtr<Object> object = GetGeneric(handle); | ||
| 133 | if (object == nullptr) { | ||
| 134 | LOG_ERROR(Kernel, "Tried to duplicate invalid handle: %08X", handle); | ||
| 135 | return ERR_INVALID_HANDLE; | ||
| 136 | } | ||
| 137 | return Create(std::move(object)); | ||
| 138 | } | ||
| 139 | |||
| 140 | ResultCode HandleTable::Close(Handle handle) { | ||
| 141 | if (!IsValid(handle)) | ||
| 142 | return ERR_INVALID_HANDLE; | ||
| 143 | |||
| 144 | u16 slot = GetSlot(handle); | ||
| 145 | |||
| 146 | objects[slot] = nullptr; | ||
| 147 | |||
| 148 | generations[slot] = next_free_slot; | ||
| 149 | next_free_slot = slot; | ||
| 150 | return RESULT_SUCCESS; | ||
| 151 | } | ||
| 152 | |||
| 153 | bool HandleTable::IsValid(Handle handle) const { | ||
| 154 | size_t slot = GetSlot(handle); | ||
| 155 | u16 generation = GetGeneration(handle); | ||
| 156 | |||
| 157 | return slot < MAX_COUNT && objects[slot] != nullptr && generations[slot] == generation; | ||
| 158 | } | ||
| 159 | |||
| 160 | SharedPtr<Object> HandleTable::GetGeneric(Handle handle) const { | ||
| 161 | if (handle == CurrentThread) { | ||
| 162 | return GetCurrentThread(); | ||
| 163 | } else if (handle == CurrentProcess) { | ||
| 164 | return g_current_process; | ||
| 165 | } | ||
| 166 | |||
| 167 | if (!IsValid(handle)) { | ||
| 168 | return nullptr; | ||
| 169 | } | ||
| 170 | return objects[GetSlot(handle)]; | ||
| 171 | } | ||
| 172 | |||
| 173 | void HandleTable::Clear() { | ||
| 174 | for (u16 i = 0; i < MAX_COUNT; ++i) { | ||
| 175 | generations[i] = i + 1; | ||
| 176 | objects[i] = nullptr; | ||
| 177 | } | ||
| 178 | next_free_slot = 0; | ||
| 179 | } | ||
| 180 | 18 | ||
| 181 | /// Initialize the kernel | 19 | /// Initialize the kernel |
| 182 | void Init(u32 system_mode) { | 20 | void Init(u32 system_mode) { |
diff --git a/src/core/hle/kernel/kernel.h b/src/core/hle/kernel/kernel.h index 94f2025a0..9cf288b08 100644 --- a/src/core/hle/kernel/kernel.h +++ b/src/core/hle/kernel/kernel.h | |||
| @@ -4,26 +4,16 @@ | |||
| 4 | 4 | ||
| 5 | #pragma once | 5 | #pragma once |
| 6 | 6 | ||
| 7 | #include <algorithm> | ||
| 8 | #include <array> | ||
| 9 | #include <cstddef> | 7 | #include <cstddef> |
| 10 | #include <string> | 8 | #include <string> |
| 11 | #include <vector> | 9 | #include <utility> |
| 12 | #include <boost/smart_ptr/intrusive_ptr.hpp> | 10 | #include <boost/smart_ptr/intrusive_ptr.hpp> |
| 13 | #include "common/common_types.h" | 11 | #include "common/common_types.h" |
| 14 | #include "core/hle/result.h" | ||
| 15 | 12 | ||
| 16 | namespace Kernel { | 13 | namespace Kernel { |
| 17 | 14 | ||
| 18 | using Handle = u32; | 15 | using Handle = u32; |
| 19 | 16 | ||
| 20 | class Thread; | ||
| 21 | |||
| 22 | enum KernelHandle : Handle { | ||
| 23 | CurrentThread = 0xFFFF8000, | ||
| 24 | CurrentProcess = 0xFFFF8001, | ||
| 25 | }; | ||
| 26 | |||
| 27 | enum class HandleType : u32 { | 17 | enum class HandleType : u32 { |
| 28 | Unknown, | 18 | Unknown, |
| 29 | Event, | 19 | Event, |
| @@ -121,170 +111,17 @@ inline void intrusive_ptr_release(Object* object) { | |||
| 121 | template <typename T> | 111 | template <typename T> |
| 122 | using SharedPtr = boost::intrusive_ptr<T>; | 112 | using SharedPtr = boost::intrusive_ptr<T>; |
| 123 | 113 | ||
| 124 | /// Class that represents a Kernel object that a thread can be waiting on | ||
| 125 | class WaitObject : public Object { | ||
| 126 | public: | ||
| 127 | /** | ||
| 128 | * Check if the specified thread should wait until the object is available | ||
| 129 | * @param thread The thread about which we're deciding. | ||
| 130 | * @return True if the current thread should wait due to this object being unavailable | ||
| 131 | */ | ||
| 132 | virtual bool ShouldWait(Thread* thread) const = 0; | ||
| 133 | |||
| 134 | /// Acquire/lock the object for the specified thread if it is available | ||
| 135 | virtual void Acquire(Thread* thread) = 0; | ||
| 136 | |||
| 137 | /** | ||
| 138 | * Add a thread to wait on this object | ||
| 139 | * @param thread Pointer to thread to add | ||
| 140 | */ | ||
| 141 | virtual void AddWaitingThread(SharedPtr<Thread> thread); | ||
| 142 | |||
| 143 | /** | ||
| 144 | * Removes a thread from waiting on this object (e.g. if it was resumed already) | ||
| 145 | * @param thread Pointer to thread to remove | ||
| 146 | */ | ||
| 147 | virtual void RemoveWaitingThread(Thread* thread); | ||
| 148 | |||
| 149 | /** | ||
| 150 | * Wake up all threads waiting on this object that can be awoken, in priority order, | ||
| 151 | * and set the synchronization result and output of the thread. | ||
| 152 | */ | ||
| 153 | virtual void WakeupAllWaitingThreads(); | ||
| 154 | |||
| 155 | /// Obtains the highest priority thread that is ready to run from this object's waiting list. | ||
| 156 | SharedPtr<Thread> GetHighestPriorityReadyThread(); | ||
| 157 | |||
| 158 | /// Get a const reference to the waiting threads list for debug use | ||
| 159 | const std::vector<SharedPtr<Thread>>& GetWaitingThreads() const; | ||
| 160 | |||
| 161 | private: | ||
| 162 | /// Threads waiting for this object to become available | ||
| 163 | std::vector<SharedPtr<Thread>> waiting_threads; | ||
| 164 | }; | ||
| 165 | |||
| 166 | /** | 114 | /** |
| 167 | * This class allows the creation of Handles, which are references to objects that can be tested | 115 | * Attempts to downcast the given Object pointer to a pointer to T. |
| 168 | * for validity and looked up. Here they are used to pass references to kernel objects to/from the | 116 | * @return Derived pointer to the object, or `nullptr` if `object` isn't of type T. |
| 169 | * emulated process. it has been designed so that it follows the same handle format and has | ||
| 170 | * approximately the same restrictions as the handle manager in the CTR-OS. | ||
| 171 | * | ||
| 172 | * Handles contain two sub-fields: a slot index (bits 31:15) and a generation value (bits 14:0). | ||
| 173 | * The slot index is used to index into the arrays in this class to access the data corresponding | ||
| 174 | * to the Handle. | ||
| 175 | * | ||
| 176 | * To prevent accidental use of a freed Handle whose slot has already been reused, a global counter | ||
| 177 | * is kept and incremented every time a Handle is created. This is the Handle's "generation". The | ||
| 178 | * value of the counter is stored into the Handle as well as in the handle table (in the | ||
| 179 | * "generations" array). When looking up a handle, the Handle's generation must match with the | ||
| 180 | * value stored on the class, otherwise the Handle is considered invalid. | ||
| 181 | * | ||
| 182 | * To find free slots when allocating a Handle without needing to scan the entire object array, the | ||
| 183 | * generations field of unallocated slots is re-purposed as a linked list of indices to free slots. | ||
| 184 | * When a Handle is created, an index is popped off the list and used for the new Handle. When it | ||
| 185 | * is destroyed, it is again pushed onto the list to be re-used by the next allocation. It is | ||
| 186 | * likely that this allocation strategy differs from the one used in CTR-OS, but this hasn't been | ||
| 187 | * verified and isn't likely to cause any problems. | ||
| 188 | */ | 117 | */ |
| 189 | class HandleTable final : NonCopyable { | 118 | template <typename T> |
| 190 | public: | 119 | inline SharedPtr<T> DynamicObjectCast(SharedPtr<Object> object) { |
| 191 | HandleTable(); | 120 | if (object != nullptr && object->GetHandleType() == T::HANDLE_TYPE) { |
| 192 | 121 | return boost::static_pointer_cast<T>(std::move(object)); | |
| 193 | /** | ||
| 194 | * Allocates a handle for the given object. | ||
| 195 | * @return The created Handle or one of the following errors: | ||
| 196 | * - `ERR_OUT_OF_HANDLES`: the maximum number of handles has been exceeded. | ||
| 197 | */ | ||
| 198 | ResultVal<Handle> Create(SharedPtr<Object> obj); | ||
| 199 | |||
| 200 | /** | ||
| 201 | * Returns a new handle that points to the same object as the passed in handle. | ||
| 202 | * @return The duplicated Handle or one of the following errors: | ||
| 203 | * - `ERR_INVALID_HANDLE`: an invalid handle was passed in. | ||
| 204 | * - Any errors returned by `Create()`. | ||
| 205 | */ | ||
| 206 | ResultVal<Handle> Duplicate(Handle handle); | ||
| 207 | |||
| 208 | /** | ||
| 209 | * Closes a handle, removing it from the table and decreasing the object's ref-count. | ||
| 210 | * @return `RESULT_SUCCESS` or one of the following errors: | ||
| 211 | * - `ERR_INVALID_HANDLE`: an invalid handle was passed in. | ||
| 212 | */ | ||
| 213 | ResultCode Close(Handle handle); | ||
| 214 | |||
| 215 | /// Checks if a handle is valid and points to an existing object. | ||
| 216 | bool IsValid(Handle handle) const; | ||
| 217 | |||
| 218 | /** | ||
| 219 | * Looks up a handle. | ||
| 220 | * @return Pointer to the looked-up object, or `nullptr` if the handle is not valid. | ||
| 221 | */ | ||
| 222 | SharedPtr<Object> GetGeneric(Handle handle) const; | ||
| 223 | |||
| 224 | /** | ||
| 225 | * Looks up a handle while verifying its type. | ||
| 226 | * @return Pointer to the looked-up object, or `nullptr` if the handle is not valid or its | ||
| 227 | * type differs from the handle type `T::HANDLE_TYPE`. | ||
| 228 | */ | ||
| 229 | template <class T> | ||
| 230 | SharedPtr<T> Get(Handle handle) const { | ||
| 231 | SharedPtr<Object> object = GetGeneric(handle); | ||
| 232 | if (object != nullptr && object->GetHandleType() == T::HANDLE_TYPE) { | ||
| 233 | return boost::static_pointer_cast<T>(std::move(object)); | ||
| 234 | } | ||
| 235 | return nullptr; | ||
| 236 | } | ||
| 237 | |||
| 238 | /** | ||
| 239 | * Looks up a handle while verifying that it is an object that a thread can wait on | ||
| 240 | * @return Pointer to the looked-up object, or `nullptr` if the handle is not valid or it is | ||
| 241 | * not a waitable object. | ||
| 242 | */ | ||
| 243 | SharedPtr<WaitObject> GetWaitObject(Handle handle) const { | ||
| 244 | SharedPtr<Object> object = GetGeneric(handle); | ||
| 245 | if (object != nullptr && object->IsWaitable()) { | ||
| 246 | return boost::static_pointer_cast<WaitObject>(std::move(object)); | ||
| 247 | } | ||
| 248 | return nullptr; | ||
| 249 | } | ||
| 250 | |||
| 251 | /// Closes all handles held in this table. | ||
| 252 | void Clear(); | ||
| 253 | |||
| 254 | private: | ||
| 255 | /** | ||
| 256 | * This is the maximum limit of handles allowed per process in CTR-OS. It can be further | ||
| 257 | * reduced by ExHeader values, but this is not emulated here. | ||
| 258 | */ | ||
| 259 | static const size_t MAX_COUNT = 4096; | ||
| 260 | |||
| 261 | static u16 GetSlot(Handle handle) { | ||
| 262 | return handle >> 15; | ||
| 263 | } | ||
| 264 | static u16 GetGeneration(Handle handle) { | ||
| 265 | return handle & 0x7FFF; | ||
| 266 | } | 122 | } |
| 267 | 123 | return nullptr; | |
| 268 | /// Stores the Object referenced by the handle or null if the slot is empty. | 124 | } |
| 269 | std::array<SharedPtr<Object>, MAX_COUNT> objects; | ||
| 270 | |||
| 271 | /** | ||
| 272 | * The value of `next_generation` when the handle was created, used to check for validity. For | ||
| 273 | * empty slots, contains the index of the next free slot in the list. | ||
| 274 | */ | ||
| 275 | std::array<u16, MAX_COUNT> generations; | ||
| 276 | |||
| 277 | /** | ||
| 278 | * Global counter of the number of created handles. Stored in `generations` when a handle is | ||
| 279 | * created, and wraps around to 1 when it hits 0x8000. | ||
| 280 | */ | ||
| 281 | u16 next_generation; | ||
| 282 | |||
| 283 | /// Head of the free slots linked list. | ||
| 284 | u16 next_free_slot; | ||
| 285 | }; | ||
| 286 | |||
| 287 | extern HandleTable g_handle_table; | ||
| 288 | 125 | ||
| 289 | /// Initialize the kernel with the specified system mode. | 126 | /// Initialize the kernel with the specified system mode. |
| 290 | void Init(u32 system_mode); | 127 | void Init(u32 system_mode); |
diff --git a/src/core/hle/kernel/memory.cpp b/src/core/hle/kernel/memory.cpp index 8250a90b5..804f23b1c 100644 --- a/src/core/hle/kernel/memory.cpp +++ b/src/core/hle/kernel/memory.cpp | |||
| @@ -2,6 +2,7 @@ | |||
| 2 | // Licensed under GPLv2 or any later version | 2 | // Licensed under GPLv2 or any later version |
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| 5 | #include <algorithm> | ||
| 5 | #include <cinttypes> | 6 | #include <cinttypes> |
| 6 | #include <map> | 7 | #include <map> |
| 7 | #include <memory> | 8 | #include <memory> |
diff --git a/src/core/hle/kernel/mutex.h b/src/core/hle/kernel/mutex.h index c57adf400..bacacd690 100644 --- a/src/core/hle/kernel/mutex.h +++ b/src/core/hle/kernel/mutex.h | |||
| @@ -7,6 +7,7 @@ | |||
| 7 | #include <string> | 7 | #include <string> |
| 8 | #include "common/common_types.h" | 8 | #include "common/common_types.h" |
| 9 | #include "core/hle/kernel/kernel.h" | 9 | #include "core/hle/kernel/kernel.h" |
| 10 | #include "core/hle/kernel/wait_object.h" | ||
| 10 | 11 | ||
| 11 | namespace Kernel { | 12 | namespace Kernel { |
| 12 | 13 | ||
diff --git a/src/core/hle/kernel/resource_limit.cpp b/src/core/hle/kernel/resource_limit.cpp index 3f51bc5de..a8f10a3ee 100644 --- a/src/core/hle/kernel/resource_limit.cpp +++ b/src/core/hle/kernel/resource_limit.cpp | |||
| @@ -3,6 +3,7 @@ | |||
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| 5 | #include <cstring> | 5 | #include <cstring> |
| 6 | #include "common/assert.h" | ||
| 6 | #include "common/logging/log.h" | 7 | #include "common/logging/log.h" |
| 7 | #include "core/hle/kernel/resource_limit.h" | 8 | #include "core/hle/kernel/resource_limit.h" |
| 8 | 9 | ||
diff --git a/src/core/hle/kernel/semaphore.h b/src/core/hle/kernel/semaphore.h index cde94f7cc..7b0cacf2e 100644 --- a/src/core/hle/kernel/semaphore.h +++ b/src/core/hle/kernel/semaphore.h | |||
| @@ -8,6 +8,8 @@ | |||
| 8 | #include <string> | 8 | #include <string> |
| 9 | #include "common/common_types.h" | 9 | #include "common/common_types.h" |
| 10 | #include "core/hle/kernel/kernel.h" | 10 | #include "core/hle/kernel/kernel.h" |
| 11 | #include "core/hle/kernel/wait_object.h" | ||
| 12 | #include "core/hle/result.h" | ||
| 11 | 13 | ||
| 12 | namespace Kernel { | 14 | namespace Kernel { |
| 13 | 15 | ||
diff --git a/src/core/hle/kernel/server_port.h b/src/core/hle/kernel/server_port.h index 6f8bdb6a9..2a24d8412 100644 --- a/src/core/hle/kernel/server_port.h +++ b/src/core/hle/kernel/server_port.h | |||
| @@ -9,6 +9,7 @@ | |||
| 9 | #include <tuple> | 9 | #include <tuple> |
| 10 | #include "common/common_types.h" | 10 | #include "common/common_types.h" |
| 11 | #include "core/hle/kernel/kernel.h" | 11 | #include "core/hle/kernel/kernel.h" |
| 12 | #include "core/hle/kernel/wait_object.h" | ||
| 12 | 13 | ||
| 13 | namespace Service { | 14 | namespace Service { |
| 14 | class SessionRequestHandler; | 15 | class SessionRequestHandler; |
diff --git a/src/core/hle/kernel/server_session.h b/src/core/hle/kernel/server_session.h index c907d487c..f1b76d8aa 100644 --- a/src/core/hle/kernel/server_session.h +++ b/src/core/hle/kernel/server_session.h | |||
| @@ -10,7 +10,7 @@ | |||
| 10 | #include "common/common_types.h" | 10 | #include "common/common_types.h" |
| 11 | #include "core/hle/kernel/kernel.h" | 11 | #include "core/hle/kernel/kernel.h" |
| 12 | #include "core/hle/kernel/session.h" | 12 | #include "core/hle/kernel/session.h" |
| 13 | #include "core/hle/kernel/thread.h" | 13 | #include "core/hle/kernel/wait_object.h" |
| 14 | #include "core/hle/result.h" | 14 | #include "core/hle/result.h" |
| 15 | #include "core/hle/service/service.h" | 15 | #include "core/hle/service/service.h" |
| 16 | #include "core/memory.h" | 16 | #include "core/memory.h" |
| @@ -20,6 +20,7 @@ namespace Kernel { | |||
| 20 | class ClientSession; | 20 | class ClientSession; |
| 21 | class ClientPort; | 21 | class ClientPort; |
| 22 | class ServerSession; | 22 | class ServerSession; |
| 23 | class Thread; | ||
| 23 | 24 | ||
| 24 | /** | 25 | /** |
| 25 | * Kernel object representing the server endpoint of an IPC session. Sessions are the basic CTR-OS | 26 | * Kernel object representing the server endpoint of an IPC session. Sessions are the basic CTR-OS |
diff --git a/src/core/hle/kernel/thread.cpp b/src/core/hle/kernel/thread.cpp index 519ff51a8..75ce626f8 100644 --- a/src/core/hle/kernel/thread.cpp +++ b/src/core/hle/kernel/thread.cpp | |||
| @@ -15,6 +15,7 @@ | |||
| 15 | #include "core/core.h" | 15 | #include "core/core.h" |
| 16 | #include "core/core_timing.h" | 16 | #include "core/core_timing.h" |
| 17 | #include "core/hle/kernel/errors.h" | 17 | #include "core/hle/kernel/errors.h" |
| 18 | #include "core/hle/kernel/handle_table.h" | ||
| 18 | #include "core/hle/kernel/kernel.h" | 19 | #include "core/hle/kernel/kernel.h" |
| 19 | #include "core/hle/kernel/memory.h" | 20 | #include "core/hle/kernel/memory.h" |
| 20 | #include "core/hle/kernel/mutex.h" | 21 | #include "core/hle/kernel/mutex.h" |
diff --git a/src/core/hle/kernel/thread.h b/src/core/hle/kernel/thread.h index 7b5169cfc..6a3566f15 100644 --- a/src/core/hle/kernel/thread.h +++ b/src/core/hle/kernel/thread.h | |||
| @@ -12,6 +12,7 @@ | |||
| 12 | #include "common/common_types.h" | 12 | #include "common/common_types.h" |
| 13 | #include "core/arm/arm_interface.h" | 13 | #include "core/arm/arm_interface.h" |
| 14 | #include "core/hle/kernel/kernel.h" | 14 | #include "core/hle/kernel/kernel.h" |
| 15 | #include "core/hle/kernel/wait_object.h" | ||
| 15 | #include "core/hle/result.h" | 16 | #include "core/hle/result.h" |
| 16 | 17 | ||
| 17 | enum ThreadPriority : s32 { | 18 | enum ThreadPriority : s32 { |
diff --git a/src/core/hle/kernel/timer.cpp b/src/core/hle/kernel/timer.cpp index a00c75679..6f2cf3b02 100644 --- a/src/core/hle/kernel/timer.cpp +++ b/src/core/hle/kernel/timer.cpp | |||
| @@ -6,6 +6,7 @@ | |||
| 6 | #include "common/assert.h" | 6 | #include "common/assert.h" |
| 7 | #include "common/logging/log.h" | 7 | #include "common/logging/log.h" |
| 8 | #include "core/core_timing.h" | 8 | #include "core/core_timing.h" |
| 9 | #include "core/hle/kernel/handle_table.h" | ||
| 9 | #include "core/hle/kernel/kernel.h" | 10 | #include "core/hle/kernel/kernel.h" |
| 10 | #include "core/hle/kernel/thread.h" | 11 | #include "core/hle/kernel/thread.h" |
| 11 | #include "core/hle/kernel/timer.h" | 12 | #include "core/hle/kernel/timer.h" |
diff --git a/src/core/hle/kernel/timer.h b/src/core/hle/kernel/timer.h index b0f818933..82552372d 100644 --- a/src/core/hle/kernel/timer.h +++ b/src/core/hle/kernel/timer.h | |||
| @@ -6,6 +6,7 @@ | |||
| 6 | 6 | ||
| 7 | #include "common/common_types.h" | 7 | #include "common/common_types.h" |
| 8 | #include "core/hle/kernel/kernel.h" | 8 | #include "core/hle/kernel/kernel.h" |
| 9 | #include "core/hle/kernel/wait_object.h" | ||
| 9 | 10 | ||
| 10 | namespace Kernel { | 11 | namespace Kernel { |
| 11 | 12 | ||
diff --git a/src/core/hle/kernel/wait_object.cpp b/src/core/hle/kernel/wait_object.cpp new file mode 100644 index 000000000..f245eda6c --- /dev/null +++ b/src/core/hle/kernel/wait_object.cpp | |||
| @@ -0,0 +1,99 @@ | |||
| 1 | // Copyright 2014 Citra Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #include <algorithm> | ||
| 6 | #include "common/assert.h" | ||
| 7 | #include "common/logging/log.h" | ||
| 8 | #include "core/hle/config_mem.h" | ||
| 9 | #include "core/hle/kernel/errors.h" | ||
| 10 | #include "core/hle/kernel/kernel.h" | ||
| 11 | #include "core/hle/kernel/memory.h" | ||
| 12 | #include "core/hle/kernel/process.h" | ||
| 13 | #include "core/hle/kernel/resource_limit.h" | ||
| 14 | #include "core/hle/kernel/thread.h" | ||
| 15 | #include "core/hle/kernel/timer.h" | ||
| 16 | #include "core/hle/shared_page.h" | ||
| 17 | |||
| 18 | namespace Kernel { | ||
| 19 | |||
| 20 | void WaitObject::AddWaitingThread(SharedPtr<Thread> thread) { | ||
| 21 | auto itr = std::find(waiting_threads.begin(), waiting_threads.end(), thread); | ||
| 22 | if (itr == waiting_threads.end()) | ||
| 23 | waiting_threads.push_back(std::move(thread)); | ||
| 24 | } | ||
| 25 | |||
| 26 | void WaitObject::RemoveWaitingThread(Thread* thread) { | ||
| 27 | auto itr = std::find(waiting_threads.begin(), waiting_threads.end(), thread); | ||
| 28 | // If a thread passed multiple handles to the same object, | ||
| 29 | // the kernel might attempt to remove the thread from the object's | ||
| 30 | // waiting threads list multiple times. | ||
| 31 | if (itr != waiting_threads.end()) | ||
| 32 | waiting_threads.erase(itr); | ||
| 33 | } | ||
| 34 | |||
| 35 | SharedPtr<Thread> WaitObject::GetHighestPriorityReadyThread() { | ||
| 36 | Thread* candidate = nullptr; | ||
| 37 | s32 candidate_priority = THREADPRIO_LOWEST + 1; | ||
| 38 | |||
| 39 | for (const auto& thread : waiting_threads) { | ||
| 40 | // The list of waiting threads must not contain threads that are not waiting to be awakened. | ||
| 41 | ASSERT_MSG(thread->status == THREADSTATUS_WAIT_SYNCH_ANY || | ||
| 42 | thread->status == THREADSTATUS_WAIT_SYNCH_ALL, | ||
| 43 | "Inconsistent thread statuses in waiting_threads"); | ||
| 44 | |||
| 45 | if (thread->current_priority >= candidate_priority) | ||
| 46 | continue; | ||
| 47 | |||
| 48 | if (ShouldWait(thread.get())) | ||
| 49 | continue; | ||
| 50 | |||
| 51 | // A thread is ready to run if it's either in THREADSTATUS_WAIT_SYNCH_ANY or | ||
| 52 | // in THREADSTATUS_WAIT_SYNCH_ALL and the rest of the objects it is waiting on are ready. | ||
| 53 | bool ready_to_run = true; | ||
| 54 | if (thread->status == THREADSTATUS_WAIT_SYNCH_ALL) { | ||
| 55 | ready_to_run = std::none_of(thread->wait_objects.begin(), thread->wait_objects.end(), | ||
| 56 | [&thread](const SharedPtr<WaitObject>& object) { | ||
| 57 | return object->ShouldWait(thread.get()); | ||
| 58 | }); | ||
| 59 | } | ||
| 60 | |||
| 61 | if (ready_to_run) { | ||
| 62 | candidate = thread.get(); | ||
| 63 | candidate_priority = thread->current_priority; | ||
| 64 | } | ||
| 65 | } | ||
| 66 | |||
| 67 | return candidate; | ||
| 68 | } | ||
| 69 | |||
| 70 | void WaitObject::WakeupAllWaitingThreads() { | ||
| 71 | while (auto thread = GetHighestPriorityReadyThread()) { | ||
| 72 | if (!thread->IsSleepingOnWaitAll()) { | ||
| 73 | Acquire(thread.get()); | ||
| 74 | // Set the output index of the WaitSynchronizationN call to the index of this object. | ||
| 75 | if (thread->wait_set_output) { | ||
| 76 | thread->SetWaitSynchronizationOutput(thread->GetWaitObjectIndex(this)); | ||
| 77 | thread->wait_set_output = false; | ||
| 78 | } | ||
| 79 | } else { | ||
| 80 | for (auto& object : thread->wait_objects) { | ||
| 81 | object->Acquire(thread.get()); | ||
| 82 | } | ||
| 83 | // Note: This case doesn't update the output index of WaitSynchronizationN. | ||
| 84 | } | ||
| 85 | |||
| 86 | for (auto& object : thread->wait_objects) | ||
| 87 | object->RemoveWaitingThread(thread.get()); | ||
| 88 | thread->wait_objects.clear(); | ||
| 89 | |||
| 90 | thread->SetWaitSynchronizationResult(RESULT_SUCCESS); | ||
| 91 | thread->ResumeFromWait(); | ||
| 92 | } | ||
| 93 | } | ||
| 94 | |||
| 95 | const std::vector<SharedPtr<Thread>>& WaitObject::GetWaitingThreads() const { | ||
| 96 | return waiting_threads; | ||
| 97 | } | ||
| 98 | |||
| 99 | } // namespace Kernel | ||
diff --git a/src/core/hle/kernel/wait_object.h b/src/core/hle/kernel/wait_object.h new file mode 100644 index 000000000..861578186 --- /dev/null +++ b/src/core/hle/kernel/wait_object.h | |||
| @@ -0,0 +1,67 @@ | |||
| 1 | // Copyright 2014 Citra Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #pragma once | ||
| 6 | |||
| 7 | #include <vector> | ||
| 8 | #include <boost/smart_ptr/intrusive_ptr.hpp> | ||
| 9 | #include "common/common_types.h" | ||
| 10 | #include "core/hle/kernel/kernel.h" | ||
| 11 | |||
| 12 | namespace Kernel { | ||
| 13 | |||
| 14 | class Thread; | ||
| 15 | |||
| 16 | /// Class that represents a Kernel object that a thread can be waiting on | ||
| 17 | class WaitObject : public Object { | ||
| 18 | public: | ||
| 19 | /** | ||
| 20 | * Check if the specified thread should wait until the object is available | ||
| 21 | * @param thread The thread about which we're deciding. | ||
| 22 | * @return True if the current thread should wait due to this object being unavailable | ||
| 23 | */ | ||
| 24 | virtual bool ShouldWait(Thread* thread) const = 0; | ||
| 25 | |||
| 26 | /// Acquire/lock the object for the specified thread if it is available | ||
| 27 | virtual void Acquire(Thread* thread) = 0; | ||
| 28 | |||
| 29 | /** | ||
| 30 | * Add a thread to wait on this object | ||
| 31 | * @param thread Pointer to thread to add | ||
| 32 | */ | ||
| 33 | virtual void AddWaitingThread(SharedPtr<Thread> thread); | ||
| 34 | |||
| 35 | /** | ||
| 36 | * Removes a thread from waiting on this object (e.g. if it was resumed already) | ||
| 37 | * @param thread Pointer to thread to remove | ||
| 38 | */ | ||
| 39 | virtual void RemoveWaitingThread(Thread* thread); | ||
| 40 | |||
| 41 | /** | ||
| 42 | * Wake up all threads waiting on this object that can be awoken, in priority order, | ||
| 43 | * and set the synchronization result and output of the thread. | ||
| 44 | */ | ||
| 45 | virtual void WakeupAllWaitingThreads(); | ||
| 46 | |||
| 47 | /// Obtains the highest priority thread that is ready to run from this object's waiting list. | ||
| 48 | SharedPtr<Thread> GetHighestPriorityReadyThread(); | ||
| 49 | |||
| 50 | /// Get a const reference to the waiting threads list for debug use | ||
| 51 | const std::vector<SharedPtr<Thread>>& GetWaitingThreads() const; | ||
| 52 | |||
| 53 | private: | ||
| 54 | /// Threads waiting for this object to become available | ||
| 55 | std::vector<SharedPtr<Thread>> waiting_threads; | ||
| 56 | }; | ||
| 57 | |||
| 58 | // Specialization of DynamicObjectCast for WaitObjects | ||
| 59 | template <> | ||
| 60 | inline SharedPtr<WaitObject> DynamicObjectCast<WaitObject>(SharedPtr<Object> object) { | ||
| 61 | if (object != nullptr && object->IsWaitable()) { | ||
| 62 | return boost::static_pointer_cast<WaitObject>(std::move(object)); | ||
| 63 | } | ||
| 64 | return nullptr; | ||
| 65 | } | ||
| 66 | |||
| 67 | } // namespace Kernel | ||
diff --git a/src/core/hle/service/apt/apt.h b/src/core/hle/service/apt/apt.h index e63b61450..ee80926d2 100644 --- a/src/core/hle/service/apt/apt.h +++ b/src/core/hle/service/apt/apt.h | |||
| @@ -4,6 +4,8 @@ | |||
| 4 | 4 | ||
| 5 | #pragma once | 5 | #pragma once |
| 6 | 6 | ||
| 7 | #include <vector> | ||
| 8 | #include "common/common_funcs.h" | ||
| 7 | #include "common/common_types.h" | 9 | #include "common/common_types.h" |
| 8 | #include "common/swap.h" | 10 | #include "common/swap.h" |
| 9 | #include "core/hle/kernel/kernel.h" | 11 | #include "core/hle/kernel/kernel.h" |
diff --git a/src/core/hle/service/service.h b/src/core/hle/service/service.h index e6a5f1417..ffabc24a4 100644 --- a/src/core/hle/service/service.h +++ b/src/core/hle/service/service.h | |||
| @@ -12,7 +12,6 @@ | |||
| 12 | #include "core/hle/ipc.h" | 12 | #include "core/hle/ipc.h" |
| 13 | #include "core/hle/ipc_helpers.h" | 13 | #include "core/hle/ipc_helpers.h" |
| 14 | #include "core/hle/kernel/client_port.h" | 14 | #include "core/hle/kernel/client_port.h" |
| 15 | #include "core/hle/kernel/thread.h" | ||
| 16 | #include "core/hle/result.h" | 15 | #include "core/hle/result.h" |
| 17 | #include "core/memory.h" | 16 | #include "core/memory.h" |
| 18 | 17 | ||
diff --git a/src/core/hle/svc.cpp b/src/core/hle/svc.cpp index 30230d65a..e68b9f16a 100644 --- a/src/core/hle/svc.cpp +++ b/src/core/hle/svc.cpp | |||
| @@ -2,6 +2,7 @@ | |||
| 2 | // Licensed under GPLv2 or any later version | 2 | // Licensed under GPLv2 or any later version |
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| 5 | #include <algorithm> | ||
| 5 | #include <cinttypes> | 6 | #include <cinttypes> |
| 6 | #include <map> | 7 | #include <map> |
| 7 | #include "common/logging/log.h" | 8 | #include "common/logging/log.h" |
| @@ -16,6 +17,7 @@ | |||
| 16 | #include "core/hle/kernel/client_session.h" | 17 | #include "core/hle/kernel/client_session.h" |
| 17 | #include "core/hle/kernel/errors.h" | 18 | #include "core/hle/kernel/errors.h" |
| 18 | #include "core/hle/kernel/event.h" | 19 | #include "core/hle/kernel/event.h" |
| 20 | #include "core/hle/kernel/handle_table.h" | ||
| 19 | #include "core/hle/kernel/memory.h" | 21 | #include "core/hle/kernel/memory.h" |
| 20 | #include "core/hle/kernel/mutex.h" | 22 | #include "core/hle/kernel/mutex.h" |
| 21 | #include "core/hle/kernel/process.h" | 23 | #include "core/hle/kernel/process.h" |
| @@ -27,6 +29,7 @@ | |||
| 27 | #include "core/hle/kernel/thread.h" | 29 | #include "core/hle/kernel/thread.h" |
| 28 | #include "core/hle/kernel/timer.h" | 30 | #include "core/hle/kernel/timer.h" |
| 29 | #include "core/hle/kernel/vm_manager.h" | 31 | #include "core/hle/kernel/vm_manager.h" |
| 32 | #include "core/hle/kernel/wait_object.h" | ||
| 30 | #include "core/hle/result.h" | 33 | #include "core/hle/result.h" |
| 31 | #include "core/hle/service/service.h" | 34 | #include "core/hle/service/service.h" |
| 32 | 35 | ||
| @@ -244,7 +247,7 @@ static ResultCode CloseHandle(Kernel::Handle handle) { | |||
| 244 | 247 | ||
| 245 | /// Wait for a handle to synchronize, timeout after the specified nanoseconds | 248 | /// Wait for a handle to synchronize, timeout after the specified nanoseconds |
| 246 | static ResultCode WaitSynchronization1(Kernel::Handle handle, s64 nano_seconds) { | 249 | static ResultCode WaitSynchronization1(Kernel::Handle handle, s64 nano_seconds) { |
| 247 | auto object = Kernel::g_handle_table.GetWaitObject(handle); | 250 | auto object = Kernel::g_handle_table.Get<Kernel::WaitObject>(handle); |
| 248 | Kernel::Thread* thread = Kernel::GetCurrentThread(); | 251 | Kernel::Thread* thread = Kernel::GetCurrentThread(); |
| 249 | 252 | ||
| 250 | if (object == nullptr) | 253 | if (object == nullptr) |
| @@ -299,7 +302,7 @@ static ResultCode WaitSynchronizationN(s32* out, Kernel::Handle* handles, s32 ha | |||
| 299 | std::vector<ObjectPtr> objects(handle_count); | 302 | std::vector<ObjectPtr> objects(handle_count); |
| 300 | 303 | ||
| 301 | for (int i = 0; i < handle_count; ++i) { | 304 | for (int i = 0; i < handle_count; ++i) { |
| 302 | auto object = Kernel::g_handle_table.GetWaitObject(handles[i]); | 305 | auto object = Kernel::g_handle_table.Get<Kernel::WaitObject>(handles[i]); |
| 303 | if (object == nullptr) | 306 | if (object == nullptr) |
| 304 | return ERR_INVALID_HANDLE; | 307 | return ERR_INVALID_HANDLE; |
| 305 | objects[i] = object; | 308 | objects[i] = object; |