diff options
Diffstat (limited to 'src/core/hle')
24 files changed, 424 insertions, 341 deletions
| 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; | 
