diff --git a/libraries/libmesosphere/include/mesosphere/kern_k_worker_task_manager.hpp b/libraries/libmesosphere/include/mesosphere/kern_k_worker_task_manager.hpp
new file mode 100644
index 000000000..6e26d6af9
--- /dev/null
+++ b/libraries/libmesosphere/include/mesosphere/kern_k_worker_task_manager.hpp
@@ -0,0 +1,51 @@
+/*
+ * Copyright (c) 2018-2020 Atmosphère-NX
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+#pragma once
+#include
+#include
+#include
+
+namespace ams::kern {
+
+ class KWorkerTaskManager {
+ public:
+ static constexpr s32 ExitWorkerPriority = 11;
+
+ enum WorkerType {
+ WorkerType_Exit,
+
+ WorkerType_Count,
+ };
+ private:
+ KWorkerTask *head_task;
+ KWorkerTask *tail_task;
+ KThread *thread;
+ WorkerType type;
+ bool active;
+ private:
+ static void ThreadFunction(uintptr_t arg);
+ void ThreadFunctionImpl();
+
+ KWorkerTask *GetTask();
+ void AddTask(KWorkerTask *task);
+ public:
+ constexpr KWorkerTaskManager() : head_task(), tail_task(), thread(), type(WorkerType_Count), active() { /* ... */ }
+
+ NOINLINE void Initialize(WorkerType wt, s32 priority);
+ static void AddTask(WorkerType type, KWorkerTask *task);
+ };
+
+}
diff --git a/libraries/libmesosphere/include/mesosphere/kern_kernel.hpp b/libraries/libmesosphere/include/mesosphere/kern_kernel.hpp
index 2ba8599f1..8f12783c9 100644
--- a/libraries/libmesosphere/include/mesosphere/kern_kernel.hpp
+++ b/libraries/libmesosphere/include/mesosphere/kern_kernel.hpp
@@ -20,6 +20,7 @@
#include
#include
#include
+#include
namespace ams::kern {
@@ -69,6 +70,7 @@ namespace ams::kern {
static KMemoryBlockSlabManager s_sys_memory_block_manager;
static KBlockInfoManager s_block_info_manager;
static KSupervisorPageTable s_supervisor_page_table;
+ static KWorkerTaskManager s_worker_task_managers[KWorkerTaskManager::WorkerType_Count];
private:
static ALWAYS_INLINE KCoreLocalContext &GetCoreLocalContext() {
return reinterpret_cast(cpu::GetCoreLocalRegionAddress())->current.context;
@@ -135,6 +137,11 @@ namespace ams::kern {
static ALWAYS_INLINE KSupervisorPageTable &GetKernelPageTable() {
return s_supervisor_page_table;
}
+
+ static ALWAYS_INLINE KWorkerTaskManager &GetWorkerTaskManager(KWorkerTaskManager::WorkerType type) {
+ MESOSPHERE_ASSERT(type <= KWorkerTaskManager::WorkerType_Count);
+ return s_worker_task_managers[type];
+ }
};
}
diff --git a/libraries/libmesosphere/source/kern_k_worker_task_manager.cpp b/libraries/libmesosphere/source/kern_k_worker_task_manager.cpp
new file mode 100644
index 000000000..5a156cd53
--- /dev/null
+++ b/libraries/libmesosphere/source/kern_k_worker_task_manager.cpp
@@ -0,0 +1,111 @@
+/*
+ * Copyright (c) 2018-2020 Atmosphère-NX
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+#include
+
+namespace ams::kern {
+
+ void KWorkerTaskManager::Initialize(WorkerType wt, s32 priority) {
+ /* Set type, other members already initialized in constructor. */
+ this->type = wt;
+
+ /* Reserve a thread from the system limit. */
+ MESOSPHERE_ABORT_UNLESS(Kernel::GetSystemResourceLimit().Reserve(ams::svc::LimitableResource_ThreadCountMax, 1));
+
+ /* Create a new thread. */
+ this->thread = KThread::Create();
+ MESOSPHERE_ABORT_UNLESS(this->thread != nullptr);
+
+ /* Launch the new thread. */
+ MESOSPHERE_R_ABORT_UNLESS(KThread::InitializeKernelThread(this->thread, ThreadFunction, reinterpret_cast(this), priority, cpu::NumCores - 1));
+
+ /* Register the new thread. */
+ KThread::Register(this->thread);
+
+ /* Run the thread. */
+ this->thread->Run();
+ }
+
+ void KWorkerTaskManager::AddTask(WorkerType type, KWorkerTask *task) {
+ MESOSPHERE_ASSERT(type <= WorkerType_Count);
+ Kernel::GetWorkerTaskManager(type).AddTask(task);
+ }
+
+ void KWorkerTaskManager::ThreadFunction(uintptr_t arg) {
+ reinterpret_cast(arg)->ThreadFunctionImpl();
+ }
+
+ void KWorkerTaskManager::ThreadFunctionImpl() {
+ while (true) {
+ KWorkerTask *task = nullptr;
+
+ /* Get a worker task. */
+ {
+ KScopedSchedulerLock sl;
+ task = this->GetTask();
+
+ if (task == nullptr) {
+ /* If there's nothing to do, set ourselves as waiting. */
+ this->active = false;
+ this->thread->SetState(KThread::ThreadState_Waiting);
+ continue;
+ }
+
+ this->active = true;
+ }
+
+ /* Do the task. */
+ task->DoWorkerTask();
+ }
+ }
+
+ KWorkerTask *KWorkerTaskManager::GetTask() {
+ MESOSPHERE_ASSERT(KScheduler::IsSchedulerLockedByCurrentThread());
+ KWorkerTask *next = this->head_task;
+ if (next) {
+ /* Advance the list. */
+ if (this->head_task == this->tail_task) {
+ this->head_task = nullptr;
+ this->tail_task = nullptr;
+ } else {
+ this->head_task = this->head_task->GetNextTask();
+ }
+
+ /* Clear the next task's next. */
+ next->SetNextTask(nullptr);
+ }
+ return next;
+ }
+
+ void KWorkerTaskManager::AddTask(KWorkerTask *task) {
+ KScopedSchedulerLock sl;
+ MESOSPHERE_ASSERT(task->GetNextTask() == nullptr);
+
+ /* Insert the task. */
+ if (this->tail_task) {
+ this->tail_task->SetNextTask(task);
+ this->tail_task = task;
+ } else {
+ this->head_task = task;
+ this->tail_task = task;
+
+ /* Make ourselves active if we need to. */
+ if (!this->active) {
+ this->thread->SetState(KThread::ThreadState_Runnable);
+ }
+ }
+ }
+
+}
diff --git a/libraries/libmesosphere/source/kern_main.cpp b/libraries/libmesosphere/source/kern_main.cpp
index 81ab0d680..bafa88c08 100644
--- a/libraries/libmesosphere/source/kern_main.cpp
+++ b/libraries/libmesosphere/source/kern_main.cpp
@@ -107,7 +107,7 @@ namespace ams::kern {
/* Perform more core-0 specific initialization. */
if (core_id == 0) {
- MESOSPHERE_TODO("Initialize KWorkerThreadManager");
+ Kernel::GetWorkerTaskManager(KWorkerTaskManager::WorkerType_Exit).Initialize(KWorkerTaskManager::WorkerType_Exit, KWorkerTaskManager::ExitWorkerPriority);
MESOSPHERE_TODO("KSystemControl::InitializeSleepManagerAndAppletSecureMemory();");
diff --git a/mesosphere/kernel/source/kern_kernel_instantiations.cpp b/mesosphere/kernel/source/kern_kernel_instantiations.cpp
index 6167c2726..ddd4dafde 100644
--- a/mesosphere/kernel/source/kern_kernel_instantiations.cpp
+++ b/mesosphere/kernel/source/kern_kernel_instantiations.cpp
@@ -26,6 +26,7 @@ namespace ams::kern {
KMemoryBlockSlabManager Kernel::s_sys_memory_block_manager;
KBlockInfoManager Kernel::s_block_info_manager;
KSupervisorPageTable Kernel::s_supervisor_page_table;
+ KWorkerTaskManager Kernel::s_worker_task_managers[KWorkerTaskManager::WorkerType_Count];
namespace {