diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt
index 88a31e0f2..843bce150 100644
--- a/src/core/CMakeLists.txt
+++ b/src/core/CMakeLists.txt
@@ -170,6 +170,7 @@ add_library(core STATIC
     hle/kernel/k_synchronization_object.h
     hle/kernel/k_thread.cpp
     hle/kernel/k_thread.h
+    hle/kernel/k_thread_queue.h
     hle/kernel/kernel.cpp
     hle/kernel/kernel.h
     hle/kernel/memory/address_space_info.cpp
diff --git a/src/core/hle/kernel/k_thread_queue.h b/src/core/hle/kernel/k_thread_queue.h
new file mode 100644
index 000000000..c52eba249
--- /dev/null
+++ b/src/core/hle/kernel/k_thread_queue.h
@@ -0,0 +1,81 @@
+// Copyright 2021 yuzu Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include "core/hle/kernel/k_thread.h"
+
+namespace Kernel {
+
+class KThreadQueue {
+public:
+    explicit KThreadQueue(KernelCore& kernel) : kernel{kernel} {}
+
+    bool IsEmpty() const {
+        return wait_list.empty();
+    }
+
+    KThread::WaiterList::iterator begin() {
+        return wait_list.begin();
+    }
+    KThread::WaiterList::iterator end() {
+        return wait_list.end();
+    }
+
+    bool SleepThread(KThread* t) {
+        KScopedSchedulerLock sl{kernel};
+
+        // If the thread needs terminating, don't enqueue it.
+        if (t->IsTerminationRequested()) {
+            return false;
+        }
+
+        // Set the thread's queue and mark it as waiting.
+        t->SetSleepingQueue(this);
+        t->SetState(ThreadState::Waiting);
+
+        // Add the thread to the queue.
+        wait_list.push_back(*t);
+
+        return true;
+    }
+
+    void WakeupThread(KThread* t) {
+        KScopedSchedulerLock sl{kernel};
+
+        // Remove the thread from the queue.
+        wait_list.erase(wait_list.iterator_to(*t));
+
+        // Mark the thread as no longer sleeping.
+        t->SetState(ThreadState::Runnable);
+        t->SetSleepingQueue(nullptr);
+    }
+
+    KThread* WakeupFrontThread() {
+        KScopedSchedulerLock sl{kernel};
+
+        if (wait_list.empty()) {
+            return nullptr;
+        } else {
+            // Remove the thread from the queue.
+            auto it = wait_list.begin();
+            KThread* thread = std::addressof(*it);
+            wait_list.erase(it);
+
+            ASSERT(thread->GetState() == ThreadState::Waiting);
+
+            // Mark the thread as no longer sleeping.
+            thread->SetState(ThreadState::Runnable);
+            thread->SetSleepingQueue(nullptr);
+
+            return thread;
+        }
+    }
+
+private:
+    KernelCore& kernel;
+    KThread::WaiterList wait_list{};
+};
+
+} // namespace Kernel