diff options
Diffstat (limited to 'src/core/hle/kernel')
26 files changed, 534 insertions, 379 deletions
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.cpp b/src/core/hle/kernel/client_port.cpp index 03ffdece1..ce5d94e99 100644 --- a/src/core/hle/kernel/client_port.cpp +++ b/src/core/hle/kernel/client_port.cpp @@ -6,6 +6,7 @@ #include "core/hle/kernel/client_port.h" #include "core/hle/kernel/client_session.h" #include "core/hle/kernel/errors.h" +#include "core/hle/kernel/hle_ipc.h" #include "core/hle/kernel/kernel.h" #include "core/hle/kernel/server_port.h" #include "core/hle/kernel/server_session.h" @@ -25,20 +26,17 @@ ResultVal<SharedPtr<ClientSession>> ClientPort::Connect() { active_sessions++; // Create a new session pair, let the created sessions inherit the parent port's HLE handler. - auto sessions = - ServerSession::CreateSessionPair(server_port->GetName(), server_port->hle_handler, this); - auto client_session = std::get<SharedPtr<ClientSession>>(sessions); - auto server_session = std::get<SharedPtr<ServerSession>>(sessions); + auto sessions = ServerSession::CreateSessionPair(server_port->GetName(), this); if (server_port->hle_handler) - server_port->hle_handler->ClientConnected(server_session); + server_port->hle_handler->ClientConnected(std::get<SharedPtr<ServerSession>>(sessions)); else - server_port->pending_sessions.push_back(std::move(server_session)); + server_port->pending_sessions.push_back(std::get<SharedPtr<ServerSession>>(sessions)); // Wake the threads waiting on the ServerPort server_port->WakeupAllWaitingThreads(); - return MakeResult<SharedPtr<ClientSession>>(std::move(client_session)); + return MakeResult(std::get<SharedPtr<ClientSession>>(sessions)); } } // namespace 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.cpp b/src/core/hle/kernel/client_session.cpp index 783b1c061..6bc49ff64 100644 --- a/src/core/hle/kernel/client_session.cpp +++ b/src/core/hle/kernel/client_session.cpp @@ -5,6 +5,8 @@ #include "common/assert.h" #include "core/hle/kernel/client_session.h" +#include "core/hle/kernel/errors.h" +#include "core/hle/kernel/hle_ipc.h" #include "core/hle/kernel/server_session.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/hle_ipc.cpp b/src/core/hle/kernel/hle_ipc.cpp new file mode 100644 index 000000000..0922b3f47 --- /dev/null +++ b/src/core/hle/kernel/hle_ipc.cpp @@ -0,0 +1,24 @@ +// Copyright 2017 Citra Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include <boost/range/algorithm_ext/erase.hpp> +#include "common/assert.h" +#include "common/common_types.h" +#include "core/hle/kernel/hle_ipc.h" +#include "core/hle/kernel/kernel.h" +#include "core/hle/kernel/server_session.h" + +namespace Kernel { + +void SessionRequestHandler::ClientConnected(SharedPtr<ServerSession> server_session) { + server_session->SetHleHandler(shared_from_this()); + connected_sessions.push_back(server_session); +} + +void SessionRequestHandler::ClientDisconnected(SharedPtr<ServerSession> server_session) { + server_session->SetHleHandler(nullptr); + boost::range::remove_erase(connected_sessions, server_session); +} + +} // namespace Kernel diff --git a/src/core/hle/kernel/hle_ipc.h b/src/core/hle/kernel/hle_ipc.h new file mode 100644 index 000000000..14f682f44 --- /dev/null +++ b/src/core/hle/kernel/hle_ipc.h @@ -0,0 +1,53 @@ +// Copyright 2017 Citra Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include <memory> +#include <vector> +#include "core/hle/kernel/kernel.h" + +namespace Kernel { + +class ServerSession; + +/** + * Interface implemented by HLE Session handlers. + * This can be provided to a ServerSession in order to hook into several relevant events + * (such as a new connection or a SyncRequest) so they can be implemented in the emulator. + */ +class SessionRequestHandler : public std::enable_shared_from_this<SessionRequestHandler> { +public: + /** + * Handles a sync request from the emulated application. + * @param server_session The ServerSession that was triggered for this sync request, + * it should be used to differentiate which client (As in ClientSession) we're answering to. + * TODO(Subv): Use a wrapper structure to hold all the information relevant to + * this request (ServerSession, Originator thread, Translated command buffer, etc). + * @returns ResultCode the result code of the translate operation. + */ + virtual void HandleSyncRequest(Kernel::SharedPtr<Kernel::ServerSession> server_session) = 0; + + /** + * Signals that a client has just connected to this HLE handler and keeps the + * associated ServerSession alive for the duration of the connection. + * @param server_session Owning pointer to the ServerSession associated with the connection. + */ + void ClientConnected(Kernel::SharedPtr<Kernel::ServerSession> server_session); + + /** + * Signals that a client has just disconnected from this HLE handler and releases the + * associated ServerSession. + * @param server_session ServerSession associated with the connection. + */ + void ClientDisconnected(Kernel::SharedPtr<Kernel::ServerSession> server_session); + +protected: + /// List of sessions that are connected to this handler. + /// A ServerSession whose server endpoint is an HLE implementation is kept alive by this list + // for the duration of the connection. + std::vector<Kernel::SharedPtr<Kernel::ServerSession>> connected_sessions; +}; + +} // namespace Kernel 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.cpp b/src/core/hle/kernel/server_port.cpp index fd3bbbcad..4d20c39a1 100644 --- a/src/core/hle/kernel/server_port.cpp +++ b/src/core/hle/kernel/server_port.cpp @@ -24,14 +24,12 @@ void ServerPort::Acquire(Thread* thread) { } std::tuple<SharedPtr<ServerPort>, SharedPtr<ClientPort>> ServerPort::CreatePortPair( - u32 max_sessions, std::string name, - std::shared_ptr<Service::SessionRequestHandler> hle_handler) { + u32 max_sessions, std::string name) { SharedPtr<ServerPort> server_port(new ServerPort); SharedPtr<ClientPort> client_port(new ClientPort); server_port->name = name + "_Server"; - server_port->hle_handler = std::move(hle_handler); client_port->name = name + "_Client"; client_port->server_port = server_port; client_port->max_sessions = max_sessions; diff --git a/src/core/hle/kernel/server_port.h b/src/core/hle/kernel/server_port.h index 6f8bdb6a9..f1419cd46 100644 --- a/src/core/hle/kernel/server_port.h +++ b/src/core/hle/kernel/server_port.h @@ -9,28 +9,24 @@ #include <tuple> #include "common/common_types.h" #include "core/hle/kernel/kernel.h" - -namespace Service { -class SessionRequestHandler; -} +#include "core/hle/kernel/wait_object.h" namespace Kernel { class ClientPort; +class SessionRequestHandler; class ServerPort final : public WaitObject { public: /** * Creates a pair of ServerPort and an associated ClientPort. + * * @param max_sessions Maximum number of sessions to the port * @param name Optional name of the ports - * @param hle_handler Optional HLE handler template for the port, - * ServerSessions crated from this port will inherit a reference to this handler. * @return The created port tuple */ static std::tuple<SharedPtr<ServerPort>, SharedPtr<ClientPort>> CreatePortPair( - u32 max_sessions, std::string name = "UnknownPort", - std::shared_ptr<Service::SessionRequestHandler> hle_handler = nullptr); + u32 max_sessions, std::string name = "UnknownPort"); std::string GetTypeName() const override { return "ServerPort"; @@ -44,6 +40,14 @@ public: return HANDLE_TYPE; } + /** + * Sets the HLE handler template for the port. ServerSessions crated by connecting to this port + * will inherit a reference to this handler. + */ + void SetHleHandler(std::shared_ptr<SessionRequestHandler> hle_handler_) { + hle_handler = std::move(hle_handler_); + } + std::string name; ///< Name of port (optional) std::vector<SharedPtr<WaitObject>> @@ -51,7 +55,7 @@ public: /// This session's HLE request handler template (optional) /// ServerSessions created from this port inherit a reference to this handler. - std::shared_ptr<Service::SessionRequestHandler> hle_handler; + std::shared_ptr<SessionRequestHandler> hle_handler; bool ShouldWait(Thread* thread) const override; void Acquire(Thread* thread) override; diff --git a/src/core/hle/kernel/server_session.cpp b/src/core/hle/kernel/server_session.cpp index 500b909ab..2dc709bc9 100644 --- a/src/core/hle/kernel/server_session.cpp +++ b/src/core/hle/kernel/server_session.cpp @@ -4,8 +4,11 @@ #include <tuple> +#include "core/hle/kernel/client_port.h" #include "core/hle/kernel/client_session.h" +#include "core/hle/kernel/hle_ipc.h" #include "core/hle/kernel/server_session.h" +#include "core/hle/kernel/session.h" #include "core/hle/kernel/thread.h" namespace Kernel { @@ -25,16 +28,14 @@ ServerSession::~ServerSession() { parent->server = nullptr; } -ResultVal<SharedPtr<ServerSession>> ServerSession::Create( - std::string name, std::shared_ptr<Service::SessionRequestHandler> hle_handler) { +ResultVal<SharedPtr<ServerSession>> ServerSession::Create(std::string name) { SharedPtr<ServerSession> server_session(new ServerSession); server_session->name = std::move(name); server_session->signaled = false; - server_session->hle_handler = std::move(hle_handler); server_session->parent = nullptr; - return MakeResult<SharedPtr<ServerSession>>(std::move(server_session)); + return MakeResult(std::move(server_session)); } bool ServerSession::ShouldWait(Thread* thread) const { @@ -68,13 +69,9 @@ ResultCode ServerSession::HandleSyncRequest() { return RESULT_SUCCESS; } -ServerSession::SessionPair ServerSession::CreateSessionPair( - const std::string& name, std::shared_ptr<Service::SessionRequestHandler> hle_handler, - SharedPtr<ClientPort> port) { - - auto server_session = - ServerSession::Create(name + "_Server", std::move(hle_handler)).MoveFrom(); - +ServerSession::SessionPair ServerSession::CreateSessionPair(const std::string& name, + SharedPtr<ClientPort> port) { + auto server_session = ServerSession::Create(name + "_Server").MoveFrom(); SharedPtr<ClientSession> client_session(new ClientSession); client_session->name = name + "_Client"; diff --git a/src/core/hle/kernel/server_session.h b/src/core/hle/kernel/server_session.h index c907d487c..28f365b9e 100644 --- a/src/core/hle/kernel/server_session.h +++ b/src/core/hle/kernel/server_session.h @@ -10,9 +10,8 @@ #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" namespace Kernel { @@ -20,6 +19,8 @@ namespace Kernel { class ClientSession; class ClientPort; class ServerSession; +class SessionRequestHandler; +class Thread; /** * Kernel object representing the server endpoint of an IPC session. Sessions are the basic CTR-OS @@ -49,14 +50,20 @@ public: /** * Creates a pair of ServerSession and an associated ClientSession. * @param name Optional name of the ports. - * @param hle_handler Optional HLE handler for this server session. * @param client_port Optional The ClientPort that spawned this session. * @return The created session tuple */ - static SessionPair CreateSessionPair( - const std::string& name = "Unknown", - std::shared_ptr<Service::SessionRequestHandler> hle_handler = nullptr, - SharedPtr<ClientPort> client_port = nullptr); + static SessionPair CreateSessionPair(const std::string& name = "Unknown", + SharedPtr<ClientPort> client_port = nullptr); + + /** + * Sets the HLE handler for the session. This handler will be called to service IPC requests + * instead of the regular IPC machinery. (The regular IPC machinery is currently not + * implemented.) + */ + void SetHleHandler(std::shared_ptr<SessionRequestHandler> hle_handler_) { + hle_handler = std::move(hle_handler_); + } /** * Handle a sync request from the emulated application. @@ -71,7 +78,7 @@ public: std::string name; ///< The name of this session (optional) bool signaled; ///< Whether there's new data available to this ServerSession std::shared_ptr<Session> parent; ///< The parent session, which links to the client endpoint. - std::shared_ptr<Service::SessionRequestHandler> + std::shared_ptr<SessionRequestHandler> hle_handler; ///< This session's HLE request handler (optional) private: @@ -82,12 +89,9 @@ private: * Creates a server session. The server session can have an optional HLE handler, * which will be invoked to handle the IPC requests that this session receives. * @param name Optional name of the server session. - * @param hle_handler Optional HLE handler for this server session. * @return The created server session */ - static ResultVal<SharedPtr<ServerSession>> Create( - std::string name = "Unknown", - std::shared_ptr<Service::SessionRequestHandler> hle_handler = nullptr); + static ResultVal<SharedPtr<ServerSession>> Create(std::string name = "Unknown"); }; /** 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 |