diff options
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 @@ #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() {} 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 @@ #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" 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 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 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 @@ // 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 { 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 @@ #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 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 @@ #include <string> #include "common/common_types.h" #include "core/hle/kernel/kernel.h" +#include "core/hle/result.h" namespace Kernel { 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 @@ #include <memory> #include <string> - #include "common/common_types.h" - #include "core/hle/kernel/kernel.h" +#include "core/hle/result.h" namespace Kernel { 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 @@ #include "common/common_types.h" #include "core/hle/kernel/kernel.h" +#include "core/hle/kernel/wait_object.h" namespace Kernel { 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 @@ +// 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 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 @@ +// 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 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 @@ // 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) { 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 @@ #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; - } - - /** - * 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; +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)); } - - /// 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); 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 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. +#include <algorithm> #include <cinttypes> #include <map> #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 @@ #include <string> #include "common/common_types.h" #include "core/hle/kernel/kernel.h" +#include "core/hle/kernel/wait_object.h" namespace Kernel { 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 @@ // 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" 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 @@ #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 { 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 @@ #include <tuple> #include "common/common_types.h" #include "core/hle/kernel/kernel.h" +#include "core/hle/kernel/wait_object.h" namespace Service { 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 @@ #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 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 @@ #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" 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 @@ #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 { 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 @@ #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" 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 @@ #include "common/common_types.h" #include "core/hle/kernel/kernel.h" +#include "core/hle/kernel/wait_object.h" namespace Kernel { 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 @@ +// 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 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 @@ +// 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 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 @@ #pragma once +#include <vector> +#include "common/common_funcs.h" #include "common/common_types.h" #include "common/swap.h" #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 @@ #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" 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 @@ // 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; |