2014-12-17 06:38:14 +01:00
|
|
|
// Copyright 2014 Citra Emulator Project
|
|
|
|
// Licensed under GPLv2 or any later version
|
2014-11-19 09:49:13 +01:00
|
|
|
// Refer to the license.txt file included.
|
2014-05-10 04:11:18 +02:00
|
|
|
|
2014-12-04 01:55:45 +01:00
|
|
|
#include <algorithm>
|
2015-05-06 09:06:12 +02:00
|
|
|
#include "common/assert.h"
|
|
|
|
#include "common/logging/log.h"
|
2015-08-06 02:26:52 +02:00
|
|
|
#include "core/hle/config_mem.h"
|
2016-09-21 08:52:38 +02:00
|
|
|
#include "core/hle/kernel/kernel.h"
|
2015-08-06 02:26:52 +02:00
|
|
|
#include "core/hle/kernel/memory.h"
|
2015-05-04 05:01:16 +02:00
|
|
|
#include "core/hle/kernel/process.h"
|
2015-08-06 02:26:52 +02:00
|
|
|
#include "core/hle/kernel/resource_limit.h"
|
2014-05-10 04:11:18 +02:00
|
|
|
#include "core/hle/kernel/thread.h"
|
2014-12-04 20:45:47 +01:00
|
|
|
#include "core/hle/kernel/timer.h"
|
2015-08-06 02:26:52 +02:00
|
|
|
#include "core/hle/shared_page.h"
|
2014-05-10 04:11:18 +02:00
|
|
|
|
2014-05-21 00:13:25 +02:00
|
|
|
namespace Kernel {
|
2014-05-10 04:11:18 +02:00
|
|
|
|
2015-04-28 04:12:35 +02:00
|
|
|
unsigned int Object::next_object_id;
|
2014-12-14 00:16:13 +01:00
|
|
|
HandleTable g_handle_table;
|
2014-05-10 04:11:18 +02:00
|
|
|
|
2015-02-01 02:26:16 +01:00
|
|
|
void WaitObject::AddWaitingThread(SharedPtr<Thread> thread) {
|
2015-01-15 05:19:22 +01:00
|
|
|
auto itr = std::find(waiting_threads.begin(), waiting_threads.end(), thread);
|
|
|
|
if (itr == waiting_threads.end())
|
2015-02-01 02:26:16 +01:00
|
|
|
waiting_threads.push_back(std::move(thread));
|
2015-01-15 05:19:22 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
void WaitObject::RemoveWaitingThread(Thread* thread) {
|
|
|
|
auto itr = std::find(waiting_threads.begin(), waiting_threads.end(), thread);
|
|
|
|
if (itr != waiting_threads.end())
|
|
|
|
waiting_threads.erase(itr);
|
2015-01-15 01:22:50 +01:00
|
|
|
}
|
|
|
|
|
2016-12-04 04:38:14 +01:00
|
|
|
SharedPtr<Thread> WaitObject::GetHighestPriorityReadyThread() {
|
|
|
|
// Remove the threads that are ready or already running from our waitlist
|
2016-12-04 15:58:36 +01:00
|
|
|
waiting_threads.erase(std::remove_if(waiting_threads.begin(), waiting_threads.end(), [](const SharedPtr<Thread>& thread) -> bool {
|
2016-12-04 04:38:14 +01:00
|
|
|
return thread->status == THREADSTATUS_RUNNING || thread->status == THREADSTATUS_READY;
|
|
|
|
}), waiting_threads.end());
|
|
|
|
|
|
|
|
if (waiting_threads.empty())
|
|
|
|
return nullptr;
|
|
|
|
|
2016-12-07 01:15:32 +01:00
|
|
|
SharedPtr<Thread> candidate = nullptr;
|
|
|
|
s32 candidate_priority = THREADPRIO_LOWEST + 1;
|
2016-12-04 04:38:14 +01:00
|
|
|
|
2016-12-07 01:15:32 +01:00
|
|
|
for (const auto& thread : waiting_threads) {
|
|
|
|
if (thread->current_priority >= candidate_priority)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
bool ready_to_run = std::none_of(thread->wait_objects.begin(), thread->wait_objects.end(), [](const SharedPtr<WaitObject>& object) {
|
2016-12-04 15:58:36 +01:00
|
|
|
return object->ShouldWait();
|
|
|
|
});
|
2016-12-07 01:15:32 +01:00
|
|
|
if (ready_to_run) {
|
|
|
|
candidate = thread;
|
|
|
|
candidate_priority = thread->current_priority;
|
|
|
|
}
|
|
|
|
}
|
2016-12-04 04:38:14 +01:00
|
|
|
|
2016-12-07 01:15:32 +01:00
|
|
|
return candidate;
|
2016-12-04 04:38:14 +01:00
|
|
|
}
|
|
|
|
|
2015-01-18 19:25:51 +01:00
|
|
|
void WaitObject::WakeupAllWaitingThreads() {
|
2016-12-04 04:38:14 +01:00
|
|
|
// Wake up all threads that can be awoken, in priority order
|
|
|
|
while (auto thread = GetHighestPriorityReadyThread()) {
|
|
|
|
if (thread->wait_objects.empty()) {
|
|
|
|
Acquire();
|
|
|
|
// 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();
|
|
|
|
// Remove the thread from the object's waitlist
|
|
|
|
object->RemoveWaitingThread(thread.get());
|
|
|
|
}
|
|
|
|
// Note: This case doesn't update the output index of WaitSynchronizationN.
|
|
|
|
// Clear the thread's waitlist
|
|
|
|
thread->wait_objects.clear();
|
|
|
|
}
|
|
|
|
|
|
|
|
// Set the result of the call to WaitSynchronization to RESULT_SUCCESS
|
|
|
|
thread->SetWaitSynchronizationResult(RESULT_SUCCESS);
|
2015-06-08 05:39:37 +02:00
|
|
|
thread->ResumeFromWait();
|
2016-12-04 04:38:14 +01:00
|
|
|
// Note: Removing the thread from the object's waitlist will be done by GetHighestPriorityReadyThread
|
|
|
|
}
|
2015-01-15 01:22:50 +01:00
|
|
|
}
|
|
|
|
|
2016-04-08 18:28:54 +02:00
|
|
|
const std::vector<SharedPtr<Thread>>& WaitObject::GetWaitingThreads() const {
|
|
|
|
return waiting_threads;
|
|
|
|
}
|
|
|
|
|
2014-12-14 00:16:13 +01:00
|
|
|
HandleTable::HandleTable() {
|
2014-12-21 13:04:08 +01:00
|
|
|
next_generation = 1;
|
|
|
|
Clear();
|
2014-05-10 04:11:18 +02:00
|
|
|
}
|
|
|
|
|
2014-12-29 13:55:30 +01:00
|
|
|
ResultVal<Handle> HandleTable::Create(SharedPtr<Object> obj) {
|
2015-01-21 02:16:47 +01:00
|
|
|
DEBUG_ASSERT(obj != nullptr);
|
2014-12-21 13:04:08 +01:00
|
|
|
|
|
|
|
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;
|
2014-05-10 04:11:18 +02:00
|
|
|
}
|
2014-12-21 13:04:08 +01:00
|
|
|
next_free_slot = generations[slot];
|
2014-05-10 04:11:18 +02:00
|
|
|
|
2014-12-21 13:04:08 +01:00
|
|
|
u16 generation = next_generation++;
|
2014-05-10 04:11:18 +02:00
|
|
|
|
2014-12-21 13:04:08 +01:00
|
|
|
// 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.
|
2016-09-18 02:38:01 +02:00
|
|
|
if (next_generation >= (1 << 15))
|
|
|
|
next_generation = 1;
|
2014-12-21 13:04:08 +01:00
|
|
|
|
2014-12-29 13:55:30 +01:00
|
|
|
generations[slot] = generation;
|
|
|
|
objects[slot] = std::move(obj);
|
|
|
|
|
2015-01-31 17:57:32 +01:00
|
|
|
Handle handle = generation | (slot << 15);
|
2014-12-21 13:04:08 +01:00
|
|
|
return MakeResult<Handle>(handle);
|
2014-05-10 04:11:18 +02:00
|
|
|
}
|
|
|
|
|
2014-12-21 13:04:08 +01:00
|
|
|
ResultVal<Handle> HandleTable::Duplicate(Handle handle) {
|
2014-12-29 13:55:30 +01:00
|
|
|
SharedPtr<Object> object = GetGeneric(handle);
|
2014-12-21 13:04:08 +01:00
|
|
|
if (object == nullptr) {
|
|
|
|
LOG_ERROR(Kernel, "Tried to duplicate invalid handle: %08X", handle);
|
|
|
|
return ERR_INVALID_HANDLE;
|
2014-05-10 04:11:18 +02:00
|
|
|
}
|
2014-12-29 13:55:30 +01:00
|
|
|
return Create(std::move(object));
|
2014-05-10 04:11:18 +02:00
|
|
|
}
|
|
|
|
|
2014-12-21 13:04:08 +01:00
|
|
|
ResultCode HandleTable::Close(Handle handle) {
|
|
|
|
if (!IsValid(handle))
|
|
|
|
return ERR_INVALID_HANDLE;
|
|
|
|
|
2015-02-01 21:31:21 +01:00
|
|
|
u16 slot = GetSlot(handle);
|
2014-12-21 13:04:08 +01:00
|
|
|
|
|
|
|
objects[slot] = nullptr;
|
|
|
|
|
2015-01-31 19:12:20 +01:00
|
|
|
generations[slot] = next_free_slot;
|
2014-12-21 13:04:08 +01:00
|
|
|
next_free_slot = slot;
|
|
|
|
return RESULT_SUCCESS;
|
2014-05-10 04:11:18 +02:00
|
|
|
}
|
|
|
|
|
2014-12-21 13:04:08 +01:00
|
|
|
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;
|
2014-05-10 04:11:18 +02:00
|
|
|
}
|
|
|
|
|
2014-12-29 13:55:30 +01:00
|
|
|
SharedPtr<Object> HandleTable::GetGeneric(Handle handle) const {
|
2014-12-21 13:04:08 +01:00
|
|
|
if (handle == CurrentThread) {
|
2014-12-22 07:32:03 +01:00
|
|
|
return GetCurrentThread();
|
2014-12-21 13:04:08 +01:00
|
|
|
} else if (handle == CurrentProcess) {
|
2015-05-11 16:15:10 +02:00
|
|
|
return g_current_process;
|
2014-12-21 13:04:08 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
if (!IsValid(handle)) {
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
return objects[GetSlot(handle)];
|
2014-05-10 04:11:18 +02:00
|
|
|
}
|
|
|
|
|
2014-12-21 13:04:08 +01:00
|
|
|
void HandleTable::Clear() {
|
2015-02-01 21:31:21 +01:00
|
|
|
for (u16 i = 0; i < MAX_COUNT; ++i) {
|
2014-12-21 13:04:08 +01:00
|
|
|
generations[i] = i + 1;
|
|
|
|
objects[i] = nullptr;
|
|
|
|
}
|
|
|
|
next_free_slot = 0;
|
2014-05-10 04:11:18 +02:00
|
|
|
}
|
2014-05-14 03:57:12 +02:00
|
|
|
|
2014-06-11 04:43:50 +02:00
|
|
|
/// Initialize the kernel
|
2016-11-20 02:40:04 +01:00
|
|
|
void Init(u32 system_mode) {
|
2015-08-06 02:26:52 +02:00
|
|
|
ConfigMem::Init();
|
|
|
|
SharedPage::Init();
|
|
|
|
|
2016-11-20 02:40:04 +01:00
|
|
|
Kernel::MemoryInit(system_mode);
|
2015-08-06 02:26:52 +02:00
|
|
|
|
2015-05-12 22:25:15 +02:00
|
|
|
Kernel::ResourceLimitsInit();
|
2014-05-21 01:37:46 +02:00
|
|
|
Kernel::ThreadingInit();
|
2014-12-04 20:45:47 +01:00
|
|
|
Kernel::TimersInit();
|
2015-04-28 04:12:35 +02:00
|
|
|
|
|
|
|
Object::next_object_id = 0;
|
2015-05-12 01:23:45 +02:00
|
|
|
// TODO(Subv): Start the process ids from 10 for now, as lower PIDs are
|
|
|
|
// reserved for low-level services
|
|
|
|
Process::next_process_id = 10;
|
2014-05-21 00:13:25 +02:00
|
|
|
}
|
|
|
|
|
2014-06-11 04:43:50 +02:00
|
|
|
/// Shutdown the kernel
|
2014-05-21 00:13:25 +02:00
|
|
|
void Shutdown() {
|
2015-08-06 02:26:52 +02:00
|
|
|
g_handle_table.Clear(); // Free all kernel objects
|
|
|
|
|
2014-05-21 01:37:46 +02:00
|
|
|
Kernel::ThreadingShutdown();
|
2015-08-06 02:26:52 +02:00
|
|
|
g_current_process = nullptr;
|
|
|
|
|
2014-12-04 20:45:47 +01:00
|
|
|
Kernel::TimersShutdown();
|
2015-05-12 22:25:15 +02:00
|
|
|
Kernel::ResourceLimitsShutdown();
|
2015-08-06 02:26:52 +02:00
|
|
|
Kernel::MemoryShutdown();
|
2014-05-14 03:57:12 +02:00
|
|
|
}
|
2014-05-23 01:06:12 +02:00
|
|
|
|
|
|
|
} // namespace
|