committed by
GitHub
27 changed files with 430 additions and 344 deletions
-
1src/citra_qt/debugger/wait_tree.cpp
-
4src/citra_qt/debugger/wait_tree.h
-
4src/core/CMakeLists.txt
-
2src/core/hle/ipc_helpers.h
-
1src/core/hle/kernel/address_arbiter.h
-
1src/core/hle/kernel/client_port.h
-
3src/core/hle/kernel/client_session.h
-
1src/core/hle/kernel/event.h
-
97src/core/hle/kernel/handle_table.cpp
-
126src/core/hle/kernel/handle_table.h
-
164src/core/hle/kernel/kernel.cpp
-
181src/core/hle/kernel/kernel.h
-
1src/core/hle/kernel/memory.cpp
-
1src/core/hle/kernel/mutex.h
-
1src/core/hle/kernel/resource_limit.cpp
-
2src/core/hle/kernel/semaphore.h
-
1src/core/hle/kernel/server_port.h
-
3src/core/hle/kernel/server_session.h
-
1src/core/hle/kernel/thread.cpp
-
1src/core/hle/kernel/thread.h
-
1src/core/hle/kernel/timer.cpp
-
1src/core/hle/kernel/timer.h
-
99src/core/hle/kernel/wait_object.cpp
-
67src/core/hle/kernel/wait_object.h
-
2src/core/hle/service/apt/apt.h
-
1src/core/hle/service/service.h
-
7src/core/hle/svc.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
|
|||
@ -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 |
|||
@ -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
|
|||
@ -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 |
|||
Write
Preview
Loading…
Cancel
Save
Reference in new issue