Core timing 2.0 (#4913)

* Core::Timing: Add multiple timer, one for each core

* revert clang-format; work on tests for CoreTiming

* Kernel:: Add support for multiple cores, asserts in HandleSyncRequest because Thread->status == WaitIPC

* Add some TRACE_LOGs

* fix tests

* make some adjustments to qt-debugger, cheats and gdbstub(probably still broken)

* Make ARM_Interface::id private, rework ARM_Interface ctor

* ReRename TimingManager to Timing for smaler diff

* addressed review comments
This commit is contained in:
Ben 2020-02-21 19:31:32 +01:00 committed by GitHub
parent e3dbdcbdff
commit 55ec7031cc
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
32 changed files with 760 additions and 535 deletions

View file

@ -83,7 +83,7 @@ bool HandleTable::IsValid(Handle handle) const {
std::shared_ptr<Object> HandleTable::GetGeneric(Handle handle) const {
if (handle == CurrentThread) {
return SharedFrom(kernel.GetThreadManager().GetCurrentThread());
return SharedFrom(kernel.GetCurrentThreadManager().GetCurrentThread());
} else if (handle == CurrentProcess) {
return kernel.GetCurrentProcess();
}

View file

@ -18,19 +18,27 @@ namespace Kernel {
/// Initialize the kernel
KernelSystem::KernelSystem(Memory::MemorySystem& memory, Core::Timing& timing,
std::function<void()> prepare_reschedule_callback, u32 system_mode)
std::function<void()> prepare_reschedule_callback, u32 system_mode,
u32 num_cores)
: memory(memory), timing(timing),
prepare_reschedule_callback(std::move(prepare_reschedule_callback)) {
MemoryInit(system_mode);
resource_limits = std::make_unique<ResourceLimitList>(*this);
thread_manager = std::make_unique<ThreadManager>(*this);
for (u32 core_id = 0; core_id < num_cores; ++core_id) {
thread_managers.push_back(std::make_unique<ThreadManager>(*this, core_id));
}
timer_manager = std::make_unique<TimerManager>(timing);
ipc_recorder = std::make_unique<IPCDebugger::Recorder>();
stored_processes.assign(num_cores, nullptr);
next_thread_id = 1;
}
/// Shutdown the kernel
KernelSystem::~KernelSystem() = default;
KernelSystem::~KernelSystem() {
ResetThreadIDs();
};
ResourceLimitList& KernelSystem::ResourceLimit() {
return *resource_limits;
@ -53,6 +61,15 @@ void KernelSystem::SetCurrentProcess(std::shared_ptr<Process> process) {
SetCurrentMemoryPageTable(&process->vm_manager.page_table);
}
void KernelSystem::SetCurrentProcessForCPU(std::shared_ptr<Process> process, u32 core_id) {
if (current_cpu->GetID() == core_id) {
current_process = process;
SetCurrentMemoryPageTable(&process->vm_manager.page_table);
} else {
stored_processes[core_id] = process;
}
}
void KernelSystem::SetCurrentMemoryPageTable(Memory::PageTable* page_table) {
memory.SetCurrentPageTable(page_table);
if (current_cpu != nullptr) {
@ -60,17 +77,39 @@ void KernelSystem::SetCurrentMemoryPageTable(Memory::PageTable* page_table) {
}
}
void KernelSystem::SetCPU(std::shared_ptr<ARM_Interface> cpu) {
void KernelSystem::SetCPUs(std::vector<std::shared_ptr<ARM_Interface>> cpus) {
ASSERT(cpus.size() == thread_managers.size());
u32 i = 0;
for (const auto& cpu : cpus) {
thread_managers[i++]->SetCPU(*cpu);
}
}
void KernelSystem::SetRunningCPU(std::shared_ptr<ARM_Interface> cpu) {
if (current_process) {
stored_processes[current_cpu->GetID()] = current_process;
}
current_cpu = cpu;
thread_manager->SetCPU(*cpu);
timing.SetCurrentTimer(cpu->GetID());
if (stored_processes[current_cpu->GetID()]) {
SetCurrentProcess(stored_processes[current_cpu->GetID()]);
}
}
ThreadManager& KernelSystem::GetThreadManager() {
return *thread_manager;
ThreadManager& KernelSystem::GetThreadManager(u32 core_id) {
return *thread_managers[core_id];
}
const ThreadManager& KernelSystem::GetThreadManager() const {
return *thread_manager;
const ThreadManager& KernelSystem::GetThreadManager(u32 core_id) const {
return *thread_managers[core_id];
}
ThreadManager& KernelSystem::GetCurrentThreadManager() {
return *thread_managers[current_cpu->GetID()];
}
const ThreadManager& KernelSystem::GetCurrentThreadManager() const {
return *thread_managers[current_cpu->GetID()];
}
TimerManager& KernelSystem::GetTimerManager() {
@ -101,4 +140,12 @@ void KernelSystem::AddNamedPort(std::string name, std::shared_ptr<ClientPort> po
named_ports.emplace(std::move(name), std::move(port));
}
u32 KernelSystem::NewThreadId() {
return next_thread_id++;
}
void KernelSystem::ResetThreadIDs() {
next_thread_id = 0;
}
} // namespace Kernel

View file

@ -85,7 +85,8 @@ enum class MemoryRegion : u16 {
class KernelSystem {
public:
explicit KernelSystem(Memory::MemorySystem& memory, Core::Timing& timing,
std::function<void()> prepare_reschedule_callback, u32 system_mode);
std::function<void()> prepare_reschedule_callback, u32 system_mode,
u32 num_cores);
~KernelSystem();
using PortPair = std::pair<std::shared_ptr<ServerPort>, std::shared_ptr<ClientPort>>;
@ -210,13 +211,19 @@ public:
std::shared_ptr<Process> GetCurrentProcess() const;
void SetCurrentProcess(std::shared_ptr<Process> process);
void SetCurrentProcessForCPU(std::shared_ptr<Process> process, u32 core_id);
void SetCurrentMemoryPageTable(Memory::PageTable* page_table);
void SetCPU(std::shared_ptr<ARM_Interface> cpu);
void SetCPUs(std::vector<std::shared_ptr<ARM_Interface>> cpu);
ThreadManager& GetThreadManager();
const ThreadManager& GetThreadManager() const;
void SetRunningCPU(std::shared_ptr<ARM_Interface> cpu);
ThreadManager& GetThreadManager(u32 core_id);
const ThreadManager& GetThreadManager(u32 core_id) const;
ThreadManager& GetCurrentThreadManager();
const ThreadManager& GetCurrentThreadManager() const;
TimerManager& GetTimerManager();
const TimerManager& GetTimerManager() const;
@ -242,6 +249,10 @@ public:
prepare_reschedule_callback();
}
u32 NewThreadId();
void ResetThreadIDs();
/// Map of named ports managed by the kernel, which can be retrieved using the ConnectToPort
std::unordered_map<std::string, std::shared_ptr<ClientPort>> named_ports;
@ -276,13 +287,16 @@ private:
std::vector<std::shared_ptr<Process>> process_list;
std::shared_ptr<Process> current_process;
std::vector<std::shared_ptr<Process>> stored_processes;
std::unique_ptr<ThreadManager> thread_manager;
std::vector<std::unique_ptr<ThreadManager>> thread_managers;
std::unique_ptr<ConfigMem::Handler> config_mem_handler;
std::unique_ptr<SharedPage::Handler> shared_page_handler;
std::unique_ptr<IPCDebugger::Recorder> ipc_recorder;
u32 next_thread_id;
};
} // namespace Kernel

View file

@ -35,7 +35,7 @@ std::shared_ptr<Mutex> KernelSystem::CreateMutex(bool initial_locked, std::strin
// Acquire mutex with current thread if initialized as locked
if (initial_locked)
mutex->Acquire(thread_manager->GetCurrentThread());
mutex->Acquire(thread_managers[current_cpu->GetID()]->GetCurrentThread());
return mutex;
}

View file

@ -56,7 +56,7 @@ Handler::Handler(Core::Timing& timing) : timing(timing) {
using namespace std::placeholders;
update_time_event = timing.RegisterEvent("SharedPage::UpdateTimeCallback",
std::bind(&Handler::UpdateTimeCallback, this, _1, _2));
timing.ScheduleEvent(0, update_time_event);
timing.ScheduleEvent(0, update_time_event, 0, 0);
float slidestate = Settings::values.factor_3d / 100.0f;
shared_page.sliderstate_3d = static_cast<float_le>(slidestate);

View file

@ -280,12 +280,12 @@ void SVC::ExitProcess() {
current_process->status = ProcessStatus::Exited;
// Stop all the process threads that are currently waiting for objects.
auto& thread_list = kernel.GetThreadManager().GetThreadList();
auto& thread_list = kernel.GetCurrentThreadManager().GetThreadList();
for (auto& thread : thread_list) {
if (thread->owner_process != current_process.get())
continue;
if (thread.get() == kernel.GetThreadManager().GetCurrentThread())
if (thread.get() == kernel.GetCurrentThreadManager().GetCurrentThread())
continue;
// TODO(Subv): When are the other running/ready threads terminated?
@ -297,7 +297,7 @@ void SVC::ExitProcess() {
}
// Kill the current thread
kernel.GetThreadManager().GetCurrentThread()->Stop();
kernel.GetCurrentThreadManager().GetCurrentThread()->Stop();
system.PrepareReschedule();
}
@ -388,7 +388,7 @@ ResultCode SVC::SendSyncRequest(Handle handle) {
system.PrepareReschedule();
auto thread = SharedFrom(kernel.GetThreadManager().GetCurrentThread());
auto thread = SharedFrom(kernel.GetCurrentThreadManager().GetCurrentThread());
if (kernel.GetIPCRecorder().IsEnabled()) {
kernel.GetIPCRecorder().RegisterRequest(session, thread);
@ -406,7 +406,7 @@ ResultCode SVC::CloseHandle(Handle handle) {
/// Wait for a handle to synchronize, timeout after the specified nanoseconds
ResultCode SVC::WaitSynchronization1(Handle handle, s64 nano_seconds) {
auto object = kernel.GetCurrentProcess()->handle_table.Get<WaitObject>(handle);
Thread* thread = kernel.GetThreadManager().GetCurrentThread();
Thread* thread = kernel.GetCurrentThreadManager().GetCurrentThread();
if (object == nullptr)
return ERR_INVALID_HANDLE;
@ -458,7 +458,7 @@ ResultCode SVC::WaitSynchronization1(Handle handle, s64 nano_seconds) {
/// Wait for the given handles to synchronize, timeout after the specified nanoseconds
ResultCode SVC::WaitSynchronizationN(s32* out, VAddr handles_address, s32 handle_count,
bool wait_all, s64 nano_seconds) {
Thread* thread = kernel.GetThreadManager().GetCurrentThread();
Thread* thread = kernel.GetCurrentThreadManager().GetCurrentThread();
if (!Memory::IsValidVirtualAddress(*kernel.GetCurrentProcess(), handles_address))
return ERR_INVALID_POINTER;
@ -654,7 +654,7 @@ ResultCode SVC::ReplyAndReceive(s32* index, VAddr handles_address, s32 handle_co
// We are also sending a command reply.
// Do not send a reply if the command id in the command buffer is 0xFFFF.
Thread* thread = kernel.GetThreadManager().GetCurrentThread();
Thread* thread = kernel.GetCurrentThreadManager().GetCurrentThread();
u32 cmd_buff_header = memory.Read32(thread->GetCommandBufferAddress());
IPC::Header header{cmd_buff_header};
if (reply_target != 0 && header.command_id != 0xFFFF) {
@ -776,7 +776,7 @@ ResultCode SVC::ArbitrateAddress(Handle handle, u32 address, u32 type, u32 value
return ERR_INVALID_HANDLE;
auto res =
arbiter->ArbitrateAddress(SharedFrom(kernel.GetThreadManager().GetCurrentThread()),
arbiter->ArbitrateAddress(SharedFrom(kernel.GetCurrentThreadManager().GetCurrentThread()),
static_cast<ArbitrationType>(type), address, value, nanoseconds);
// TODO(Subv): Identify in which specific cases this call should cause a reschedule.
@ -897,14 +897,19 @@ ResultCode SVC::CreateThread(Handle* out_handle, u32 entry_point, u32 arg, VAddr
break;
case ThreadProcessorIdAll:
LOG_INFO(Kernel_SVC,
"Newly created thread is allowed to be run in any Core, unimplemented.");
"Newly created thread is allowed to be run in any Core, for now run in core 0.");
processor_id = ThreadProcessorId0;
break;
case ThreadProcessorId1:
LOG_ERROR(Kernel_SVC,
"Newly created thread must run in the SysCore (Core1), unimplemented.");
case ThreadProcessorId2:
case ThreadProcessorId3:
// TODO: Check and log for: When processorid==0x2 and the process is not a BASE mem-region
// process, exheader kernel-flags bitmask 0x2000 must be set (otherwise error 0xD9001BEA is
// returned). When processorid==0x3 and the process is not a BASE mem-region process, error
// 0xD9001BEA is returned. These are the only restriction checks done by the kernel for
// processorid.
break;
default:
// TODO(bunnei): Implement support for other processor IDs
ASSERT_MSG(false, "Unsupported thread processor ID: {}", processor_id);
break;
}
@ -930,9 +935,9 @@ ResultCode SVC::CreateThread(Handle* out_handle, u32 entry_point, u32 arg, VAddr
/// Called when a thread exits
void SVC::ExitThread() {
LOG_TRACE(Kernel_SVC, "called, pc=0x{:08X}", system.CPU().GetPC());
LOG_TRACE(Kernel_SVC, "called, pc=0x{:08X}", system.GetRunningCore().GetPC());
kernel.GetThreadManager().ExitCurrentThread();
kernel.GetCurrentThreadManager().ExitCurrentThread();
system.PrepareReschedule();
}
@ -978,7 +983,7 @@ ResultCode SVC::SetThreadPriority(Handle handle, u32 priority) {
/// Create a mutex
ResultCode SVC::CreateMutex(Handle* out_handle, u32 initial_locked) {
std::shared_ptr<Mutex> mutex = kernel.CreateMutex(initial_locked != 0);
mutex->name = fmt::format("mutex-{:08x}", system.CPU().GetReg(14));
mutex->name = fmt::format("mutex-{:08x}", system.GetRunningCore().GetReg(14));
CASCADE_RESULT(*out_handle, kernel.GetCurrentProcess()->handle_table.Create(std::move(mutex)));
LOG_TRACE(Kernel_SVC, "called initial_locked={} : created handle=0x{:08X}",
@ -995,7 +1000,7 @@ ResultCode SVC::ReleaseMutex(Handle handle) {
if (mutex == nullptr)
return ERR_INVALID_HANDLE;
return mutex->Release(kernel.GetThreadManager().GetCurrentThread());
return mutex->Release(kernel.GetCurrentThreadManager().GetCurrentThread());
}
/// Get the ID of the specified process
@ -1045,7 +1050,7 @@ ResultCode SVC::GetThreadId(u32* thread_id, Handle handle) {
ResultCode SVC::CreateSemaphore(Handle* out_handle, s32 initial_count, s32 max_count) {
CASCADE_RESULT(std::shared_ptr<Semaphore> semaphore,
kernel.CreateSemaphore(initial_count, max_count));
semaphore->name = fmt::format("semaphore-{:08x}", system.CPU().GetReg(14));
semaphore->name = fmt::format("semaphore-{:08x}", system.GetRunningCore().GetReg(14));
CASCADE_RESULT(*out_handle,
kernel.GetCurrentProcess()->handle_table.Create(std::move(semaphore)));
@ -1115,8 +1120,9 @@ ResultCode SVC::QueryMemory(MemoryInfo* memory_info, PageInfo* page_info, u32 ad
/// Create an event
ResultCode SVC::CreateEvent(Handle* out_handle, u32 reset_type) {
std::shared_ptr<Event> evt = kernel.CreateEvent(
static_cast<ResetType>(reset_type), fmt::format("event-{:08x}", system.CPU().GetReg(14)));
std::shared_ptr<Event> evt =
kernel.CreateEvent(static_cast<ResetType>(reset_type),
fmt::format("event-{:08x}", system.GetRunningCore().GetReg(14)));
CASCADE_RESULT(*out_handle, kernel.GetCurrentProcess()->handle_table.Create(std::move(evt)));
LOG_TRACE(Kernel_SVC, "called reset_type=0x{:08X} : created handle=0x{:08X}", reset_type,
@ -1158,8 +1164,9 @@ ResultCode SVC::ClearEvent(Handle handle) {
/// Creates a timer
ResultCode SVC::CreateTimer(Handle* out_handle, u32 reset_type) {
std::shared_ptr<Timer> timer = kernel.CreateTimer(
static_cast<ResetType>(reset_type), fmt ::format("timer-{:08x}", system.CPU().GetReg(14)));
std::shared_ptr<Timer> timer =
kernel.CreateTimer(static_cast<ResetType>(reset_type),
fmt ::format("timer-{:08x}", system.GetRunningCore().GetReg(14)));
CASCADE_RESULT(*out_handle, kernel.GetCurrentProcess()->handle_table.Create(std::move(timer)));
LOG_TRACE(Kernel_SVC, "called reset_type=0x{:08X} : created handle=0x{:08X}", reset_type,
@ -1213,7 +1220,7 @@ ResultCode SVC::CancelTimer(Handle handle) {
void SVC::SleepThread(s64 nanoseconds) {
LOG_TRACE(Kernel_SVC, "called nanoseconds={}", nanoseconds);
ThreadManager& thread_manager = kernel.GetThreadManager();
ThreadManager& thread_manager = kernel.GetCurrentThreadManager();
// Don't attempt to yield execution if there are no available threads to run,
// this way we avoid a useless reschedule to the idle thread.
@ -1231,10 +1238,11 @@ void SVC::SleepThread(s64 nanoseconds) {
/// This returns the total CPU ticks elapsed since the CPU was powered-on
s64 SVC::GetSystemTick() {
s64 result = system.CoreTiming().GetTicks();
// TODO: Use globalTicks here?
s64 result = system.GetRunningCore().GetTimer()->GetTicks();
// Advance time to defeat dumb games (like Cubic Ninja) that busy-wait for the frame to end.
// Measured time between two calls on a 9.2 o3DS with Ninjhax 1.1b
system.CoreTiming().AddTicks(150);
system.GetRunningCore().GetTimer()->AddTicks(150);
return result;
}
@ -1596,11 +1604,11 @@ void SVC::CallSVC(u32 immediate) {
SVC::SVC(Core::System& system) : system(system), kernel(system.Kernel()), memory(system.Memory()) {}
u32 SVC::GetReg(std::size_t n) {
return system.CPU().GetReg(static_cast<int>(n));
return system.GetRunningCore().GetReg(static_cast<int>(n));
}
void SVC::SetReg(std::size_t n, u32 value) {
system.CPU().SetReg(static_cast<int>(n), value);
system.GetRunningCore().SetReg(static_cast<int>(n), value);
}
SVCContext::SVCContext(Core::System& system) : impl(std::make_unique<SVC>(system)) {}

View file

@ -33,13 +33,9 @@ void Thread::Acquire(Thread* thread) {
ASSERT_MSG(!ShouldWait(thread), "object unavailable!");
}
u32 ThreadManager::NewThreadId() {
return next_thread_id++;
}
Thread::Thread(KernelSystem& kernel)
: WaitObject(kernel), context(kernel.GetThreadManager().NewContext()),
thread_manager(kernel.GetThreadManager()) {}
Thread::Thread(KernelSystem& kernel, u32 core_id)
: WaitObject(kernel), context(kernel.GetThreadManager(core_id).NewContext()),
thread_manager(kernel.GetThreadManager(core_id)) {}
Thread::~Thread() {}
Thread* ThreadManager::GetCurrentThread() const {
@ -84,7 +80,7 @@ void ThreadManager::SwitchContext(Thread* new_thread) {
// Save context for previous thread
if (previous_thread) {
previous_thread->last_running_ticks = timing.GetTicks();
previous_thread->last_running_ticks = timing.GetGlobalTicks();
cpu->SaveContext(previous_thread->context);
if (previous_thread->status == ThreadStatus::Running) {
@ -111,7 +107,7 @@ void ThreadManager::SwitchContext(Thread* new_thread) {
new_thread->status = ThreadStatus::Running;
if (previous_process.get() != current_thread->owner_process) {
kernel.SetCurrentProcess(SharedFrom(current_thread->owner_process));
kernel.SetCurrentProcessForCPU(SharedFrom(current_thread->owner_process), cpu->GetID());
}
cpu->LoadContext(new_thread->context);
@ -124,7 +120,7 @@ void ThreadManager::SwitchContext(Thread* new_thread) {
}
Thread* ThreadManager::PopNextReadyThread() {
Thread* next;
Thread* next = nullptr;
Thread* thread = GetCurrentThread();
if (thread && thread->status == ThreadStatus::Running) {
@ -309,22 +305,22 @@ ResultVal<std::shared_ptr<Thread>> KernelSystem::CreateThread(std::string name,
ErrorSummary::InvalidArgument, ErrorLevel::Permanent);
}
auto thread{std::make_shared<Thread>(*this)};
auto thread{std::make_shared<Thread>(*this, processor_id)};
thread_manager->thread_list.push_back(thread);
thread_manager->ready_queue.prepare(priority);
thread_managers[processor_id]->thread_list.push_back(thread);
thread_managers[processor_id]->ready_queue.prepare(priority);
thread->thread_id = thread_manager->NewThreadId();
thread->thread_id = NewThreadId();
thread->status = ThreadStatus::Dormant;
thread->entry_point = entry_point;
thread->stack_top = stack_top;
thread->nominal_priority = thread->current_priority = priority;
thread->last_running_ticks = timing.GetTicks();
thread->last_running_ticks = timing.GetGlobalTicks();
thread->processor_id = processor_id;
thread->wait_objects.clear();
thread->wait_address = 0;
thread->name = std::move(name);
thread_manager->wakeup_callback_table[thread->thread_id] = thread.get();
thread_managers[processor_id]->wakeup_callback_table[thread->thread_id] = thread.get();
thread->owner_process = &owner_process;
// Find the next available TLS index, and mark it as used
@ -369,7 +365,7 @@ ResultVal<std::shared_ptr<Thread>> KernelSystem::CreateThread(std::string name,
// to initialize the context
ResetThreadContext(thread->context, stack_top, entry_point, arg);
thread_manager->ready_queue.push_back(thread->current_priority, thread.get());
thread_managers[processor_id]->ready_queue.push_back(thread->current_priority, thread.get());
thread->status = ThreadStatus::Ready;
return MakeResult<std::shared_ptr<Thread>>(std::move(thread));
@ -435,6 +431,9 @@ void ThreadManager::Reschedule() {
LOG_TRACE(Kernel, "context switch {} -> idle", cur->GetObjectId());
} else if (next) {
LOG_TRACE(Kernel, "context switch idle -> {}", next->GetObjectId());
} else {
LOG_TRACE(Kernel, "context switch idle -> idle, do nothing");
return;
}
SwitchContext(next);
@ -461,11 +460,10 @@ VAddr Thread::GetCommandBufferAddress() const {
return GetTLSAddress() + command_header_offset;
}
ThreadManager::ThreadManager(Kernel::KernelSystem& kernel) : kernel(kernel) {
ThreadWakeupEventType =
kernel.timing.RegisterEvent("ThreadWakeupCallback", [this](u64 thread_id, s64 cycle_late) {
ThreadWakeupCallback(thread_id, cycle_late);
});
ThreadManager::ThreadManager(Kernel::KernelSystem& kernel, u32 core_id) : kernel(kernel) {
ThreadWakeupEventType = kernel.timing.RegisterEvent(
"ThreadWakeupCallback_" + std::to_string(core_id),
[this](u64 thread_id, s64 cycle_late) { ThreadWakeupCallback(thread_id, cycle_late); });
}
ThreadManager::~ThreadManager() {

View file

@ -34,7 +34,9 @@ enum ThreadProcessorId : s32 {
ThreadProcessorIdAll = -1, ///< Run thread on either core
ThreadProcessorId0 = 0, ///< Run thread on core 0 (AppCore)
ThreadProcessorId1 = 1, ///< Run thread on core 1 (SysCore)
ThreadProcessorIdMax = 2, ///< Processor ID must be less than this
ThreadProcessorId2 = 2, ///< Run thread on core 2 (additional n3ds core)
ThreadProcessorId3 = 3, ///< Run thread on core 3 (additional n3ds core)
ThreadProcessorIdMax = 4, ///< Processor ID must be less than this
};
enum class ThreadStatus {
@ -57,15 +59,9 @@ enum class ThreadWakeupReason {
class ThreadManager {
public:
explicit ThreadManager(Kernel::KernelSystem& kernel);
explicit ThreadManager(Kernel::KernelSystem& kernel, u32 core_id);
~ThreadManager();
/**
* Creates a new thread ID
* @return The new thread ID
*/
u32 NewThreadId();
/**
* Gets the current thread
*/
@ -132,7 +128,6 @@ private:
Kernel::KernelSystem& kernel;
ARM_Interface* cpu;
u32 next_thread_id = 1;
std::shared_ptr<Thread> current_thread;
Common::ThreadQueueList<Thread*, ThreadPrioLowest + 1> ready_queue;
std::unordered_map<u64, Thread*> wakeup_callback_table;
@ -149,7 +144,7 @@ private:
class Thread final : public WaitObject {
public:
explicit Thread(KernelSystem&);
explicit Thread(KernelSystem&, u32 core_id);
~Thread() override;
std::string GetName() const override {