From 28281ae2500a4af9c36c26de5ba07b80d440b335 Mon Sep 17 00:00:00 2001
From: bunnei <bunneidev@gmail.com>
Date: Wed, 9 Dec 2020 21:27:05 -0800
Subject: [PATCH 01/26] core: hle: server_session: Use separate threads for
 each service connection.

---
 src/core/CMakeLists.txt                |   2 +
 src/core/hle/kernel/kernel.cpp         |   2 +-
 src/core/hle/kernel/server_session.cpp |  20 +----
 src/core/hle/kernel/server_session.h   |  12 +--
 src/core/hle/kernel/service_thread.cpp | 100 +++++++++++++++++++++++++
 src/core/hle/kernel/service_thread.h   |  27 +++++++
 6 files changed, 140 insertions(+), 23 deletions(-)
 create mode 100644 src/core/hle/kernel/service_thread.cpp
 create mode 100644 src/core/hle/kernel/service_thread.h

diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt
index 59bd3d2a66..87712a3ceb 100644
--- a/src/core/CMakeLists.txt
+++ b/src/core/CMakeLists.txt
@@ -202,6 +202,8 @@ add_library(core STATIC
     hle/kernel/server_port.h
     hle/kernel/server_session.cpp
     hle/kernel/server_session.h
+    hle/kernel/service_thread.cpp
+    hle/kernel/service_thread.h
     hle/kernel/session.cpp
     hle/kernel/session.h
     hle/kernel/shared_memory.cpp
diff --git a/src/core/hle/kernel/kernel.cpp b/src/core/hle/kernel/kernel.cpp
index 04cae3a433..1bf4c33557 100644
--- a/src/core/hle/kernel/kernel.cpp
+++ b/src/core/hle/kernel/kernel.cpp
@@ -329,7 +329,7 @@ struct KernelCore::Impl {
     std::atomic<u32> registered_thread_ids{Core::Hardware::NUM_CPU_CORES};
 
     // Number of host threads is a relatively high number to avoid overflowing
-    static constexpr size_t NUM_REGISTRABLE_HOST_THREADS = 64;
+    static constexpr size_t NUM_REGISTRABLE_HOST_THREADS = 1024;
     std::atomic<size_t> num_host_threads{0};
     std::array<std::atomic<std::thread::id>, NUM_REGISTRABLE_HOST_THREADS>
         register_host_thread_keys{};
diff --git a/src/core/hle/kernel/server_session.cpp b/src/core/hle/kernel/server_session.cpp
index a35c8aa4be..079c3911a7 100644
--- a/src/core/hle/kernel/server_session.cpp
+++ b/src/core/hle/kernel/server_session.cpp
@@ -32,12 +32,9 @@ ResultVal<std::shared_ptr<ServerSession>> ServerSession::Create(KernelCore& kern
                                                                 std::string name) {
     std::shared_ptr<ServerSession> session{std::make_shared<ServerSession>(kernel)};
 
-    session->request_event =
-        Core::Timing::CreateEvent(name, [session](std::uintptr_t, std::chrono::nanoseconds) {
-            session->CompleteSyncRequest();
-        });
     session->name = std::move(name);
     session->parent = std::move(parent);
+    session->service_thread = std::make_unique<ServiceThread>(kernel);
 
     return MakeResult(std::move(session));
 }
@@ -142,16 +139,12 @@ ResultCode ServerSession::QueueSyncRequest(std::shared_ptr<Thread> thread,
         std::make_shared<HLERequestContext>(kernel, memory, SharedFrom(this), std::move(thread));
 
     context->PopulateFromIncomingCommandBuffer(kernel.CurrentProcess()->GetHandleTable(), cmd_buf);
-    request_queue.Push(std::move(context));
+    service_thread->QueueSyncRequest(*this, std::move(context));
 
     return RESULT_SUCCESS;
 }
 
-ResultCode ServerSession::CompleteSyncRequest() {
-    ASSERT(!request_queue.Empty());
-
-    auto& context = *request_queue.Front();
-
+ResultCode ServerSession::CompleteSyncRequest(HLERequestContext& context) {
     ResultCode result = RESULT_SUCCESS;
     // If the session has been converted to a domain, handle the domain request
     if (IsDomain() && context.HasDomainMessageHeader()) {
@@ -177,18 +170,13 @@ ResultCode ServerSession::CompleteSyncRequest() {
         }
     }
 
-    request_queue.Pop();
-
     return result;
 }
 
 ResultCode ServerSession::HandleSyncRequest(std::shared_ptr<Thread> thread,
                                             Core::Memory::Memory& memory,
                                             Core::Timing::CoreTiming& core_timing) {
-    const ResultCode result = QueueSyncRequest(std::move(thread), memory);
-    const auto delay = std::chrono::nanoseconds{kernel.IsMulticore() ? 0 : 20000};
-    core_timing.ScheduleEvent(delay, request_event, {});
-    return result;
+    return QueueSyncRequest(std::move(thread), memory);
 }
 
 } // namespace Kernel
diff --git a/src/core/hle/kernel/server_session.h b/src/core/hle/kernel/server_session.h
index d23e9ec68f..8466b03e60 100644
--- a/src/core/hle/kernel/server_session.h
+++ b/src/core/hle/kernel/server_session.h
@@ -10,6 +10,7 @@
 #include <vector>
 
 #include "common/threadsafe_queue.h"
+#include "core/hle/kernel/service_thread.h"
 #include "core/hle/kernel/synchronization_object.h"
 #include "core/hle/result.h"
 
@@ -43,6 +44,8 @@ class Thread;
  * TLS buffer and control is transferred back to it.
  */
 class ServerSession final : public SynchronizationObject {
+    friend class ServiceThread;
+
 public:
     explicit ServerSession(KernelCore& kernel);
     ~ServerSession() override;
@@ -132,7 +135,7 @@ private:
     ResultCode QueueSyncRequest(std::shared_ptr<Thread> thread, Core::Memory::Memory& memory);
 
     /// Completes a sync request from the emulated application.
-    ResultCode CompleteSyncRequest();
+    ResultCode CompleteSyncRequest(HLERequestContext& context);
 
     /// Handles a SyncRequest to a domain, forwarding the request to the proper object or closing an
     /// object handle.
@@ -163,11 +166,8 @@ private:
     /// The name of this session (optional)
     std::string name;
 
-    /// Core timing event used to schedule the service request at some point in the future
-    std::shared_ptr<Core::Timing::EventType> request_event;
-
-    /// Queue of scheduled service requests
-    Common::MPSCQueue<std::shared_ptr<Kernel::HLERequestContext>> request_queue;
+    /// Thread to dispatch service requests
+    std::unique_ptr<ServiceThread> service_thread;
 };
 
 } // namespace Kernel
diff --git a/src/core/hle/kernel/service_thread.cpp b/src/core/hle/kernel/service_thread.cpp
new file mode 100644
index 0000000000..59a6045df5
--- /dev/null
+++ b/src/core/hle/kernel/service_thread.cpp
@@ -0,0 +1,100 @@
+// Copyright 2020 yuzu emulator team
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#include <condition_variable>
+#include <functional>
+#include <mutex>
+#include <thread>
+#include <vector>
+#include <queue>
+
+#include "common/assert.h"
+#include "common/scope_exit.h"
+#include "core/core.h"
+#include "core/hle/kernel/kernel.h"
+#include "core/hle/kernel/server_session.h"
+#include "core/hle/kernel/service_thread.h"
+#include "core/hle/lock.h"
+#include "video_core/renderer_base.h"
+
+namespace Kernel {
+
+class ServiceThread::Impl final {
+public:
+    explicit Impl(KernelCore& kernel);
+    ~Impl();
+
+    void QueueSyncRequest(ServerSession& session, std::shared_ptr<HLERequestContext>&& context);
+
+private:
+    std::vector<std::thread> threads;
+    std::queue<std::function<void()>> requests;
+    std::mutex queue_mutex;
+    std::condition_variable condition;
+    bool stop{};
+};
+
+ServiceThread::Impl::Impl(KernelCore& kernel) {
+    constexpr std::size_t SizeOfPool{1};
+    for (std::size_t i = 0; i < SizeOfPool; ++i)
+        threads.emplace_back([&] {
+            // Wait for first request before trying to acquire a render context
+            {
+                std::unique_lock lock{queue_mutex};
+                condition.wait(lock, [this] { return stop || !requests.empty(); });
+            }
+
+            kernel.RegisterHostThread();
+
+            while (true) {
+                std::function<void()> task;
+
+                {
+                    std::unique_lock lock{queue_mutex};
+                    condition.wait(lock, [this] { return stop || !requests.empty(); });
+                    if (stop && requests.empty()) {
+                        return;
+                    }
+                    task = std::move(requests.front());
+                    requests.pop();
+                }
+
+                task();
+            }
+        });
+}
+
+void ServiceThread::Impl::QueueSyncRequest(ServerSession& session,
+                                           std::shared_ptr<HLERequestContext>&& context) {
+    {
+        std::unique_lock lock{queue_mutex};
+        requests.emplace([session{SharedFrom(&session)}, context{std::move(context)}]() {
+            session->CompleteSyncRequest(*context);
+            return;
+        });
+    }
+    condition.notify_one();
+}
+
+ServiceThread::Impl::~Impl() {
+    {
+        std::unique_lock lock{queue_mutex};
+        stop = true;
+    }
+    condition.notify_all();
+    for (std::thread& thread : threads) {
+        thread.join();
+    }
+}
+
+ServiceThread::ServiceThread(KernelCore& kernel) : impl{std::make_unique<Impl>(kernel)} {}
+
+ServiceThread::~ServiceThread() = default;
+
+void ServiceThread::QueueSyncRequest(ServerSession& session,
+                                     std::shared_ptr<HLERequestContext>&& context) {
+    impl->QueueSyncRequest(session, std::move(context));
+}
+
+} // namespace Kernel
diff --git a/src/core/hle/kernel/service_thread.h b/src/core/hle/kernel/service_thread.h
new file mode 100644
index 0000000000..d252490bba
--- /dev/null
+++ b/src/core/hle/kernel/service_thread.h
@@ -0,0 +1,27 @@
+// Copyright 2020 yuzu emulator team
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include <memory>
+
+namespace Kernel {
+
+class HLERequestContext;
+class KernelCore;
+class ServerSession;
+
+class ServiceThread final {
+public:
+    explicit ServiceThread(KernelCore& kernel);
+    ~ServiceThread();
+
+    void QueueSyncRequest(ServerSession& session, std::shared_ptr<HLERequestContext>&& context);
+
+private:
+    class Impl;
+    std::unique_ptr<Impl> impl;
+};
+
+} // namespace Kernel

From b377da042b39ac9bde1ca9be69009117372f33d3 Mon Sep 17 00:00:00 2001
From: bunnei <bunneidev@gmail.com>
Date: Wed, 9 Dec 2020 22:52:09 -0800
Subject: [PATCH 02/26] hle: service: vi: Remove usage of SleepClientThread.

---
 src/core/hle/service/vi/vi.cpp | 73 +++++++++++++++++++---------------
 1 file changed, 41 insertions(+), 32 deletions(-)

diff --git a/src/core/hle/service/vi/vi.cpp b/src/core/hle/service/vi/vi.cpp
index 45cfffe066..93a113b930 100644
--- a/src/core/hle/service/vi/vi.cpp
+++ b/src/core/hle/service/vi/vi.cpp
@@ -536,9 +536,6 @@ private:
         LOG_DEBUG(Service_VI, "called. id=0x{:08X} transaction={:X}, flags=0x{:08X}", id,
                   transaction, flags);
 
-        const auto guard = nv_flinger.Lock();
-        auto& buffer_queue = nv_flinger.FindBufferQueue(id);
-
         switch (transaction) {
         case TransactionId::Connect: {
             IGBPConnectRequestParcel request{ctx.ReadBuffer()};
@@ -553,7 +550,11 @@ private:
         case TransactionId::SetPreallocatedBuffer: {
             IGBPSetPreallocatedBufferRequestParcel request{ctx.ReadBuffer()};
 
-            buffer_queue.SetPreallocatedBuffer(request.data.slot, request.buffer_container.buffer);
+            {
+                const auto guard = nv_flinger.Lock();
+                auto& buffer_queue = nv_flinger.FindBufferQueue(id);
+                buffer_queue.SetPreallocatedBuffer(request.data.slot, request.buffer_container.buffer);
+            }
 
             IGBPSetPreallocatedBufferResponseParcel response{};
             ctx.WriteBuffer(response.Serialize());
@@ -563,48 +564,46 @@ private:
             IGBPDequeueBufferRequestParcel request{ctx.ReadBuffer()};
             const u32 width{request.data.width};
             const u32 height{request.data.height};
-            auto result = buffer_queue.DequeueBuffer(width, height);
 
-            if (result) {
-                // Buffer is available
-                IGBPDequeueBufferResponseParcel response{result->first, *result->second};
-                ctx.WriteBuffer(response.Serialize());
-            } else {
-                // Wait the current thread until a buffer becomes available
-                ctx.SleepClientThread(
-                    "IHOSBinderDriver::DequeueBuffer", UINT64_MAX,
-                    [=, this](std::shared_ptr<Kernel::Thread> thread,
-                              Kernel::HLERequestContext& ctx, Kernel::ThreadWakeupReason reason) {
-                        // Repeat TransactParcel DequeueBuffer when a buffer is available
-                        const auto guard = nv_flinger.Lock();
-                        auto& buffer_queue = nv_flinger.FindBufferQueue(id);
-                        auto result = buffer_queue.DequeueBuffer(width, height);
-                        ASSERT_MSG(result != std::nullopt, "Could not dequeue buffer.");
+            std::optional<std::pair<u32, Service::Nvidia::MultiFence*>> result;
 
-                        IGBPDequeueBufferResponseParcel response{result->first, *result->second};
-                        ctx.WriteBuffer(response.Serialize());
-                        IPC::ResponseBuilder rb{ctx, 2};
-                        rb.Push(RESULT_SUCCESS);
-                    },
-                    buffer_queue.GetWritableBufferWaitEvent());
+            while (!result) {
+                {
+                    const auto guard = nv_flinger.Lock();
+                    auto& buffer_queue = nv_flinger.FindBufferQueue(id);
+                    result = buffer_queue.DequeueBuffer(width, height);
+                }
+
+                if (result) {
+                    // Buffer is available
+                    IGBPDequeueBufferResponseParcel response{result->first, *result->second};
+                    ctx.WriteBuffer(response.Serialize());
+                }
             }
+
             break;
         }
         case TransactionId::RequestBuffer: {
             IGBPRequestBufferRequestParcel request{ctx.ReadBuffer()};
 
+            const auto guard = nv_flinger.Lock();
+            auto& buffer_queue = nv_flinger.FindBufferQueue(id);
             auto& buffer = buffer_queue.RequestBuffer(request.slot);
-
             IGBPRequestBufferResponseParcel response{buffer};
             ctx.WriteBuffer(response.Serialize());
+
             break;
         }
         case TransactionId::QueueBuffer: {
             IGBPQueueBufferRequestParcel request{ctx.ReadBuffer()};
 
-            buffer_queue.QueueBuffer(request.data.slot, request.data.transform,
-                                     request.data.GetCropRect(), request.data.swap_interval,
-                                     request.data.multi_fence);
+            {
+                const auto guard = nv_flinger.Lock();
+                auto& buffer_queue = nv_flinger.FindBufferQueue(id);
+                buffer_queue.QueueBuffer(request.data.slot, request.data.transform,
+                                         request.data.GetCropRect(), request.data.swap_interval,
+                                         request.data.multi_fence);
+            }
 
             IGBPQueueBufferResponseParcel response{1280, 720};
             ctx.WriteBuffer(response.Serialize());
@@ -613,6 +612,8 @@ private:
         case TransactionId::Query: {
             IGBPQueryRequestParcel request{ctx.ReadBuffer()};
 
+            const auto guard = nv_flinger.Lock();
+            auto& buffer_queue = nv_flinger.FindBufferQueue(id);
             const u32 value =
                 buffer_queue.Query(static_cast<NVFlinger::BufferQueue::QueryType>(request.type));
 
@@ -623,7 +624,11 @@ private:
         case TransactionId::CancelBuffer: {
             IGBPCancelBufferRequestParcel request{ctx.ReadBuffer()};
 
-            buffer_queue.CancelBuffer(request.data.slot, request.data.multi_fence);
+            {
+                const auto guard = nv_flinger.Lock();
+                auto& buffer_queue = nv_flinger.FindBufferQueue(id);
+                buffer_queue.CancelBuffer(request.data.slot, request.data.multi_fence);
+            }
 
             IGBPCancelBufferResponseParcel response{};
             ctx.WriteBuffer(response.Serialize());
@@ -633,7 +638,11 @@ private:
             LOG_WARNING(Service_VI, "(STUBBED) called, transaction=Disconnect");
             const auto buffer = ctx.ReadBuffer();
 
-            buffer_queue.Disconnect();
+            {
+                const auto guard = nv_flinger.Lock();
+                auto& buffer_queue = nv_flinger.FindBufferQueue(id);
+                buffer_queue.Disconnect();
+            }
 
             IGBPEmptyResponseParcel response{};
             ctx.WriteBuffer(response.Serialize());

From 19a8f03ad59abacb3fae60f7ba6d5d358e779c29 Mon Sep 17 00:00:00 2001
From: bunnei <bunneidev@gmail.com>
Date: Thu, 10 Dec 2020 15:32:52 -0800
Subject: [PATCH 03/26] hle: service: nvflinger: Refactor locking and
 interfaces.

---
 src/core/hle/service/nvflinger/nvflinger.cpp | 23 +++++-----
 src/core/hle/service/nvflinger/nvflinger.h   |  7 +--
 src/core/hle/service/vi/vi.cpp               | 46 +++++++-------------
 3 files changed, 31 insertions(+), 45 deletions(-)

diff --git a/src/core/hle/service/nvflinger/nvflinger.cpp b/src/core/hle/service/nvflinger/nvflinger.cpp
index 44aa2bdaea..a7a679df1d 100644
--- a/src/core/hle/service/nvflinger/nvflinger.cpp
+++ b/src/core/hle/service/nvflinger/nvflinger.cpp
@@ -104,6 +104,8 @@ void NVFlinger::SetNVDrvInstance(std::shared_ptr<Nvidia::Module> instance) {
 }
 
 std::optional<u64> NVFlinger::OpenDisplay(std::string_view name) {
+    const auto guard = Lock();
+
     LOG_DEBUG(Service, "Opening \"{}\" display", name);
 
     // TODO(Subv): Currently we only support the Default display.
@@ -121,6 +123,7 @@ std::optional<u64> NVFlinger::OpenDisplay(std::string_view name) {
 }
 
 std::optional<u64> NVFlinger::CreateLayer(u64 display_id) {
+    const auto guard = Lock();
     auto* const display = FindDisplay(display_id);
 
     if (display == nullptr) {
@@ -135,12 +138,15 @@ std::optional<u64> NVFlinger::CreateLayer(u64 display_id) {
 }
 
 void NVFlinger::CloseLayer(u64 layer_id) {
+    const auto guard = Lock();
+
     for (auto& display : displays) {
         display.CloseLayer(layer_id);
     }
 }
 
 std::optional<u32> NVFlinger::FindBufferQueueId(u64 display_id, u64 layer_id) const {
+    const auto guard = Lock();
     const auto* const layer = FindLayer(display_id, layer_id);
 
     if (layer == nullptr) {
@@ -151,6 +157,7 @@ std::optional<u32> NVFlinger::FindBufferQueueId(u64 display_id, u64 layer_id) co
 }
 
 std::shared_ptr<Kernel::ReadableEvent> NVFlinger::FindVsyncEvent(u64 display_id) const {
+    const auto guard = Lock();
     auto* const display = FindDisplay(display_id);
 
     if (display == nullptr) {
@@ -160,20 +167,16 @@ std::shared_ptr<Kernel::ReadableEvent> NVFlinger::FindVsyncEvent(u64 display_id)
     return display->GetVSyncEvent();
 }
 
-BufferQueue& NVFlinger::FindBufferQueue(u32 id) {
+BufferQueue* NVFlinger::FindBufferQueue(u32 id) {
+    const auto guard = Lock();
     const auto itr = std::find_if(buffer_queues.begin(), buffer_queues.end(),
                                   [id](const auto& queue) { return queue.GetId() == id; });
 
-    ASSERT(itr != buffer_queues.end());
-    return *itr;
-}
+    if (itr == buffer_queues.end()) {
+        return nullptr;
+    }
 
-const BufferQueue& NVFlinger::FindBufferQueue(u32 id) const {
-    const auto itr = std::find_if(buffer_queues.begin(), buffer_queues.end(),
-                                  [id](const auto& queue) { return queue.GetId() == id; });
-
-    ASSERT(itr != buffer_queues.end());
-    return *itr;
+    return &*itr;
 }
 
 VI::Display* NVFlinger::FindDisplay(u64 display_id) {
diff --git a/src/core/hle/service/nvflinger/nvflinger.h b/src/core/hle/service/nvflinger/nvflinger.h
index 1ebe949c08..ce1347d6da 100644
--- a/src/core/hle/service/nvflinger/nvflinger.h
+++ b/src/core/hle/service/nvflinger/nvflinger.h
@@ -75,10 +75,7 @@ public:
     [[nodiscard]] std::shared_ptr<Kernel::ReadableEvent> FindVsyncEvent(u64 display_id) const;
 
     /// Obtains a buffer queue identified by the ID.
-    [[nodiscard]] BufferQueue& FindBufferQueue(u32 id);
-
-    /// Obtains a buffer queue identified by the ID.
-    [[nodiscard]] const BufferQueue& FindBufferQueue(u32 id) const;
+    [[nodiscard]] BufferQueue* FindBufferQueue(u32 id);
 
     /// Performs a composition request to the emulated nvidia GPU and triggers the vsync events when
     /// finished.
@@ -86,11 +83,11 @@ public:
 
     [[nodiscard]] s64 GetNextTicks() const;
 
+private:
     [[nodiscard]] std::unique_lock<std::mutex> Lock() const {
         return std::unique_lock{*guard};
     }
 
-private:
     /// Finds the display identified by the specified ID.
     [[nodiscard]] VI::Display* FindDisplay(u64 display_id);
 
diff --git a/src/core/hle/service/vi/vi.cpp b/src/core/hle/service/vi/vi.cpp
index 93a113b930..ce0272e590 100644
--- a/src/core/hle/service/vi/vi.cpp
+++ b/src/core/hle/service/vi/vi.cpp
@@ -551,9 +551,9 @@ private:
             IGBPSetPreallocatedBufferRequestParcel request{ctx.ReadBuffer()};
 
             {
-                const auto guard = nv_flinger.Lock();
-                auto& buffer_queue = nv_flinger.FindBufferQueue(id);
-                buffer_queue.SetPreallocatedBuffer(request.data.slot, request.buffer_container.buffer);
+                auto& buffer_queue = *nv_flinger.FindBufferQueue(id);
+                buffer_queue.SetPreallocatedBuffer(request.data.slot,
+                                                   request.buffer_container.buffer);
             }
 
             IGBPSetPreallocatedBufferResponseParcel response{};
@@ -568,11 +568,8 @@ private:
             std::optional<std::pair<u32, Service::Nvidia::MultiFence*>> result;
 
             while (!result) {
-                {
-                    const auto guard = nv_flinger.Lock();
-                    auto& buffer_queue = nv_flinger.FindBufferQueue(id);
-                    result = buffer_queue.DequeueBuffer(width, height);
-                }
+                auto& buffer_queue = *nv_flinger.FindBufferQueue(id);
+                result = buffer_queue.DequeueBuffer(width, height);
 
                 if (result) {
                     // Buffer is available
@@ -586,8 +583,7 @@ private:
         case TransactionId::RequestBuffer: {
             IGBPRequestBufferRequestParcel request{ctx.ReadBuffer()};
 
-            const auto guard = nv_flinger.Lock();
-            auto& buffer_queue = nv_flinger.FindBufferQueue(id);
+            auto& buffer_queue = *nv_flinger.FindBufferQueue(id);
             auto& buffer = buffer_queue.RequestBuffer(request.slot);
             IGBPRequestBufferResponseParcel response{buffer};
             ctx.WriteBuffer(response.Serialize());
@@ -597,13 +593,10 @@ private:
         case TransactionId::QueueBuffer: {
             IGBPQueueBufferRequestParcel request{ctx.ReadBuffer()};
 
-            {
-                const auto guard = nv_flinger.Lock();
-                auto& buffer_queue = nv_flinger.FindBufferQueue(id);
-                buffer_queue.QueueBuffer(request.data.slot, request.data.transform,
-                                         request.data.GetCropRect(), request.data.swap_interval,
-                                         request.data.multi_fence);
-            }
+            auto& buffer_queue = *nv_flinger.FindBufferQueue(id);
+            buffer_queue.QueueBuffer(request.data.slot, request.data.transform,
+                                     request.data.GetCropRect(), request.data.swap_interval,
+                                     request.data.multi_fence);
 
             IGBPQueueBufferResponseParcel response{1280, 720};
             ctx.WriteBuffer(response.Serialize());
@@ -612,8 +605,7 @@ private:
         case TransactionId::Query: {
             IGBPQueryRequestParcel request{ctx.ReadBuffer()};
 
-            const auto guard = nv_flinger.Lock();
-            auto& buffer_queue = nv_flinger.FindBufferQueue(id);
+            auto& buffer_queue = *nv_flinger.FindBufferQueue(id);
             const u32 value =
                 buffer_queue.Query(static_cast<NVFlinger::BufferQueue::QueryType>(request.type));
 
@@ -624,11 +616,8 @@ private:
         case TransactionId::CancelBuffer: {
             IGBPCancelBufferRequestParcel request{ctx.ReadBuffer()};
 
-            {
-                const auto guard = nv_flinger.Lock();
-                auto& buffer_queue = nv_flinger.FindBufferQueue(id);
-                buffer_queue.CancelBuffer(request.data.slot, request.data.multi_fence);
-            }
+            auto& buffer_queue = *nv_flinger.FindBufferQueue(id);
+            buffer_queue.CancelBuffer(request.data.slot, request.data.multi_fence);
 
             IGBPCancelBufferResponseParcel response{};
             ctx.WriteBuffer(response.Serialize());
@@ -638,11 +627,8 @@ private:
             LOG_WARNING(Service_VI, "(STUBBED) called, transaction=Disconnect");
             const auto buffer = ctx.ReadBuffer();
 
-            {
-                const auto guard = nv_flinger.Lock();
-                auto& buffer_queue = nv_flinger.FindBufferQueue(id);
-                buffer_queue.Disconnect();
-            }
+            auto& buffer_queue = *nv_flinger.FindBufferQueue(id);
+            buffer_queue.Disconnect();
 
             IGBPEmptyResponseParcel response{};
             ctx.WriteBuffer(response.Serialize());
@@ -691,7 +677,7 @@ private:
 
         LOG_WARNING(Service_VI, "(STUBBED) called id={}, unknown={:08X}", id, unknown);
 
-        const auto& buffer_queue = nv_flinger.FindBufferQueue(id);
+        const auto& buffer_queue = *nv_flinger.FindBufferQueue(id);
 
         // TODO(Subv): Find out what this actually is.
         IPC::ResponseBuilder rb{ctx, 2, 1};

From 8bc3d66354972c8a288a56c9b75c9705597778f8 Mon Sep 17 00:00:00 2001
From: bunnei <bunneidev@gmail.com>
Date: Thu, 10 Dec 2020 16:03:35 -0800
Subject: [PATCH 04/26] hle: kernel: service_thread: Add parameter for thread
 pool size.

---
 src/core/hle/kernel/server_session.cpp |  2 +-
 src/core/hle/kernel/service_thread.cpp | 10 +++++-----
 src/core/hle/kernel/service_thread.h   |  2 +-
 3 files changed, 7 insertions(+), 7 deletions(-)

diff --git a/src/core/hle/kernel/server_session.cpp b/src/core/hle/kernel/server_session.cpp
index 079c3911a7..ed42452ffd 100644
--- a/src/core/hle/kernel/server_session.cpp
+++ b/src/core/hle/kernel/server_session.cpp
@@ -34,7 +34,7 @@ ResultVal<std::shared_ptr<ServerSession>> ServerSession::Create(KernelCore& kern
 
     session->name = std::move(name);
     session->parent = std::move(parent);
-    session->service_thread = std::make_unique<ServiceThread>(kernel);
+    session->service_thread = std::make_unique<ServiceThread>(kernel, 1);
 
     return MakeResult(std::move(session));
 }
diff --git a/src/core/hle/kernel/service_thread.cpp b/src/core/hle/kernel/service_thread.cpp
index 59a6045df5..4ceb7e56a4 100644
--- a/src/core/hle/kernel/service_thread.cpp
+++ b/src/core/hle/kernel/service_thread.cpp
@@ -22,7 +22,7 @@ namespace Kernel {
 
 class ServiceThread::Impl final {
 public:
-    explicit Impl(KernelCore& kernel);
+    explicit Impl(KernelCore& kernel, std::size_t num_threads);
     ~Impl();
 
     void QueueSyncRequest(ServerSession& session, std::shared_ptr<HLERequestContext>&& context);
@@ -35,9 +35,8 @@ private:
     bool stop{};
 };
 
-ServiceThread::Impl::Impl(KernelCore& kernel) {
-    constexpr std::size_t SizeOfPool{1};
-    for (std::size_t i = 0; i < SizeOfPool; ++i)
+ServiceThread::Impl::Impl(KernelCore& kernel, std::size_t num_threads) {
+    for (std::size_t i = 0; i < num_threads; ++i)
         threads.emplace_back([&] {
             // Wait for first request before trying to acquire a render context
             {
@@ -88,7 +87,8 @@ ServiceThread::Impl::~Impl() {
     }
 }
 
-ServiceThread::ServiceThread(KernelCore& kernel) : impl{std::make_unique<Impl>(kernel)} {}
+ServiceThread::ServiceThread(KernelCore& kernel, std::size_t num_threads)
+    : impl{std::make_unique<Impl>(kernel, num_threads)} {}
 
 ServiceThread::~ServiceThread() = default;
 
diff --git a/src/core/hle/kernel/service_thread.h b/src/core/hle/kernel/service_thread.h
index d252490bba..91ad7ae856 100644
--- a/src/core/hle/kernel/service_thread.h
+++ b/src/core/hle/kernel/service_thread.h
@@ -14,7 +14,7 @@ class ServerSession;
 
 class ServiceThread final {
 public:
-    explicit ServiceThread(KernelCore& kernel);
+    explicit ServiceThread(KernelCore& kernel, std::size_t num_threads);
     ~ServiceThread();
 
     void QueueSyncRequest(ServerSession& session, std::shared_ptr<HLERequestContext>&& context);

From 0c81b83ca9bd773b4a769820459c6a4a01435f89 Mon Sep 17 00:00:00 2001
From: bunnei <bunneidev@gmail.com>
Date: Fri, 11 Dec 2020 16:04:46 -0800
Subject: [PATCH 05/26] hle: service: nvdrv: Revert #4981 to remove usage of
 SleepClientThread.

- Note, this always processes the ioctl right away, which fixes BotW 1.0.0 issues.
---
 src/core/hle/service/nvdrv/devices/nvdevice.h |  9 +-
 .../service/nvdrv/devices/nvdisp_disp0.cpp    |  9 +-
 .../hle/service/nvdrv/devices/nvdisp_disp0.h  |  8 +-
 .../service/nvdrv/devices/nvhost_as_gpu.cpp   |  9 +-
 .../hle/service/nvdrv/devices/nvhost_as_gpu.h |  8 +-
 .../hle/service/nvdrv/devices/nvhost_ctrl.cpp | 20 ++--
 .../hle/service/nvdrv/devices/nvhost_ctrl.h   | 11 +--
 .../service/nvdrv/devices/nvhost_ctrl_gpu.cpp |  8 +-
 .../service/nvdrv/devices/nvhost_ctrl_gpu.h   |  8 +-
 .../hle/service/nvdrv/devices/nvhost_gpu.cpp  |  8 +-
 .../hle/service/nvdrv/devices/nvhost_gpu.h    |  8 +-
 .../service/nvdrv/devices/nvhost_nvdec.cpp    |  9 +-
 .../hle/service/nvdrv/devices/nvhost_nvdec.h  |  8 +-
 .../service/nvdrv/devices/nvhost_nvjpg.cpp    |  9 +-
 .../hle/service/nvdrv/devices/nvhost_nvjpg.h  |  8 +-
 .../hle/service/nvdrv/devices/nvhost_vic.cpp  |  8 +-
 .../hle/service/nvdrv/devices/nvhost_vic.h    |  8 +-
 src/core/hle/service/nvdrv/devices/nvmap.cpp  |  8 +-
 src/core/hle/service/nvdrv/devices/nvmap.h    |  8 +-
 src/core/hle/service/nvdrv/interface.cpp      | 92 ++-----------------
 src/core/hle/service/nvdrv/nvdata.h           | 11 ---
 src/core/hle/service/nvdrv/nvdrv.cpp          | 13 ++-
 src/core/hle/service/nvdrv/nvdrv.h            |  6 +-
 23 files changed, 83 insertions(+), 211 deletions(-)

diff --git a/src/core/hle/service/nvdrv/devices/nvdevice.h b/src/core/hle/service/nvdrv/devices/nvdevice.h
index 44a8bc0604..5681599ba2 100644
--- a/src/core/hle/service/nvdrv/devices/nvdevice.h
+++ b/src/core/hle/service/nvdrv/devices/nvdevice.h
@@ -31,8 +31,8 @@ public:
      * @param output A buffer where the output data will be written to.
      * @returns The result code of the ioctl.
      */
-    virtual NvResult Ioctl1(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output,
-                            IoctlCtrl& ctrl) = 0;
+    virtual NvResult Ioctl1(Ioctl command, const std::vector<u8>& input,
+                            std::vector<u8>& output) = 0;
 
     /**
      * Handles an ioctl2 request.
@@ -43,8 +43,7 @@ public:
      * @returns The result code of the ioctl.
      */
     virtual NvResult Ioctl2(Ioctl command, const std::vector<u8>& input,
-                            const std::vector<u8>& inline_input, std::vector<u8>& output,
-                            IoctlCtrl& ctrl) = 0;
+                            const std::vector<u8>& inline_input, std::vector<u8>& output) = 0;
 
     /**
      * Handles an ioctl3 request.
@@ -55,7 +54,7 @@ public:
      * @returns The result code of the ioctl.
      */
     virtual NvResult Ioctl3(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output,
-                            std::vector<u8>& inline_output, IoctlCtrl& ctrl) = 0;
+                            std::vector<u8>& inline_output) = 0;
 
 protected:
     Core::System& system;
diff --git a/src/core/hle/service/nvdrv/devices/nvdisp_disp0.cpp b/src/core/hle/service/nvdrv/devices/nvdisp_disp0.cpp
index 170a7c9a04..ce615c7581 100644
--- a/src/core/hle/service/nvdrv/devices/nvdisp_disp0.cpp
+++ b/src/core/hle/service/nvdrv/devices/nvdisp_disp0.cpp
@@ -18,21 +18,20 @@ nvdisp_disp0::nvdisp_disp0(Core::System& system, std::shared_ptr<nvmap> nvmap_de
     : nvdevice(system), nvmap_dev(std::move(nvmap_dev)) {}
 nvdisp_disp0 ::~nvdisp_disp0() = default;
 
-NvResult nvdisp_disp0::Ioctl1(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output,
-                              IoctlCtrl& ctrl) {
+NvResult nvdisp_disp0::Ioctl1(Ioctl command, const std::vector<u8>& input,
+                              std::vector<u8>& output) {
     UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw);
     return NvResult::NotImplemented;
 }
 
 NvResult nvdisp_disp0::Ioctl2(Ioctl command, const std::vector<u8>& input,
-                              const std::vector<u8>& inline_input, std::vector<u8>& output,
-                              IoctlCtrl& ctrl) {
+                              const std::vector<u8>& inline_input, std::vector<u8>& output) {
     UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw);
     return NvResult::NotImplemented;
 }
 
 NvResult nvdisp_disp0::Ioctl3(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output,
-                              std::vector<u8>& inline_output, IoctlCtrl& ctrl) {
+                              std::vector<u8>& inline_output) {
     UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw);
     return NvResult::NotImplemented;
 }
diff --git a/src/core/hle/service/nvdrv/devices/nvdisp_disp0.h b/src/core/hle/service/nvdrv/devices/nvdisp_disp0.h
index eb7575e405..55a33b7e47 100644
--- a/src/core/hle/service/nvdrv/devices/nvdisp_disp0.h
+++ b/src/core/hle/service/nvdrv/devices/nvdisp_disp0.h
@@ -20,13 +20,11 @@ public:
     explicit nvdisp_disp0(Core::System& system, std::shared_ptr<nvmap> nvmap_dev);
     ~nvdisp_disp0() override;
 
-    NvResult Ioctl1(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output,
-                    IoctlCtrl& ctrl) override;
+    NvResult Ioctl1(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output) override;
     NvResult Ioctl2(Ioctl command, const std::vector<u8>& input,
-                    const std::vector<u8>& inline_input, std::vector<u8>& output,
-                    IoctlCtrl& ctrl) override;
+                    const std::vector<u8>& inline_input, std::vector<u8>& output) override;
     NvResult Ioctl3(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output,
-                    std::vector<u8>& inline_output, IoctlCtrl& ctrl) override;
+                    std::vector<u8>& inline_output) override;
 
     /// Performs a screen flip, drawing the buffer pointed to by the handle.
     void flip(u32 buffer_handle, u32 offset, u32 format, u32 width, u32 height, u32 stride,
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.cpp b/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.cpp
index 4e0652c396..6b062e10e3 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.cpp
+++ b/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.cpp
@@ -21,8 +21,8 @@ nvhost_as_gpu::nvhost_as_gpu(Core::System& system, std::shared_ptr<nvmap> nvmap_
     : nvdevice(system), nvmap_dev(std::move(nvmap_dev)) {}
 nvhost_as_gpu::~nvhost_as_gpu() = default;
 
-NvResult nvhost_as_gpu::Ioctl1(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output,
-                               IoctlCtrl& ctrl) {
+NvResult nvhost_as_gpu::Ioctl1(Ioctl command, const std::vector<u8>& input,
+                               std::vector<u8>& output) {
     switch (command.group) {
     case 'A':
         switch (command.cmd) {
@@ -55,14 +55,13 @@ NvResult nvhost_as_gpu::Ioctl1(Ioctl command, const std::vector<u8>& input, std:
 }
 
 NvResult nvhost_as_gpu::Ioctl2(Ioctl command, const std::vector<u8>& input,
-                               const std::vector<u8>& inline_input, std::vector<u8>& output,
-                               IoctlCtrl& ctrl) {
+                               const std::vector<u8>& inline_input, std::vector<u8>& output) {
     UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw);
     return NvResult::NotImplemented;
 }
 
 NvResult nvhost_as_gpu::Ioctl3(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output,
-                               std::vector<u8>& inline_output, IoctlCtrl& ctrl) {
+                               std::vector<u8>& inline_output) {
     switch (command.group) {
     case 'A':
         switch (command.cmd) {
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.h b/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.h
index 2bd355af96..08035fa0e5 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.h
+++ b/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.h
@@ -30,13 +30,11 @@ public:
     explicit nvhost_as_gpu(Core::System& system, std::shared_ptr<nvmap> nvmap_dev);
     ~nvhost_as_gpu() override;
 
-    NvResult Ioctl1(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output,
-                    IoctlCtrl& ctrl) override;
+    NvResult Ioctl1(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output) override;
     NvResult Ioctl2(Ioctl command, const std::vector<u8>& input,
-                    const std::vector<u8>& inline_input, std::vector<u8>& output,
-                    IoctlCtrl& ctrl) override;
+                    const std::vector<u8>& inline_input, std::vector<u8>& output) override;
     NvResult Ioctl3(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output,
-                    std::vector<u8>& inline_output, IoctlCtrl& ctrl) override;
+                    std::vector<u8>& inline_output) override;
 
 private:
     class BufferMap final {
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_ctrl.cpp b/src/core/hle/service/nvdrv/devices/nvhost_ctrl.cpp
index 92d31b6207..fea3b7b9f5 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_ctrl.cpp
+++ b/src/core/hle/service/nvdrv/devices/nvhost_ctrl.cpp
@@ -20,8 +20,7 @@ nvhost_ctrl::nvhost_ctrl(Core::System& system, EventInterface& events_interface,
     : nvdevice(system), events_interface{events_interface}, syncpoint_manager{syncpoint_manager} {}
 nvhost_ctrl::~nvhost_ctrl() = default;
 
-NvResult nvhost_ctrl::Ioctl1(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output,
-                             IoctlCtrl& ctrl) {
+NvResult nvhost_ctrl::Ioctl1(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output) {
     switch (command.group) {
     case 0x0:
         switch (command.cmd) {
@@ -30,9 +29,9 @@ NvResult nvhost_ctrl::Ioctl1(Ioctl command, const std::vector<u8>& input, std::v
         case 0x1c:
             return IocCtrlClearEventWait(input, output);
         case 0x1d:
-            return IocCtrlEventWait(input, output, false, ctrl);
+            return IocCtrlEventWait(input, output, false);
         case 0x1e:
-            return IocCtrlEventWait(input, output, true, ctrl);
+            return IocCtrlEventWait(input, output, true);
         case 0x1f:
             return IocCtrlEventRegister(input, output);
         case 0x20:
@@ -48,14 +47,13 @@ NvResult nvhost_ctrl::Ioctl1(Ioctl command, const std::vector<u8>& input, std::v
 }
 
 NvResult nvhost_ctrl::Ioctl2(Ioctl command, const std::vector<u8>& input,
-                             const std::vector<u8>& inline_input, std::vector<u8>& output,
-                             IoctlCtrl& ctrl) {
+                             const std::vector<u8>& inline_input, std::vector<u8>& output) {
     UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw);
     return NvResult::NotImplemented;
 }
 
 NvResult nvhost_ctrl::Ioctl3(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output,
-                             std::vector<u8>& inline_output, IoctlCtrl& ctrl) {
+                             std::vector<u8>& inline_outpu) {
     UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw);
     return NvResult::NotImplemented;
 }
@@ -69,7 +67,7 @@ NvResult nvhost_ctrl::NvOsGetConfigU32(const std::vector<u8>& input, std::vector
 }
 
 NvResult nvhost_ctrl::IocCtrlEventWait(const std::vector<u8>& input, std::vector<u8>& output,
-                                       bool is_async, IoctlCtrl& ctrl) {
+                                       bool is_async) {
     IocCtrlEventWaitParams params{};
     std::memcpy(&params, input.data(), sizeof(params));
     LOG_DEBUG(Service_NVDRV, "syncpt_id={}, threshold={}, timeout={}, is_async={}",
@@ -141,12 +139,6 @@ NvResult nvhost_ctrl::IocCtrlEventWait(const std::vector<u8>& input, std::vector
         params.value |= event_id;
         event.event.writable->Clear();
         gpu.RegisterSyncptInterrupt(params.syncpt_id, target_value);
-        if (!is_async && ctrl.fresh_call) {
-            ctrl.must_delay = true;
-            ctrl.timeout = params.timeout;
-            ctrl.event_id = event_id;
-            return NvResult::Timeout;
-        }
         std::memcpy(output.data(), &params, sizeof(params));
         return NvResult::Timeout;
     }
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_ctrl.h b/src/core/hle/service/nvdrv/devices/nvhost_ctrl.h
index 107168e217..c5aa1362a5 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_ctrl.h
+++ b/src/core/hle/service/nvdrv/devices/nvhost_ctrl.h
@@ -18,13 +18,11 @@ public:
                          SyncpointManager& syncpoint_manager);
     ~nvhost_ctrl() override;
 
-    NvResult Ioctl1(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output,
-                    IoctlCtrl& ctrl) override;
+    NvResult Ioctl1(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output) override;
     NvResult Ioctl2(Ioctl command, const std::vector<u8>& input,
-                    const std::vector<u8>& inline_input, std::vector<u8>& output,
-                    IoctlCtrl& ctrl) override;
+                    const std::vector<u8>& inline_input, std::vector<u8>& output) override;
     NvResult Ioctl3(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output,
-                    std::vector<u8>& inline_output, IoctlCtrl& ctrl) override;
+                    std::vector<u8>& inline_output) override;
 
 private:
     struct IocSyncptReadParams {
@@ -123,8 +121,7 @@ private:
     static_assert(sizeof(IocCtrlEventKill) == 8, "IocCtrlEventKill is incorrect size");
 
     NvResult NvOsGetConfigU32(const std::vector<u8>& input, std::vector<u8>& output);
-    NvResult IocCtrlEventWait(const std::vector<u8>& input, std::vector<u8>& output, bool is_async,
-                              IoctlCtrl& ctrl);
+    NvResult IocCtrlEventWait(const std::vector<u8>& input, std::vector<u8>& output, bool is_async);
     NvResult IocCtrlEventRegister(const std::vector<u8>& input, std::vector<u8>& output);
     NvResult IocCtrlEventUnregister(const std::vector<u8>& input, std::vector<u8>& output);
     NvResult IocCtrlClearEventWait(const std::vector<u8>& input, std::vector<u8>& output);
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.cpp b/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.cpp
index 647f5907e7..0320d3ae2f 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.cpp
+++ b/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.cpp
@@ -16,7 +16,7 @@ nvhost_ctrl_gpu::nvhost_ctrl_gpu(Core::System& system) : nvdevice(system) {}
 nvhost_ctrl_gpu::~nvhost_ctrl_gpu() = default;
 
 NvResult nvhost_ctrl_gpu::Ioctl1(Ioctl command, const std::vector<u8>& input,
-                                 std::vector<u8>& output, IoctlCtrl& ctrl) {
+                                 std::vector<u8>& output) {
     switch (command.group) {
     case 'G':
         switch (command.cmd) {
@@ -48,15 +48,13 @@ NvResult nvhost_ctrl_gpu::Ioctl1(Ioctl command, const std::vector<u8>& input,
 }
 
 NvResult nvhost_ctrl_gpu::Ioctl2(Ioctl command, const std::vector<u8>& input,
-                                 const std::vector<u8>& inline_input, std::vector<u8>& output,
-                                 IoctlCtrl& ctrl) {
+                                 const std::vector<u8>& inline_input, std::vector<u8>& output) {
     UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw);
     return NvResult::NotImplemented;
 }
 
 NvResult nvhost_ctrl_gpu::Ioctl3(Ioctl command, const std::vector<u8>& input,
-                                 std::vector<u8>& output, std::vector<u8>& inline_output,
-                                 IoctlCtrl& ctrl) {
+                                 std::vector<u8>& output, std::vector<u8>& inline_output) {
     switch (command.group) {
     case 'G':
         switch (command.cmd) {
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.h b/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.h
index c2fffe7349..137b882381 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.h
+++ b/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.h
@@ -16,13 +16,11 @@ public:
     explicit nvhost_ctrl_gpu(Core::System& system);
     ~nvhost_ctrl_gpu() override;
 
-    NvResult Ioctl1(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output,
-                    IoctlCtrl& ctrl) override;
+    NvResult Ioctl1(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output) override;
     NvResult Ioctl2(Ioctl command, const std::vector<u8>& input,
-                    const std::vector<u8>& inline_input, std::vector<u8>& output,
-                    IoctlCtrl& ctrl) override;
+                    const std::vector<u8>& inline_input, std::vector<u8>& output) override;
     NvResult Ioctl3(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output,
-                    std::vector<u8>& inline_output, IoctlCtrl& ctrl) override;
+                    std::vector<u8>& inline_output) override;
 
 private:
     struct IoctlGpuCharacteristics {
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp b/src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp
index b0c2caba5f..af8b3d9f15 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp
+++ b/src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp
@@ -23,8 +23,7 @@ nvhost_gpu::nvhost_gpu(Core::System& system, std::shared_ptr<nvmap> nvmap_dev,
 
 nvhost_gpu::~nvhost_gpu() = default;
 
-NvResult nvhost_gpu::Ioctl1(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output,
-                            IoctlCtrl& ctrl) {
+NvResult nvhost_gpu::Ioctl1(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output) {
     switch (command.group) {
     case 0x0:
         switch (command.cmd) {
@@ -76,8 +75,7 @@ NvResult nvhost_gpu::Ioctl1(Ioctl command, const std::vector<u8>& input, std::ve
 };
 
 NvResult nvhost_gpu::Ioctl2(Ioctl command, const std::vector<u8>& input,
-                            const std::vector<u8>& inline_input, std::vector<u8>& output,
-                            IoctlCtrl& ctrl) {
+                            const std::vector<u8>& inline_input, std::vector<u8>& output) {
     switch (command.group) {
     case 'H':
         switch (command.cmd) {
@@ -91,7 +89,7 @@ NvResult nvhost_gpu::Ioctl2(Ioctl command, const std::vector<u8>& input,
 }
 
 NvResult nvhost_gpu::Ioctl3(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output,
-                            std::vector<u8>& inline_output, IoctlCtrl& ctrl) {
+                            std::vector<u8>& inline_output) {
     UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw);
     return NvResult::NotImplemented;
 }
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_gpu.h b/src/core/hle/service/nvdrv/devices/nvhost_gpu.h
index aa0048a9dc..e0298b4feb 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_gpu.h
+++ b/src/core/hle/service/nvdrv/devices/nvhost_gpu.h
@@ -26,13 +26,11 @@ public:
                         SyncpointManager& syncpoint_manager);
     ~nvhost_gpu() override;
 
-    NvResult Ioctl1(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output,
-                    IoctlCtrl& ctrl) override;
+    NvResult Ioctl1(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output) override;
     NvResult Ioctl2(Ioctl command, const std::vector<u8>& input,
-                    const std::vector<u8>& inline_input, std::vector<u8>& output,
-                    IoctlCtrl& ctrl) override;
+                    const std::vector<u8>& inline_input, std::vector<u8>& output) override;
     NvResult Ioctl3(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output,
-                    std::vector<u8>& inline_output, IoctlCtrl& ctrl) override;
+                    std::vector<u8>& inline_output) override;
 
 private:
     enum class CtxObjects : u32_le {
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_nvdec.cpp b/src/core/hle/service/nvdrv/devices/nvhost_nvdec.cpp
index b8328c314d..d8735491cb 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_nvdec.cpp
+++ b/src/core/hle/service/nvdrv/devices/nvhost_nvdec.cpp
@@ -15,8 +15,8 @@ nvhost_nvdec::nvhost_nvdec(Core::System& system, std::shared_ptr<nvmap> nvmap_de
     : nvhost_nvdec_common(system, std::move(nvmap_dev)) {}
 nvhost_nvdec::~nvhost_nvdec() = default;
 
-NvResult nvhost_nvdec::Ioctl1(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output,
-                              IoctlCtrl& ctrl) {
+NvResult nvhost_nvdec::Ioctl1(Ioctl command, const std::vector<u8>& input,
+                              std::vector<u8>& output) {
     switch (command.group) {
     case 0x0:
         switch (command.cmd) {
@@ -58,14 +58,13 @@ NvResult nvhost_nvdec::Ioctl1(Ioctl command, const std::vector<u8>& input, std::
 }
 
 NvResult nvhost_nvdec::Ioctl2(Ioctl command, const std::vector<u8>& input,
-                              const std::vector<u8>& inline_input, std::vector<u8>& output,
-                              IoctlCtrl& ctrl) {
+                              const std::vector<u8>& inline_input, std::vector<u8>& output) {
     UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw);
     return NvResult::NotImplemented;
 }
 
 NvResult nvhost_nvdec::Ioctl3(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output,
-                              std::vector<u8>& inline_output, IoctlCtrl& ctrl) {
+                              std::vector<u8>& inline_output) {
     UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw);
     return NvResult::NotImplemented;
 }
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_nvdec.h b/src/core/hle/service/nvdrv/devices/nvhost_nvdec.h
index 884ed6c5b1..79b8b6de12 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_nvdec.h
+++ b/src/core/hle/service/nvdrv/devices/nvhost_nvdec.h
@@ -14,13 +14,11 @@ public:
     explicit nvhost_nvdec(Core::System& system, std::shared_ptr<nvmap> nvmap_dev);
     ~nvhost_nvdec() override;
 
-    NvResult Ioctl1(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output,
-                    IoctlCtrl& ctrl) override;
+    NvResult Ioctl1(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output) override;
     NvResult Ioctl2(Ioctl command, const std::vector<u8>& input,
-                    const std::vector<u8>& inline_input, std::vector<u8>& output,
-                    IoctlCtrl& ctrl) override;
+                    const std::vector<u8>& inline_input, std::vector<u8>& output) override;
     NvResult Ioctl3(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output,
-                    std::vector<u8>& inline_output, IoctlCtrl& ctrl) override;
+                    std::vector<u8>& inline_output) override;
 };
 
 } // namespace Service::Nvidia::Devices
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_nvjpg.cpp b/src/core/hle/service/nvdrv/devices/nvhost_nvjpg.cpp
index 6f4ab0ab34..2d06955c0f 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_nvjpg.cpp
+++ b/src/core/hle/service/nvdrv/devices/nvhost_nvjpg.cpp
@@ -13,8 +13,8 @@ namespace Service::Nvidia::Devices {
 nvhost_nvjpg::nvhost_nvjpg(Core::System& system) : nvdevice(system) {}
 nvhost_nvjpg::~nvhost_nvjpg() = default;
 
-NvResult nvhost_nvjpg::Ioctl1(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output,
-                              IoctlCtrl& ctrl) {
+NvResult nvhost_nvjpg::Ioctl1(Ioctl command, const std::vector<u8>& input,
+                              std::vector<u8>& output) {
     switch (command.group) {
     case 'H':
         switch (command.cmd) {
@@ -33,14 +33,13 @@ NvResult nvhost_nvjpg::Ioctl1(Ioctl command, const std::vector<u8>& input, std::
 }
 
 NvResult nvhost_nvjpg::Ioctl2(Ioctl command, const std::vector<u8>& input,
-                              const std::vector<u8>& inline_input, std::vector<u8>& output,
-                              IoctlCtrl& ctrl) {
+                              const std::vector<u8>& inline_input, std::vector<u8>& output) {
     UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw);
     return NvResult::NotImplemented;
 }
 
 NvResult nvhost_nvjpg::Ioctl3(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output,
-                              std::vector<u8>& inline_output, IoctlCtrl& ctrl) {
+                              std::vector<u8>& inline_output) {
     UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw);
     return NvResult::NotImplemented;
 }
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_nvjpg.h b/src/core/hle/service/nvdrv/devices/nvhost_nvjpg.h
index 6fb99d959f..43948d18dd 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_nvjpg.h
+++ b/src/core/hle/service/nvdrv/devices/nvhost_nvjpg.h
@@ -16,13 +16,11 @@ public:
     explicit nvhost_nvjpg(Core::System& system);
     ~nvhost_nvjpg() override;
 
-    NvResult Ioctl1(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output,
-                    IoctlCtrl& ctrl) override;
+    NvResult Ioctl1(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output) override;
     NvResult Ioctl2(Ioctl command, const std::vector<u8>& input,
-                    const std::vector<u8>& inline_input, std::vector<u8>& output,
-                    IoctlCtrl& ctrl) override;
+                    const std::vector<u8>& inline_input, std::vector<u8>& output) override;
     NvResult Ioctl3(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output,
-                    std::vector<u8>& inline_output, IoctlCtrl& ctrl) override;
+                    std::vector<u8>& inline_output) override;
 
 private:
     struct IoctlSetNvmapFD {
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_vic.cpp b/src/core/hle/service/nvdrv/devices/nvhost_vic.cpp
index 55a17f4238..805fe86ae6 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_vic.cpp
+++ b/src/core/hle/service/nvdrv/devices/nvhost_vic.cpp
@@ -15,8 +15,7 @@ nvhost_vic::nvhost_vic(Core::System& system, std::shared_ptr<nvmap> nvmap_dev)
 
 nvhost_vic::~nvhost_vic() = default;
 
-NvResult nvhost_vic::Ioctl1(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output,
-                            IoctlCtrl& ctrl) {
+NvResult nvhost_vic::Ioctl1(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output) {
     switch (command.group) {
     case 0x0:
         switch (command.cmd) {
@@ -51,14 +50,13 @@ NvResult nvhost_vic::Ioctl1(Ioctl command, const std::vector<u8>& input, std::ve
 }
 
 NvResult nvhost_vic::Ioctl2(Ioctl command, const std::vector<u8>& input,
-                            const std::vector<u8>& inline_input, std::vector<u8>& output,
-                            IoctlCtrl& ctrl) {
+                            const std::vector<u8>& inline_input, std::vector<u8>& output) {
     UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw);
     return NvResult::NotImplemented;
 }
 
 NvResult nvhost_vic::Ioctl3(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output,
-                            std::vector<u8>& inline_output, IoctlCtrl& ctrl) {
+                            std::vector<u8>& inline_output) {
     UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw);
     return NvResult::NotImplemented;
 }
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_vic.h b/src/core/hle/service/nvdrv/devices/nvhost_vic.h
index 7f4858cd4c..b2e11f4d4b 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_vic.h
+++ b/src/core/hle/service/nvdrv/devices/nvhost_vic.h
@@ -14,12 +14,10 @@ public:
     explicit nvhost_vic(Core::System& system, std::shared_ptr<nvmap> nvmap_dev);
     ~nvhost_vic();
 
-    NvResult Ioctl1(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output,
-                    IoctlCtrl& ctrl) override;
+    NvResult Ioctl1(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output) override;
     NvResult Ioctl2(Ioctl command, const std::vector<u8>& input,
-                    const std::vector<u8>& inline_input, std::vector<u8>& output,
-                    IoctlCtrl& ctrl) override;
+                    const std::vector<u8>& inline_input, std::vector<u8>& output) override;
     NvResult Ioctl3(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output,
-                    std::vector<u8>& inline_output, IoctlCtrl& ctrl) override;
+                    std::vector<u8>& inline_output) override;
 };
 } // namespace Service::Nvidia::Devices
diff --git a/src/core/hle/service/nvdrv/devices/nvmap.cpp b/src/core/hle/service/nvdrv/devices/nvmap.cpp
index 910cfee512..4015a27400 100644
--- a/src/core/hle/service/nvdrv/devices/nvmap.cpp
+++ b/src/core/hle/service/nvdrv/devices/nvmap.cpp
@@ -19,8 +19,7 @@ nvmap::nvmap(Core::System& system) : nvdevice(system) {
 
 nvmap::~nvmap() = default;
 
-NvResult nvmap::Ioctl1(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output,
-                       IoctlCtrl& ctrl) {
+NvResult nvmap::Ioctl1(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output) {
     switch (command.group) {
     case 0x1:
         switch (command.cmd) {
@@ -49,14 +48,13 @@ NvResult nvmap::Ioctl1(Ioctl command, const std::vector<u8>& input, std::vector<
 }
 
 NvResult nvmap::Ioctl2(Ioctl command, const std::vector<u8>& input,
-                       const std::vector<u8>& inline_input, std::vector<u8>& output,
-                       IoctlCtrl& ctrl) {
+                       const std::vector<u8>& inline_input, std::vector<u8>& output) {
     UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw);
     return NvResult::NotImplemented;
 }
 
 NvResult nvmap::Ioctl3(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output,
-                       std::vector<u8>& inline_output, IoctlCtrl& ctrl) {
+                       std::vector<u8>& inline_output) {
     UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw);
     return NvResult::NotImplemented;
 }
diff --git a/src/core/hle/service/nvdrv/devices/nvmap.h b/src/core/hle/service/nvdrv/devices/nvmap.h
index c0c2fa5ebc..4484bd79f3 100644
--- a/src/core/hle/service/nvdrv/devices/nvmap.h
+++ b/src/core/hle/service/nvdrv/devices/nvmap.h
@@ -19,13 +19,11 @@ public:
     explicit nvmap(Core::System& system);
     ~nvmap() override;
 
-    NvResult Ioctl1(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output,
-                    IoctlCtrl& ctrl) override;
+    NvResult Ioctl1(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output) override;
     NvResult Ioctl2(Ioctl command, const std::vector<u8>& input,
-                    const std::vector<u8>& inline_input, std::vector<u8>& output,
-                    IoctlCtrl& ctrl) override;
+                    const std::vector<u8>& inline_input, std::vector<u8>& output) override;
     NvResult Ioctl3(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output,
-                    std::vector<u8>& inline_output, IoctlCtrl& ctrl) override;
+                    std::vector<u8>& inline_output) override;
 
     /// Returns the allocated address of an nvmap object given its handle.
     VAddr GetObjectAddress(u32 handle) const;
diff --git a/src/core/hle/service/nvdrv/interface.cpp b/src/core/hle/service/nvdrv/interface.cpp
index d72c531f61..cc23b001c1 100644
--- a/src/core/hle/service/nvdrv/interface.cpp
+++ b/src/core/hle/service/nvdrv/interface.cpp
@@ -61,32 +61,9 @@ void NVDRV::Ioctl1(Kernel::HLERequestContext& ctx) {
     std::vector<u8> output_buffer(ctx.GetWriteBufferSize(0));
     const auto input_buffer = ctx.ReadBuffer(0);
 
-    IoctlCtrl ctrl{};
-
-    const auto nv_result = nvdrv->Ioctl1(fd, command, input_buffer, output_buffer, ctrl);
-    if (ctrl.must_delay) {
-        ctrl.fresh_call = false;
-        ctx.SleepClientThread(
-            "NVServices::DelayedResponse", ctrl.timeout,
-            [=, this](std::shared_ptr<Kernel::Thread> thread, Kernel::HLERequestContext& ctx_,
-                      Kernel::ThreadWakeupReason reason) {
-                IoctlCtrl ctrl2{ctrl};
-                std::vector<u8> tmp_output = output_buffer;
-                const auto nv_result2 = nvdrv->Ioctl1(fd, command, input_buffer, tmp_output, ctrl2);
-
-                if (command.is_out != 0) {
-                    ctx.WriteBuffer(tmp_output);
-                }
-
-                IPC::ResponseBuilder rb{ctx_, 3};
-                rb.Push(RESULT_SUCCESS);
-                rb.PushEnum(nv_result2);
-            },
-            nvdrv->GetEventWriteable(ctrl.event_id));
-    } else {
-        if (command.is_out != 0) {
-            ctx.WriteBuffer(output_buffer);
-        }
+    const auto nv_result = nvdrv->Ioctl1(fd, command, input_buffer, output_buffer);
+    if (command.is_out != 0) {
+        ctx.WriteBuffer(output_buffer);
     }
 
     IPC::ResponseBuilder rb{ctx, 3};
@@ -110,36 +87,8 @@ void NVDRV::Ioctl2(Kernel::HLERequestContext& ctx) {
     const auto input_inlined_buffer = ctx.ReadBuffer(1);
     std::vector<u8> output_buffer(ctx.GetWriteBufferSize(0));
 
-    IoctlCtrl ctrl{};
-
     const auto nv_result =
-        nvdrv->Ioctl2(fd, command, input_buffer, input_inlined_buffer, output_buffer, ctrl);
-    if (ctrl.must_delay) {
-        ctrl.fresh_call = false;
-        ctx.SleepClientThread(
-            "NVServices::DelayedResponse", ctrl.timeout,
-            [=, this](std::shared_ptr<Kernel::Thread> thread, Kernel::HLERequestContext& ctx_,
-                      Kernel::ThreadWakeupReason reason) {
-                IoctlCtrl ctrl2{ctrl};
-                std::vector<u8> tmp_output = output_buffer;
-                const auto nv_result2 = nvdrv->Ioctl2(fd, command, input_buffer,
-                                                      input_inlined_buffer, tmp_output, ctrl2);
-
-                if (command.is_out != 0) {
-                    ctx.WriteBuffer(tmp_output);
-                }
-
-                IPC::ResponseBuilder rb{ctx_, 3};
-                rb.Push(RESULT_SUCCESS);
-                rb.PushEnum(nv_result2);
-            },
-            nvdrv->GetEventWriteable(ctrl.event_id));
-    } else {
-        if (command.is_out != 0) {
-            ctx.WriteBuffer(output_buffer);
-        }
-    }
-
+        nvdrv->Ioctl2(fd, command, input_buffer, input_inlined_buffer, output_buffer);
     if (command.is_out != 0) {
         ctx.WriteBuffer(output_buffer);
     }
@@ -165,36 +114,11 @@ void NVDRV::Ioctl3(Kernel::HLERequestContext& ctx) {
     std::vector<u8> output_buffer(ctx.GetWriteBufferSize(0));
     std::vector<u8> output_buffer_inline(ctx.GetWriteBufferSize(1));
 
-    IoctlCtrl ctrl{};
     const auto nv_result =
-        nvdrv->Ioctl3(fd, command, input_buffer, output_buffer, output_buffer_inline, ctrl);
-    if (ctrl.must_delay) {
-        ctrl.fresh_call = false;
-        ctx.SleepClientThread(
-            "NVServices::DelayedResponse", ctrl.timeout,
-            [=, this](std::shared_ptr<Kernel::Thread> thread, Kernel::HLERequestContext& ctx_,
-                      Kernel::ThreadWakeupReason reason) {
-                IoctlCtrl ctrl2{ctrl};
-                std::vector<u8> tmp_output = output_buffer;
-                std::vector<u8> tmp_output2 = output_buffer;
-                const auto nv_result2 =
-                    nvdrv->Ioctl3(fd, command, input_buffer, tmp_output, tmp_output2, ctrl2);
-
-                if (command.is_out != 0) {
-                    ctx.WriteBuffer(tmp_output, 0);
-                    ctx.WriteBuffer(tmp_output2, 1);
-                }
-
-                IPC::ResponseBuilder rb{ctx_, 3};
-                rb.Push(RESULT_SUCCESS);
-                rb.PushEnum(nv_result2);
-            },
-            nvdrv->GetEventWriteable(ctrl.event_id));
-    } else {
-        if (command.is_out != 0) {
-            ctx.WriteBuffer(output_buffer, 0);
-            ctx.WriteBuffer(output_buffer_inline, 1);
-        }
+        nvdrv->Ioctl3(fd, command, input_buffer, output_buffer, output_buffer_inline);
+    if (command.is_out != 0) {
+        ctx.WriteBuffer(output_buffer, 0);
+        ctx.WriteBuffer(output_buffer_inline, 1);
     }
 
     IPC::ResponseBuilder rb{ctx, 3};
diff --git a/src/core/hle/service/nvdrv/nvdata.h b/src/core/hle/service/nvdrv/nvdata.h
index a3c4ecd853..3294bc0e70 100644
--- a/src/core/hle/service/nvdrv/nvdata.h
+++ b/src/core/hle/service/nvdrv/nvdata.h
@@ -97,15 +97,4 @@ union Ioctl {
     BitField<31, 1, u32> is_out;
 };
 
-struct IoctlCtrl {
-    // First call done to the servioce for services that call itself again after a call.
-    bool fresh_call{true};
-    // Tells the Ioctl Wrapper that it must delay the IPC response and send the thread to sleep
-    bool must_delay{};
-    // Timeout for the delay
-    s64 timeout{};
-    // NV Event Id
-    s32 event_id{-1};
-};
-
 } // namespace Service::Nvidia
diff --git a/src/core/hle/service/nvdrv/nvdrv.cpp b/src/core/hle/service/nvdrv/nvdrv.cpp
index 8e0c9f093c..e03195afe4 100644
--- a/src/core/hle/service/nvdrv/nvdrv.cpp
+++ b/src/core/hle/service/nvdrv/nvdrv.cpp
@@ -91,7 +91,7 @@ DeviceFD Module::Open(const std::string& device_name) {
 }
 
 NvResult Module::Ioctl1(DeviceFD fd, Ioctl command, const std::vector<u8>& input,
-                        std::vector<u8>& output, IoctlCtrl& ctrl) {
+                        std::vector<u8>& output) {
     if (fd < 0) {
         LOG_ERROR(Service_NVDRV, "Invalid DeviceFD={}!", fd);
         return NvResult::InvalidState;
@@ -104,12 +104,11 @@ NvResult Module::Ioctl1(DeviceFD fd, Ioctl command, const std::vector<u8>& input
         return NvResult::NotImplemented;
     }
 
-    return itr->second->Ioctl1(command, input, output, ctrl);
+    return itr->second->Ioctl1(command, input, output);
 }
 
 NvResult Module::Ioctl2(DeviceFD fd, Ioctl command, const std::vector<u8>& input,
-                        const std::vector<u8>& inline_input, std::vector<u8>& output,
-                        IoctlCtrl& ctrl) {
+                        const std::vector<u8>& inline_input, std::vector<u8>& output) {
     if (fd < 0) {
         LOG_ERROR(Service_NVDRV, "Invalid DeviceFD={}!", fd);
         return NvResult::InvalidState;
@@ -122,11 +121,11 @@ NvResult Module::Ioctl2(DeviceFD fd, Ioctl command, const std::vector<u8>& input
         return NvResult::NotImplemented;
     }
 
-    return itr->second->Ioctl2(command, input, inline_input, output, ctrl);
+    return itr->second->Ioctl2(command, input, inline_input, output);
 }
 
 NvResult Module::Ioctl3(DeviceFD fd, Ioctl command, const std::vector<u8>& input,
-                        std::vector<u8>& output, std::vector<u8>& inline_output, IoctlCtrl& ctrl) {
+                        std::vector<u8>& output, std::vector<u8>& inline_output) {
     if (fd < 0) {
         LOG_ERROR(Service_NVDRV, "Invalid DeviceFD={}!", fd);
         return NvResult::InvalidState;
@@ -139,7 +138,7 @@ NvResult Module::Ioctl3(DeviceFD fd, Ioctl command, const std::vector<u8>& input
         return NvResult::NotImplemented;
     }
 
-    return itr->second->Ioctl3(command, input, output, inline_output, ctrl);
+    return itr->second->Ioctl3(command, input, output, inline_output);
 }
 
 NvResult Module::Close(DeviceFD fd) {
diff --git a/src/core/hle/service/nvdrv/nvdrv.h b/src/core/hle/service/nvdrv/nvdrv.h
index 5985d21796..144e657e5f 100644
--- a/src/core/hle/service/nvdrv/nvdrv.h
+++ b/src/core/hle/service/nvdrv/nvdrv.h
@@ -119,13 +119,13 @@ public:
 
     /// Sends an ioctl command to the specified file descriptor.
     NvResult Ioctl1(DeviceFD fd, Ioctl command, const std::vector<u8>& input,
-                    std::vector<u8>& output, IoctlCtrl& ctrl);
+                    std::vector<u8>& output);
 
     NvResult Ioctl2(DeviceFD fd, Ioctl command, const std::vector<u8>& input,
-                    const std::vector<u8>& inline_input, std::vector<u8>& output, IoctlCtrl& ctrl);
+                    const std::vector<u8>& inline_input, std::vector<u8>& output);
 
     NvResult Ioctl3(DeviceFD fd, Ioctl command, const std::vector<u8>& input,
-                    std::vector<u8>& output, std::vector<u8>& inline_output, IoctlCtrl& ctrl);
+                    std::vector<u8>& output, std::vector<u8>& inline_output);
 
     /// Closes a device file descriptor and returns operation success.
     NvResult Close(DeviceFD fd);

From 87d6588cb5c3ba5de34c647abec65b0209484930 Mon Sep 17 00:00:00 2001
From: bunnei <bunneidev@gmail.com>
Date: Fri, 11 Dec 2020 16:44:27 -0800
Subject: [PATCH 06/26] hle: service: bsd: Update to work with service threads,
 removing SleepClientThread.

---
 src/core/CMakeLists.txt                       |   1 -
 .../hle/service/sockets/blocking_worker.h     | 161 ------------------
 src/core/hle/service/sockets/bsd.cpp          | 124 +++++---------
 src/core/hle/service/sockets/bsd.h            |   9 +-
 4 files changed, 45 insertions(+), 250 deletions(-)
 delete mode 100644 src/core/hle/service/sockets/blocking_worker.h

diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt
index 87712a3ceb..01f3e94197 100644
--- a/src/core/CMakeLists.txt
+++ b/src/core/CMakeLists.txt
@@ -502,7 +502,6 @@ add_library(core STATIC
     hle/service/sm/controller.h
     hle/service/sm/sm.cpp
     hle/service/sm/sm.h
-    hle/service/sockets/blocking_worker.h
     hle/service/sockets/bsd.cpp
     hle/service/sockets/bsd.h
     hle/service/sockets/ethc.cpp
diff --git a/src/core/hle/service/sockets/blocking_worker.h b/src/core/hle/service/sockets/blocking_worker.h
deleted file mode 100644
index 2d53e52b68..0000000000
--- a/src/core/hle/service/sockets/blocking_worker.h
+++ /dev/null
@@ -1,161 +0,0 @@
-// Copyright 2020 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
-
-#pragma once
-
-#include <atomic>
-#include <memory>
-#include <string>
-#include <string_view>
-#include <thread>
-#include <variant>
-#include <vector>
-
-#include <fmt/format.h>
-
-#include "common/assert.h"
-#include "common/microprofile.h"
-#include "common/thread.h"
-#include "core/core.h"
-#include "core/hle/kernel/hle_ipc.h"
-#include "core/hle/kernel/kernel.h"
-#include "core/hle/kernel/thread.h"
-#include "core/hle/kernel/writable_event.h"
-
-namespace Service::Sockets {
-
-/**
- * Worker abstraction to execute blocking calls on host without blocking the guest thread
- *
- * @tparam Service  Service where the work is executed
- * @tparam Types Types of work to execute
- */
-template <class Service, class... Types>
-class BlockingWorker {
-    using This = BlockingWorker<Service, Types...>;
-    using WorkVariant = std::variant<std::monostate, Types...>;
-
-public:
-    /// Create a new worker
-    static std::unique_ptr<This> Create(Core::System& system, Service* service,
-                                        std::string_view name) {
-        return std::unique_ptr<This>(new This(system, service, name));
-    }
-
-    ~BlockingWorker() {
-        while (!is_available.load(std::memory_order_relaxed)) {
-            // Busy wait until work is finished
-            std::this_thread::yield();
-        }
-        // Monostate means to exit the thread
-        work = std::monostate{};
-        work_event.Set();
-        thread.join();
-    }
-
-    /**
-     * Try to capture the worker to send work after a success
-     * @returns True when the worker has been successfully captured
-     */
-    bool TryCapture() {
-        bool expected = true;
-        return is_available.compare_exchange_weak(expected, false, std::memory_order_relaxed,
-                                                  std::memory_order_relaxed);
-    }
-
-    /**
-     * Send work to this worker abstraction
-     * @see TryCapture must be called before attempting to call this function
-     */
-    template <class Work>
-    void SendWork(Work new_work) {
-        ASSERT_MSG(!is_available, "Trying to send work on a worker that's not captured");
-        work = std::move(new_work);
-        work_event.Set();
-    }
-
-    /// Generate a callback for @see SleepClientThread
-    template <class Work>
-    auto Callback() {
-        return [this](std::shared_ptr<Kernel::Thread>, Kernel::HLERequestContext& ctx,
-                      Kernel::ThreadWakeupReason reason) {
-            ASSERT(reason == Kernel::ThreadWakeupReason::Signal);
-            std::get<Work>(work).Response(ctx);
-            is_available.store(true);
-        };
-    }
-
-    /// Get kernel event that will be signalled by the worker when the host operation finishes
-    std::shared_ptr<Kernel::WritableEvent> KernelEvent() const {
-        return kernel_event;
-    }
-
-private:
-    explicit BlockingWorker(Core::System& system, Service* service, std::string_view name) {
-        auto pair = Kernel::WritableEvent::CreateEventPair(system.Kernel(), std::string(name));
-        kernel_event = std::move(pair.writable);
-        thread = std::thread([this, &system, service, name] { Run(system, service, name); });
-    }
-
-    void Run(Core::System& system, Service* service, std::string_view name) {
-        system.RegisterHostThread();
-
-        const std::string thread_name = fmt::format("yuzu:{}", name);
-        MicroProfileOnThreadCreate(thread_name.c_str());
-        Common::SetCurrentThreadName(thread_name.c_str());
-
-        bool keep_running = true;
-        while (keep_running) {
-            work_event.Wait();
-
-            const auto visit_fn = [service, &keep_running]<typename T>(T&& w) {
-                if constexpr (std::is_same_v<std::decay_t<T>, std::monostate>) {
-                    keep_running = false;
-                } else {
-                    w.Execute(service);
-                }
-            };
-            std::visit(visit_fn, work);
-
-            kernel_event->Signal();
-        }
-    }
-
-    std::thread thread;
-    WorkVariant work;
-    Common::Event work_event;
-    std::shared_ptr<Kernel::WritableEvent> kernel_event;
-    std::atomic_bool is_available{true};
-};
-
-template <class Service, class... Types>
-class BlockingWorkerPool {
-    using Worker = BlockingWorker<Service, Types...>;
-
-public:
-    explicit BlockingWorkerPool(Core::System& system_, Service* service_)
-        : system{system_}, service{service_} {}
-
-    /// Returns a captured worker thread, creating new ones if necessary
-    Worker* CaptureWorker() {
-        for (auto& worker : workers) {
-            if (worker->TryCapture()) {
-                return worker.get();
-            }
-        }
-        auto new_worker = Worker::Create(system, service, fmt::format("BSD:{}", workers.size()));
-        [[maybe_unused]] const bool success = new_worker->TryCapture();
-        ASSERT(success);
-
-        return workers.emplace_back(std::move(new_worker)).get();
-    }
-
-private:
-    Core::System& system;
-    Service* const service;
-
-    std::vector<std::unique_ptr<Worker>> workers;
-};
-
-} // namespace Service::Sockets
diff --git a/src/core/hle/service/sockets/bsd.cpp b/src/core/hle/service/sockets/bsd.cpp
index 67b419503b..2b824059d3 100644
--- a/src/core/hle/service/sockets/bsd.cpp
+++ b/src/core/hle/service/sockets/bsd.cpp
@@ -178,13 +178,12 @@ void BSD::Poll(Kernel::HLERequestContext& ctx) {
 
     LOG_DEBUG(Service, "called. nfds={} timeout={}", nfds, timeout);
 
-    ExecuteWork(ctx, "BSD:Poll", timeout != 0,
-                PollWork{
-                    .nfds = nfds,
-                    .timeout = timeout,
-                    .read_buffer = ctx.ReadBuffer(),
-                    .write_buffer = std::vector<u8>(ctx.GetWriteBufferSize()),
-                });
+    ExecuteWork(ctx, PollWork{
+                         .nfds = nfds,
+                         .timeout = timeout,
+                         .read_buffer = ctx.ReadBuffer(),
+                         .write_buffer = std::vector<u8>(ctx.GetWriteBufferSize()),
+                     });
 }
 
 void BSD::Accept(Kernel::HLERequestContext& ctx) {
@@ -193,11 +192,10 @@ void BSD::Accept(Kernel::HLERequestContext& ctx) {
 
     LOG_DEBUG(Service, "called. fd={}", fd);
 
-    ExecuteWork(ctx, "BSD:Accept", IsBlockingSocket(fd),
-                AcceptWork{
-                    .fd = fd,
-                    .write_buffer = std::vector<u8>(ctx.GetWriteBufferSize()),
-                });
+    ExecuteWork(ctx, AcceptWork{
+                         .fd = fd,
+                         .write_buffer = std::vector<u8>(ctx.GetWriteBufferSize()),
+                     });
 }
 
 void BSD::Bind(Kernel::HLERequestContext& ctx) {
@@ -215,11 +213,10 @@ void BSD::Connect(Kernel::HLERequestContext& ctx) {
 
     LOG_DEBUG(Service, "called. fd={} addrlen={}", fd, ctx.GetReadBufferSize());
 
-    ExecuteWork(ctx, "BSD:Connect", IsBlockingSocket(fd),
-                ConnectWork{
-                    .fd = fd,
-                    .addr = ctx.ReadBuffer(),
-                });
+    ExecuteWork(ctx, ConnectWork{
+                         .fd = fd,
+                         .addr = ctx.ReadBuffer(),
+                     });
 }
 
 void BSD::GetPeerName(Kernel::HLERequestContext& ctx) {
@@ -327,12 +324,11 @@ void BSD::Recv(Kernel::HLERequestContext& ctx) {
 
     LOG_DEBUG(Service, "called. fd={} flags=0x{:x} len={}", fd, flags, ctx.GetWriteBufferSize());
 
-    ExecuteWork(ctx, "BSD:Recv", IsBlockingSocket(fd),
-                RecvWork{
-                    .fd = fd,
-                    .flags = flags,
-                    .message = std::vector<u8>(ctx.GetWriteBufferSize()),
-                });
+    ExecuteWork(ctx, RecvWork{
+                         .fd = fd,
+                         .flags = flags,
+                         .message = std::vector<u8>(ctx.GetWriteBufferSize()),
+                     });
 }
 
 void BSD::RecvFrom(Kernel::HLERequestContext& ctx) {
@@ -344,13 +340,12 @@ void BSD::RecvFrom(Kernel::HLERequestContext& ctx) {
     LOG_DEBUG(Service, "called. fd={} flags=0x{:x} len={} addrlen={}", fd, flags,
               ctx.GetWriteBufferSize(0), ctx.GetWriteBufferSize(1));
 
-    ExecuteWork(ctx, "BSD:RecvFrom", IsBlockingSocket(fd),
-                RecvFromWork{
-                    .fd = fd,
-                    .flags = flags,
-                    .message = std::vector<u8>(ctx.GetWriteBufferSize(0)),
-                    .addr = std::vector<u8>(ctx.GetWriteBufferSize(1)),
-                });
+    ExecuteWork(ctx, RecvFromWork{
+                         .fd = fd,
+                         .flags = flags,
+                         .message = std::vector<u8>(ctx.GetWriteBufferSize(0)),
+                         .addr = std::vector<u8>(ctx.GetWriteBufferSize(1)),
+                     });
 }
 
 void BSD::Send(Kernel::HLERequestContext& ctx) {
@@ -361,12 +356,11 @@ void BSD::Send(Kernel::HLERequestContext& ctx) {
 
     LOG_DEBUG(Service, "called. fd={} flags=0x{:x} len={}", fd, flags, ctx.GetReadBufferSize());
 
-    ExecuteWork(ctx, "BSD:Send", IsBlockingSocket(fd),
-                SendWork{
-                    .fd = fd,
-                    .flags = flags,
-                    .message = ctx.ReadBuffer(),
-                });
+    ExecuteWork(ctx, SendWork{
+                         .fd = fd,
+                         .flags = flags,
+                         .message = ctx.ReadBuffer(),
+                     });
 }
 
 void BSD::SendTo(Kernel::HLERequestContext& ctx) {
@@ -377,13 +371,12 @@ void BSD::SendTo(Kernel::HLERequestContext& ctx) {
     LOG_DEBUG(Service, "called. fd={} flags=0x{} len={} addrlen={}", fd, flags,
               ctx.GetReadBufferSize(0), ctx.GetReadBufferSize(1));
 
-    ExecuteWork(ctx, "BSD:SendTo", IsBlockingSocket(fd),
-                SendToWork{
-                    .fd = fd,
-                    .flags = flags,
-                    .message = ctx.ReadBuffer(0),
-                    .addr = ctx.ReadBuffer(1),
-                });
+    ExecuteWork(ctx, SendToWork{
+                         .fd = fd,
+                         .flags = flags,
+                         .message = ctx.ReadBuffer(0),
+                         .addr = ctx.ReadBuffer(1),
+                     });
 }
 
 void BSD::Write(Kernel::HLERequestContext& ctx) {
@@ -392,12 +385,11 @@ void BSD::Write(Kernel::HLERequestContext& ctx) {
 
     LOG_DEBUG(Service, "called. fd={} len={}", fd, ctx.GetReadBufferSize());
 
-    ExecuteWork(ctx, "BSD:Write", IsBlockingSocket(fd),
-                SendWork{
-                    .fd = fd,
-                    .flags = 0,
-                    .message = ctx.ReadBuffer(),
-                });
+    ExecuteWork(ctx, SendWork{
+                         .fd = fd,
+                         .flags = 0,
+                         .message = ctx.ReadBuffer(),
+                     });
 }
 
 void BSD::Close(Kernel::HLERequestContext& ctx) {
@@ -410,24 +402,9 @@ void BSD::Close(Kernel::HLERequestContext& ctx) {
 }
 
 template <typename Work>
-void BSD::ExecuteWork(Kernel::HLERequestContext& ctx, std::string_view sleep_reason,
-                      bool is_blocking, Work work) {
-    if (!is_blocking) {
-        work.Execute(this);
-        work.Response(ctx);
-        return;
-    }
-
-    // Signal a dummy response to make IPC validation happy
-    // This will be overwritten by the SleepClientThread callback
+void BSD::ExecuteWork(Kernel::HLERequestContext& ctx, Work work) {
+    work.Execute(this);
     work.Response(ctx);
-
-    auto worker = worker_pool.CaptureWorker();
-
-    ctx.SleepClientThread(std::string(sleep_reason), std::numeric_limits<u64>::max(),
-                          worker->Callback<Work>(), worker->KernelEvent());
-
-    worker->SendWork(std::move(work));
 }
 
 std::pair<s32, Errno> BSD::SocketImpl(Domain domain, Type type, Protocol protocol) {
@@ -807,18 +784,6 @@ bool BSD::IsFileDescriptorValid(s32 fd) const noexcept {
     return true;
 }
 
-bool BSD::IsBlockingSocket(s32 fd) const noexcept {
-    // Inform invalid sockets as non-blocking
-    // This way we avoid using a worker thread as it will fail without blocking host
-    if (fd > static_cast<s32>(MAX_FD) || fd < 0) {
-        return false;
-    }
-    if (!file_descriptors[fd]) {
-        return false;
-    }
-    return (file_descriptors[fd]->flags & FLAG_O_NONBLOCK) != 0;
-}
-
 void BSD::BuildErrnoResponse(Kernel::HLERequestContext& ctx, Errno bsd_errno) const noexcept {
     IPC::ResponseBuilder rb{ctx, 4};
 
@@ -827,8 +792,7 @@ void BSD::BuildErrnoResponse(Kernel::HLERequestContext& ctx, Errno bsd_errno) co
     rb.PushEnum(bsd_errno);
 }
 
-BSD::BSD(Core::System& system_, const char* name)
-    : ServiceFramework{system_, name}, worker_pool{system_, this} {
+BSD::BSD(Core::System& system_, const char* name) : ServiceFramework{system_, name} {
     // clang-format off
     static const FunctionInfo functions[] = {
         {0, &BSD::RegisterClient, "RegisterClient"},
diff --git a/src/core/hle/service/sockets/bsd.h b/src/core/hle/service/sockets/bsd.h
index f14713fc42..6da0bfeb2b 100644
--- a/src/core/hle/service/sockets/bsd.h
+++ b/src/core/hle/service/sockets/bsd.h
@@ -11,7 +11,6 @@
 #include "common/common_types.h"
 #include "core/hle/kernel/hle_ipc.h"
 #include "core/hle/service/service.h"
-#include "core/hle/service/sockets/blocking_worker.h"
 #include "core/hle/service/sockets/sockets.h"
 
 namespace Core {
@@ -138,8 +137,7 @@ private:
     void Close(Kernel::HLERequestContext& ctx);
 
     template <typename Work>
-    void ExecuteWork(Kernel::HLERequestContext& ctx, std::string_view sleep_reason,
-                     bool is_blocking, Work work);
+    void ExecuteWork(Kernel::HLERequestContext& ctx, Work work);
 
     std::pair<s32, Errno> SocketImpl(Domain domain, Type type, Protocol protocol);
     std::pair<s32, Errno> PollImpl(std::vector<u8>& write_buffer, std::vector<u8> read_buffer,
@@ -163,15 +161,10 @@ private:
 
     s32 FindFreeFileDescriptorHandle() noexcept;
     bool IsFileDescriptorValid(s32 fd) const noexcept;
-    bool IsBlockingSocket(s32 fd) const noexcept;
 
     void BuildErrnoResponse(Kernel::HLERequestContext& ctx, Errno bsd_errno) const noexcept;
 
     std::array<std::optional<FileDescriptor>, MAX_FD> file_descriptors;
-
-    BlockingWorkerPool<BSD, PollWork, AcceptWork, ConnectWork, RecvWork, RecvFromWork, SendWork,
-                       SendToWork>
-        worker_pool;
 };
 
 class BSDCFG final : public ServiceFramework<BSDCFG> {

From 5d4715cc6af424e8529de5ac1a11d1cca3b3f7cf Mon Sep 17 00:00:00 2001
From: bunnei <bunneidev@gmail.com>
Date: Fri, 11 Dec 2020 16:45:27 -0800
Subject: [PATCH 07/26] hle: kernel: hle_ipc: Remove SleepClientThread.

- This was kind of hacky, and no longer is necessary with service threads.
---
 src/core/hle/kernel/hle_ipc.cpp | 37 ---------------------------------
 src/core/hle/kernel/hle_ipc.h   | 17 ---------------
 2 files changed, 54 deletions(-)

diff --git a/src/core/hle/kernel/hle_ipc.cpp b/src/core/hle/kernel/hle_ipc.cpp
index e75e80ad00..83decf6cf0 100644
--- a/src/core/hle/kernel/hle_ipc.cpp
+++ b/src/core/hle/kernel/hle_ipc.cpp
@@ -46,43 +46,6 @@ void SessionRequestHandler::ClientDisconnected(
     boost::range::remove_erase(connected_sessions, server_session);
 }
 
-std::shared_ptr<WritableEvent> HLERequestContext::SleepClientThread(
-    const std::string& reason, u64 timeout, WakeupCallback&& callback,
-    std::shared_ptr<WritableEvent> writable_event) {
-    // Put the client thread to sleep until the wait event is signaled or the timeout expires.
-
-    if (!writable_event) {
-        // Create event if not provided
-        const auto pair = WritableEvent::CreateEventPair(kernel, "HLE Pause Event: " + reason);
-        writable_event = pair.writable;
-    }
-
-    Handle event_handle = InvalidHandle;
-    {
-        KScopedSchedulerLockAndSleep lock(kernel, event_handle, thread.get(), timeout);
-        thread->SetHLECallback(
-            [context = *this, callback](std::shared_ptr<Thread> thread) mutable -> bool {
-                ThreadWakeupReason reason = thread->GetSignalingResult() == RESULT_TIMEOUT
-                                                ? ThreadWakeupReason::Timeout
-                                                : ThreadWakeupReason::Signal;
-                callback(thread, context, reason);
-                context.WriteToOutgoingCommandBuffer(*thread);
-                return true;
-            });
-        const auto readable_event{writable_event->GetReadableEvent()};
-        writable_event->Clear();
-        thread->SetHLESyncObject(readable_event.get());
-        thread->SetStatus(ThreadStatus::WaitHLEEvent);
-        thread->SetSynchronizationResults(nullptr, RESULT_TIMEOUT);
-        readable_event->AddWaitingThread(thread);
-    }
-    thread->SetHLETimeEvent(event_handle);
-
-    is_thread_waiting = true;
-
-    return writable_event;
-}
-
 HLERequestContext::HLERequestContext(KernelCore& kernel, Core::Memory::Memory& memory,
                                      std::shared_ptr<ServerSession> server_session,
                                      std::shared_ptr<Thread> thread)
diff --git a/src/core/hle/kernel/hle_ipc.h b/src/core/hle/kernel/hle_ipc.h
index c31a654764..b112e1ebd0 100644
--- a/src/core/hle/kernel/hle_ipc.h
+++ b/src/core/hle/kernel/hle_ipc.h
@@ -129,23 +129,6 @@ public:
     using WakeupCallback = std::function<void(
         std::shared_ptr<Thread> thread, HLERequestContext& context, ThreadWakeupReason reason)>;
 
-    /**
-     * Puts the specified guest thread to sleep until the returned event is signaled or until the
-     * specified timeout expires.
-     * @param reason Reason for pausing the thread, to be used for debugging purposes.
-     * @param timeout Timeout in nanoseconds after which the thread will be awoken and the callback
-     * invoked with a Timeout reason.
-     * @param callback Callback to be invoked when the thread is resumed. This callback must write
-     * the entire command response once again, regardless of the state of it before this function
-     * was called.
-     * @param writable_event Event to use to wake up the thread. If unspecified, an event will be
-     * created.
-     * @returns Event that when signaled will resume the thread and call the callback function.
-     */
-    std::shared_ptr<WritableEvent> SleepClientThread(
-        const std::string& reason, u64 timeout, WakeupCallback&& callback,
-        std::shared_ptr<WritableEvent> writable_event = nullptr);
-
     /// Populates this context with data from the requesting process/thread.
     ResultCode PopulateFromIncomingCommandBuffer(const HandleTable& handle_table,
                                                  u32_le* src_cmdbuf);

From 14c825bd1c37b2444e858bf1a75fb77455b4eb52 Mon Sep 17 00:00:00 2001
From: bunnei <bunneidev@gmail.com>
Date: Fri, 11 Dec 2020 22:26:14 -0800
Subject: [PATCH 08/26] video_core: gpu: Refactor out synchronous/asynchronous
 GPU implementations.

- We must always use a GPU thread now, even with synchronous GPU.
---
 src/video_core/CMakeLists.txt       |  5 +-
 src/video_core/framebuffer_config.h | 31 +++++++++++
 src/video_core/gpu.cpp              | 76 +++++++++++++++++++++++--
 src/video_core/gpu.h                | 55 +++++++-----------
 src/video_core/gpu_asynch.cpp       | 86 -----------------------------
 src/video_core/gpu_asynch.h         | 47 ----------------
 src/video_core/gpu_synch.cpp        | 61 --------------------
 src/video_core/gpu_synch.h          | 41 --------------
 src/video_core/gpu_thread.h         |  7 ++-
 src/video_core/video_core.cpp       | 10 +---
 10 files changed, 130 insertions(+), 289 deletions(-)
 create mode 100644 src/video_core/framebuffer_config.h
 delete mode 100644 src/video_core/gpu_asynch.cpp
 delete mode 100644 src/video_core/gpu_asynch.h
 delete mode 100644 src/video_core/gpu_synch.cpp
 delete mode 100644 src/video_core/gpu_synch.h

diff --git a/src/video_core/CMakeLists.txt b/src/video_core/CMakeLists.txt
index 5b73724ce9..4111ce8f7a 100644
--- a/src/video_core/CMakeLists.txt
+++ b/src/video_core/CMakeLists.txt
@@ -47,6 +47,7 @@ add_library(video_core STATIC
     engines/shader_bytecode.h
     engines/shader_header.h
     engines/shader_type.h
+    framebuffer_config.h
     macro/macro.cpp
     macro/macro.h
     macro/macro_hle.cpp
@@ -58,10 +59,6 @@ add_library(video_core STATIC
     fence_manager.h
     gpu.cpp
     gpu.h
-    gpu_asynch.cpp
-    gpu_asynch.h
-    gpu_synch.cpp
-    gpu_synch.h
     gpu_thread.cpp
     gpu_thread.h
     guest_driver.cpp
diff --git a/src/video_core/framebuffer_config.h b/src/video_core/framebuffer_config.h
new file mode 100644
index 0000000000..b86c3a7573
--- /dev/null
+++ b/src/video_core/framebuffer_config.h
@@ -0,0 +1,31 @@
+// Copyright 2020 yuzu Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+namespace Tegra {
+
+/**
+ * Struct describing framebuffer configuration
+ */
+struct FramebufferConfig {
+    enum class PixelFormat : u32 {
+        A8B8G8R8_UNORM = 1,
+        RGB565_UNORM = 4,
+        B8G8R8A8_UNORM = 5,
+    };
+
+    VAddr address{};
+    u32 offset{};
+    u32 width{};
+    u32 height{};
+    u32 stride{};
+    PixelFormat pixel_format{};
+
+    using TransformFlags = Service::NVFlinger::BufferQueue::BufferTransformFlags;
+    TransformFlags transform_flags{};
+    Common::Rectangle<int> crop_rect;
+};
+
+} // namespace Tegra
diff --git a/src/video_core/gpu.cpp b/src/video_core/gpu.cpp
index e2512a7f29..f99a8a0dee 100644
--- a/src/video_core/gpu.cpp
+++ b/src/video_core/gpu.cpp
@@ -10,6 +10,7 @@
 #include "core/core_timing.h"
 #include "core/core_timing_util.h"
 #include "core/frontend/emu_window.h"
+#include "core/hardware_interrupt_manager.h"
 #include "core/memory.h"
 #include "core/settings.h"
 #include "video_core/engines/fermi_2d.h"
@@ -36,7 +37,8 @@ GPU::GPU(Core::System& system_, bool is_async_, bool use_nvdec_)
       kepler_compute{std::make_unique<Engines::KeplerCompute>(system, *memory_manager)},
       maxwell_dma{std::make_unique<Engines::MaxwellDMA>(system, *memory_manager)},
       kepler_memory{std::make_unique<Engines::KeplerMemory>(system, *memory_manager)},
-      shader_notify{std::make_unique<VideoCore::ShaderNotify>()}, is_async{is_async_} {}
+      shader_notify{std::make_unique<VideoCore::ShaderNotify>()}, is_async{is_async_},
+      gpu_thread{system_} {}
 
 GPU::~GPU() = default;
 
@@ -198,10 +200,6 @@ void GPU::SyncGuestHost() {
     renderer->Rasterizer().SyncGuestHost();
 }
 
-void GPU::OnCommandListEnd() {
-    renderer->Rasterizer().ReleaseFences();
-}
-
 enum class GpuSemaphoreOperation {
     AcquireEqual = 0x1,
     WriteLong = 0x2,
@@ -461,4 +459,72 @@ void GPU::ProcessSemaphoreAcquire() {
     }
 }
 
+void GPU::Start() {
+    gpu_thread.StartThread(*renderer, renderer->Context(), *dma_pusher, *cdma_pusher);
+    cpu_context = renderer->GetRenderWindow().CreateSharedContext();
+    cpu_context->MakeCurrent();
+}
+
+void GPU::ObtainContext() {
+    cpu_context->MakeCurrent();
+}
+
+void GPU::ReleaseContext() {
+    cpu_context->DoneCurrent();
+}
+
+void GPU::PushGPUEntries(Tegra::CommandList&& entries) {
+    gpu_thread.SubmitList(std::move(entries));
+}
+
+void GPU::PushCommandBuffer(Tegra::ChCommandHeaderList& entries) {
+    if (!use_nvdec) {
+        return;
+    }
+    // This condition fires when a video stream ends, clear all intermediary data
+    if (entries[0].raw == 0xDEADB33F) {
+        cdma_pusher.reset();
+        return;
+    }
+    if (!cdma_pusher) {
+        cdma_pusher = std::make_unique<Tegra::CDmaPusher>(*this);
+    }
+
+    // SubmitCommandBuffer would make the nvdec operations async, this is not currently working
+    // TODO(ameerj): RE proper async nvdec operation
+    // gpu_thread.SubmitCommandBuffer(std::move(entries));
+
+    cdma_pusher->Push(std::move(entries));
+    cdma_pusher->DispatchCalls();
+}
+
+void GPU::SwapBuffers(const Tegra::FramebufferConfig* framebuffer) {
+    gpu_thread.SwapBuffers(framebuffer);
+}
+
+void GPU::FlushRegion(VAddr addr, u64 size) {
+    gpu_thread.FlushRegion(addr, size);
+}
+
+void GPU::InvalidateRegion(VAddr addr, u64 size) {
+    gpu_thread.InvalidateRegion(addr, size);
+}
+
+void GPU::FlushAndInvalidateRegion(VAddr addr, u64 size) {
+    gpu_thread.FlushAndInvalidateRegion(addr, size);
+}
+
+void GPU::TriggerCpuInterrupt(const u32 syncpoint_id, const u32 value) const {
+    auto& interrupt_manager = system.InterruptManager();
+    interrupt_manager.GPUInterruptSyncpt(syncpoint_id, value);
+}
+
+void GPU::WaitIdle() const {
+    gpu_thread.WaitIdle();
+}
+
+void GPU::OnCommandListEnd() {
+    gpu_thread.OnCommandListEnd();
+}
+
 } // namespace Tegra
diff --git a/src/video_core/gpu.h b/src/video_core/gpu.h
index 660641d041..a2bb4d82de 100644
--- a/src/video_core/gpu.h
+++ b/src/video_core/gpu.h
@@ -15,6 +15,8 @@
 #include "core/hle/service/nvflinger/buffer_queue.h"
 #include "video_core/cdma_pusher.h"
 #include "video_core/dma_pusher.h"
+#include "video_core/framebuffer_config.h"
+#include "video_core/gpu_thread.h"
 
 using CacheAddr = std::uintptr_t;
 [[nodiscard]] inline CacheAddr ToCacheAddr(const void* host_ptr) {
@@ -101,28 +103,6 @@ enum class DepthFormat : u32 {
 struct CommandListHeader;
 class DebugContext;
 
-/**
- * Struct describing framebuffer configuration
- */
-struct FramebufferConfig {
-    enum class PixelFormat : u32 {
-        A8B8G8R8_UNORM = 1,
-        RGB565_UNORM = 4,
-        B8G8R8A8_UNORM = 5,
-    };
-
-    VAddr address;
-    u32 offset;
-    u32 width;
-    u32 height;
-    u32 stride;
-    PixelFormat pixel_format;
-
-    using TransformFlags = Service::NVFlinger::BufferQueue::BufferTransformFlags;
-    TransformFlags transform_flags;
-    Common::Rectangle<int> crop_rect;
-};
-
 namespace Engines {
 class Fermi2D;
 class Maxwell3D;
@@ -141,7 +121,7 @@ enum class EngineID {
 
 class MemoryManager;
 
-class GPU {
+class GPU final {
 public:
     struct MethodCall {
         u32 method{};
@@ -159,7 +139,7 @@ public:
     };
 
     explicit GPU(Core::System& system_, bool is_async_, bool use_nvdec_);
-    virtual ~GPU();
+    ~GPU();
 
     /// Binds a renderer to the GPU.
     void BindRenderer(std::unique_ptr<VideoCore::RendererBase> renderer);
@@ -176,7 +156,7 @@ public:
     /// Synchronizes CPU writes with Host GPU memory.
     void SyncGuestHost();
     /// Signal the ending of command list.
-    virtual void OnCommandListEnd();
+    void OnCommandListEnd();
 
     /// Request a host GPU memory flush from the CPU.
     [[nodiscard]] u64 RequestFlush(VAddr addr, std::size_t size);
@@ -240,7 +220,7 @@ public:
     }
 
     // Waits for the GPU to finish working
-    virtual void WaitIdle() const = 0;
+    void WaitIdle() const;
 
     /// Allows the CPU/NvFlinger to wait on the GPU before presenting a frame.
     void WaitFence(u32 syncpoint_id, u32 value);
@@ -330,34 +310,34 @@ public:
     /// Performs any additional setup necessary in order to begin GPU emulation.
     /// This can be used to launch any necessary threads and register any necessary
     /// core timing events.
-    virtual void Start() = 0;
+    void Start();
 
     /// Obtain the CPU Context
-    virtual void ObtainContext() = 0;
+    void ObtainContext();
 
     /// Release the CPU Context
-    virtual void ReleaseContext() = 0;
+    void ReleaseContext();
 
     /// Push GPU command entries to be processed
-    virtual void PushGPUEntries(Tegra::CommandList&& entries) = 0;
+    void PushGPUEntries(Tegra::CommandList&& entries);
 
     /// Push GPU command buffer entries to be processed
-    virtual void PushCommandBuffer(Tegra::ChCommandHeaderList& entries) = 0;
+    void PushCommandBuffer(Tegra::ChCommandHeaderList& entries);
 
     /// Swap buffers (render frame)
-    virtual void SwapBuffers(const Tegra::FramebufferConfig* framebuffer) = 0;
+    void SwapBuffers(const Tegra::FramebufferConfig* framebuffer);
 
     /// Notify rasterizer that any caches of the specified region should be flushed to Switch memory
-    virtual void FlushRegion(VAddr addr, u64 size) = 0;
+    void FlushRegion(VAddr addr, u64 size);
 
     /// Notify rasterizer that any caches of the specified region should be invalidated
-    virtual void InvalidateRegion(VAddr addr, u64 size) = 0;
+    void InvalidateRegion(VAddr addr, u64 size);
 
     /// Notify rasterizer that any caches of the specified region should be flushed and invalidated
-    virtual void FlushAndInvalidateRegion(VAddr addr, u64 size) = 0;
+    void FlushAndInvalidateRegion(VAddr addr, u64 size);
 
 protected:
-    virtual void TriggerCpuInterrupt(u32 syncpoint_id, u32 value) const = 0;
+    void TriggerCpuInterrupt(u32 syncpoint_id, u32 value) const;
 
 private:
     void ProcessBindMethod(const MethodCall& method_call);
@@ -426,6 +406,9 @@ private:
     u64 last_flush_fence{};
     std::mutex flush_request_mutex;
 
+    VideoCommon::GPUThread::ThreadManager gpu_thread;
+    std::unique_ptr<Core::Frontend::GraphicsContext> cpu_context;
+
     const bool is_async;
 };
 
diff --git a/src/video_core/gpu_asynch.cpp b/src/video_core/gpu_asynch.cpp
deleted file mode 100644
index 6cc091ecd2..0000000000
--- a/src/video_core/gpu_asynch.cpp
+++ /dev/null
@@ -1,86 +0,0 @@
-// Copyright 2019 yuzu Emulator Project
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
-
-#include "core/core.h"
-#include "core/hardware_interrupt_manager.h"
-#include "video_core/gpu_asynch.h"
-#include "video_core/gpu_thread.h"
-#include "video_core/renderer_base.h"
-
-namespace VideoCommon {
-
-GPUAsynch::GPUAsynch(Core::System& system_, bool use_nvdec_)
-    : GPU{system_, true, use_nvdec_}, gpu_thread{system_} {}
-
-GPUAsynch::~GPUAsynch() = default;
-
-void GPUAsynch::Start() {
-    gpu_thread.StartThread(*renderer, renderer->Context(), *dma_pusher, *cdma_pusher);
-    cpu_context = renderer->GetRenderWindow().CreateSharedContext();
-    cpu_context->MakeCurrent();
-}
-
-void GPUAsynch::ObtainContext() {
-    cpu_context->MakeCurrent();
-}
-
-void GPUAsynch::ReleaseContext() {
-    cpu_context->DoneCurrent();
-}
-
-void GPUAsynch::PushGPUEntries(Tegra::CommandList&& entries) {
-    gpu_thread.SubmitList(std::move(entries));
-}
-
-void GPUAsynch::PushCommandBuffer(Tegra::ChCommandHeaderList& entries) {
-    if (!use_nvdec) {
-        return;
-    }
-    // This condition fires when a video stream ends, clear all intermediary data
-    if (entries[0].raw == 0xDEADB33F) {
-        cdma_pusher.reset();
-        return;
-    }
-    if (!cdma_pusher) {
-        cdma_pusher = std::make_unique<Tegra::CDmaPusher>(*this);
-    }
-
-    // SubmitCommandBuffer would make the nvdec operations async, this is not currently working
-    // TODO(ameerj): RE proper async nvdec operation
-    // gpu_thread.SubmitCommandBuffer(std::move(entries));
-
-    cdma_pusher->Push(std::move(entries));
-    cdma_pusher->DispatchCalls();
-}
-
-void GPUAsynch::SwapBuffers(const Tegra::FramebufferConfig* framebuffer) {
-    gpu_thread.SwapBuffers(framebuffer);
-}
-
-void GPUAsynch::FlushRegion(VAddr addr, u64 size) {
-    gpu_thread.FlushRegion(addr, size);
-}
-
-void GPUAsynch::InvalidateRegion(VAddr addr, u64 size) {
-    gpu_thread.InvalidateRegion(addr, size);
-}
-
-void GPUAsynch::FlushAndInvalidateRegion(VAddr addr, u64 size) {
-    gpu_thread.FlushAndInvalidateRegion(addr, size);
-}
-
-void GPUAsynch::TriggerCpuInterrupt(const u32 syncpoint_id, const u32 value) const {
-    auto& interrupt_manager = system.InterruptManager();
-    interrupt_manager.GPUInterruptSyncpt(syncpoint_id, value);
-}
-
-void GPUAsynch::WaitIdle() const {
-    gpu_thread.WaitIdle();
-}
-
-void GPUAsynch::OnCommandListEnd() {
-    gpu_thread.OnCommandListEnd();
-}
-
-} // namespace VideoCommon
diff --git a/src/video_core/gpu_asynch.h b/src/video_core/gpu_asynch.h
deleted file mode 100644
index a384113f4b..0000000000
--- a/src/video_core/gpu_asynch.h
+++ /dev/null
@@ -1,47 +0,0 @@
-// Copyright 2019 yuzu Emulator Project
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
-
-#pragma once
-
-#include "video_core/gpu.h"
-#include "video_core/gpu_thread.h"
-
-namespace Core::Frontend {
-class GraphicsContext;
-}
-
-namespace VideoCore {
-class RendererBase;
-} // namespace VideoCore
-
-namespace VideoCommon {
-
-/// Implementation of GPU interface that runs the GPU asynchronously
-class GPUAsynch final : public Tegra::GPU {
-public:
-    explicit GPUAsynch(Core::System& system_, bool use_nvdec_);
-    ~GPUAsynch() override;
-
-    void Start() override;
-    void ObtainContext() override;
-    void ReleaseContext() override;
-    void PushGPUEntries(Tegra::CommandList&& entries) override;
-    void PushCommandBuffer(Tegra::ChCommandHeaderList& entries) override;
-    void SwapBuffers(const Tegra::FramebufferConfig* framebuffer) override;
-    void FlushRegion(VAddr addr, u64 size) override;
-    void InvalidateRegion(VAddr addr, u64 size) override;
-    void FlushAndInvalidateRegion(VAddr addr, u64 size) override;
-    void WaitIdle() const override;
-
-    void OnCommandListEnd() override;
-
-protected:
-    void TriggerCpuInterrupt(u32 syncpoint_id, u32 value) const override;
-
-private:
-    GPUThread::ThreadManager gpu_thread;
-    std::unique_ptr<Core::Frontend::GraphicsContext> cpu_context;
-};
-
-} // namespace VideoCommon
diff --git a/src/video_core/gpu_synch.cpp b/src/video_core/gpu_synch.cpp
deleted file mode 100644
index 1e9d4b9b2c..0000000000
--- a/src/video_core/gpu_synch.cpp
+++ /dev/null
@@ -1,61 +0,0 @@
-// Copyright 2019 yuzu Emulator Project
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
-
-#include "video_core/gpu_synch.h"
-#include "video_core/renderer_base.h"
-
-namespace VideoCommon {
-
-GPUSynch::GPUSynch(Core::System& system_, bool use_nvdec_) : GPU{system_, false, use_nvdec_} {}
-
-GPUSynch::~GPUSynch() = default;
-
-void GPUSynch::Start() {}
-
-void GPUSynch::ObtainContext() {
-    renderer->Context().MakeCurrent();
-}
-
-void GPUSynch::ReleaseContext() {
-    renderer->Context().DoneCurrent();
-}
-
-void GPUSynch::PushGPUEntries(Tegra::CommandList&& entries) {
-    dma_pusher->Push(std::move(entries));
-    dma_pusher->DispatchCalls();
-}
-
-void GPUSynch::PushCommandBuffer(Tegra::ChCommandHeaderList& entries) {
-    if (!use_nvdec) {
-        return;
-    }
-    // This condition fires when a video stream ends, clears all intermediary data
-    if (entries[0].raw == 0xDEADB33F) {
-        cdma_pusher.reset();
-        return;
-    }
-    if (!cdma_pusher) {
-        cdma_pusher = std::make_unique<Tegra::CDmaPusher>(*this);
-    }
-    cdma_pusher->Push(std::move(entries));
-    cdma_pusher->DispatchCalls();
-}
-
-void GPUSynch::SwapBuffers(const Tegra::FramebufferConfig* framebuffer) {
-    renderer->SwapBuffers(framebuffer);
-}
-
-void GPUSynch::FlushRegion(VAddr addr, u64 size) {
-    renderer->Rasterizer().FlushRegion(addr, size);
-}
-
-void GPUSynch::InvalidateRegion(VAddr addr, u64 size) {
-    renderer->Rasterizer().InvalidateRegion(addr, size);
-}
-
-void GPUSynch::FlushAndInvalidateRegion(VAddr addr, u64 size) {
-    renderer->Rasterizer().FlushAndInvalidateRegion(addr, size);
-}
-
-} // namespace VideoCommon
diff --git a/src/video_core/gpu_synch.h b/src/video_core/gpu_synch.h
deleted file mode 100644
index c5904b8db8..0000000000
--- a/src/video_core/gpu_synch.h
+++ /dev/null
@@ -1,41 +0,0 @@
-// Copyright 2019 yuzu Emulator Project
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
-
-#pragma once
-
-#include "video_core/gpu.h"
-
-namespace Core::Frontend {
-class GraphicsContext;
-}
-
-namespace VideoCore {
-class RendererBase;
-} // namespace VideoCore
-
-namespace VideoCommon {
-
-/// Implementation of GPU interface that runs the GPU synchronously
-class GPUSynch final : public Tegra::GPU {
-public:
-    explicit GPUSynch(Core::System& system_, bool use_nvdec_);
-    ~GPUSynch() override;
-
-    void Start() override;
-    void ObtainContext() override;
-    void ReleaseContext() override;
-    void PushGPUEntries(Tegra::CommandList&& entries) override;
-    void PushCommandBuffer(Tegra::ChCommandHeaderList& entries) override;
-    void SwapBuffers(const Tegra::FramebufferConfig* framebuffer) override;
-    void FlushRegion(VAddr addr, u64 size) override;
-    void InvalidateRegion(VAddr addr, u64 size) override;
-    void FlushAndInvalidateRegion(VAddr addr, u64 size) override;
-    void WaitIdle() const override {}
-
-protected:
-    void TriggerCpuInterrupt([[maybe_unused]] u32 syncpoint_id,
-                             [[maybe_unused]] u32 value) const override {}
-};
-
-} // namespace VideoCommon
diff --git a/src/video_core/gpu_thread.h b/src/video_core/gpu_thread.h
index f1c52cd9e0..0071195d6d 100644
--- a/src/video_core/gpu_thread.h
+++ b/src/video_core/gpu_thread.h
@@ -10,8 +10,9 @@
 #include <optional>
 #include <thread>
 #include <variant>
+
 #include "common/threadsafe_queue.h"
-#include "video_core/gpu.h"
+#include "video_core/framebuffer_config.h"
 
 namespace Tegra {
 struct FramebufferConfig;
@@ -25,6 +26,10 @@ class GraphicsContext;
 class System;
 } // namespace Core
 
+namespace VideoCore {
+    class RendererBase;
+} // namespace VideoCore
+
 namespace VideoCommon::GPUThread {
 
 /// Command to signal to the GPU thread that processing has ended
diff --git a/src/video_core/video_core.cpp b/src/video_core/video_core.cpp
index 837800bfe2..53444e9455 100644
--- a/src/video_core/video_core.cpp
+++ b/src/video_core/video_core.cpp
@@ -7,8 +7,6 @@
 #include "common/logging/log.h"
 #include "core/core.h"
 #include "core/settings.h"
-#include "video_core/gpu_asynch.h"
-#include "video_core/gpu_synch.h"
 #include "video_core/renderer_base.h"
 #include "video_core/renderer_opengl/renderer_opengl.h"
 #include "video_core/renderer_vulkan/renderer_vulkan.h"
@@ -39,13 +37,9 @@ std::unique_ptr<VideoCore::RendererBase> CreateRenderer(
 namespace VideoCore {
 
 std::unique_ptr<Tegra::GPU> CreateGPU(Core::Frontend::EmuWindow& emu_window, Core::System& system) {
-    std::unique_ptr<Tegra::GPU> gpu;
     const bool use_nvdec = Settings::values.use_nvdec_emulation.GetValue();
-    if (Settings::values.use_asynchronous_gpu_emulation.GetValue()) {
-        gpu = std::make_unique<VideoCommon::GPUAsynch>(system, use_nvdec);
-    } else {
-        gpu = std::make_unique<VideoCommon::GPUSynch>(system, use_nvdec);
-    }
+    std::unique_ptr<Tegra::GPU> gpu = std::make_unique<Tegra::GPU>(
+        system, Settings::values.use_asynchronous_gpu_emulation.GetValue(), use_nvdec);
 
     auto context = emu_window.CreateSharedContext();
     const auto scope = context->Acquire();

From 40571c073faa02a6a4301e7f0ce365ef50a400aa Mon Sep 17 00:00:00 2001
From: bunnei <bunneidev@gmail.com>
Date: Sat, 12 Dec 2020 00:24:33 -0800
Subject: [PATCH 09/26] video_core: gpu: Implement synchronous mode using
 threaded GPU.

---
 src/video_core/gpu.cpp        |  7 +++++--
 src/video_core/gpu.h          |  4 ++--
 src/video_core/gpu_thread.cpp | 30 ++++++++++++++++++++++++------
 src/video_core/gpu_thread.h   |  5 +++--
 4 files changed, 34 insertions(+), 12 deletions(-)

diff --git a/src/video_core/gpu.cpp b/src/video_core/gpu.cpp
index f99a8a0dee..6ab06775fd 100644
--- a/src/video_core/gpu.cpp
+++ b/src/video_core/gpu.cpp
@@ -38,7 +38,7 @@ GPU::GPU(Core::System& system_, bool is_async_, bool use_nvdec_)
       maxwell_dma{std::make_unique<Engines::MaxwellDMA>(system, *memory_manager)},
       kepler_memory{std::make_unique<Engines::KeplerMemory>(system, *memory_manager)},
       shader_notify{std::make_unique<VideoCore::ShaderNotify>()}, is_async{is_async_},
-      gpu_thread{system_} {}
+      gpu_thread{system_, is_async_} {}
 
 GPU::~GPU() = default;
 
@@ -524,7 +524,10 @@ void GPU::WaitIdle() const {
 }
 
 void GPU::OnCommandListEnd() {
-    gpu_thread.OnCommandListEnd();
+    if (is_async) {
+        // This command only applies to asynchronous GPU mode
+        gpu_thread.OnCommandListEnd();
+    }
 }
 
 } // namespace Tegra
diff --git a/src/video_core/gpu.h b/src/video_core/gpu.h
index a2bb4d82de..d81e38680a 100644
--- a/src/video_core/gpu.h
+++ b/src/video_core/gpu.h
@@ -406,10 +406,10 @@ private:
     u64 last_flush_fence{};
     std::mutex flush_request_mutex;
 
+    const bool is_async;
+
     VideoCommon::GPUThread::ThreadManager gpu_thread;
     std::unique_ptr<Core::Frontend::GraphicsContext> cpu_context;
-
-    const bool is_async;
 };
 
 #define ASSERT_REG_POSITION(field_name, position)                                                  \
diff --git a/src/video_core/gpu_thread.cpp b/src/video_core/gpu_thread.cpp
index e27218b963..56b9621b18 100644
--- a/src/video_core/gpu_thread.cpp
+++ b/src/video_core/gpu_thread.cpp
@@ -65,7 +65,8 @@ static void RunThread(Core::System& system, VideoCore::RendererBase& renderer,
     }
 }
 
-ThreadManager::ThreadManager(Core::System& system_) : system{system_} {}
+ThreadManager::ThreadManager(Core::System& system_, bool is_async_)
+    : system{system_}, is_async{is_async_} {}
 
 ThreadManager::~ThreadManager() {
     if (!thread.joinable()) {
@@ -97,19 +98,30 @@ void ThreadManager::SwapBuffers(const Tegra::FramebufferConfig* framebuffer) {
 }
 
 void ThreadManager::FlushRegion(VAddr addr, u64 size) {
-    if (!Settings::IsGPULevelHigh()) {
+    if (!is_async) {
+        // Always flush with synchronous GPU mode
         PushCommand(FlushRegionCommand(addr, size));
         return;
     }
-    if (!Settings::IsGPULevelExtreme()) {
-        return;
-    }
-    if (system.Renderer().Rasterizer().MustFlushRegion(addr, size)) {
+
+    // Asynchronous GPU mode
+    switch (Settings::values.gpu_accuracy.GetValue()) {
+    case Settings::GPUAccuracy::Normal:
+        PushCommand(FlushRegionCommand(addr, size));
+        break;
+    case Settings::GPUAccuracy::High:
+        // TODO(bunnei): Is this right? Preserving existing behavior for now
+        break;
+    case Settings::GPUAccuracy::Extreme: {
         auto& gpu = system.GPU();
         u64 fence = gpu.RequestFlush(addr, size);
         PushCommand(GPUTickCommand());
         while (fence > gpu.CurrentFlushRequestFence()) {
         }
+        break;
+    }
+    default:
+        UNIMPLEMENTED_MSG("Unsupported gpu_accuracy {}", Settings::values.gpu_accuracy.GetValue());
     }
 }
 
@@ -134,6 +146,12 @@ void ThreadManager::OnCommandListEnd() {
 u64 ThreadManager::PushCommand(CommandData&& command_data) {
     const u64 fence{++state.last_fence};
     state.queue.Push(CommandDataContainer(std::move(command_data), fence));
+
+    if (!is_async) {
+        // In synchronous GPU mode, block the caller until the command has executed
+        WaitIdle();
+    }
+
     return fence;
 }
 
diff --git a/src/video_core/gpu_thread.h b/src/video_core/gpu_thread.h
index 0071195d6d..2775629e7f 100644
--- a/src/video_core/gpu_thread.h
+++ b/src/video_core/gpu_thread.h
@@ -27,7 +27,7 @@ class System;
 } // namespace Core
 
 namespace VideoCore {
-    class RendererBase;
+class RendererBase;
 } // namespace VideoCore
 
 namespace VideoCommon::GPUThread {
@@ -117,7 +117,7 @@ struct SynchState final {
 /// Class used to manage the GPU thread
 class ThreadManager final {
 public:
-    explicit ThreadManager(Core::System& system_);
+    explicit ThreadManager(Core::System& system_, bool is_async_);
     ~ThreadManager();
 
     /// Creates and starts the GPU thread.
@@ -155,6 +155,7 @@ private:
     Core::System& system;
     std::thread thread;
     std::thread::id thread_id;
+    const bool is_async;
 };
 
 } // namespace VideoCommon::GPUThread

From 916438a9de378f97129df7f5a979bb1a406cda9f Mon Sep 17 00:00:00 2001
From: bunnei <bunneidev@gmail.com>
Date: Sat, 12 Dec 2020 00:50:22 -0800
Subject: [PATCH 10/26] core: settings: Untangle multicore from asynchronous
 GPU.

- Now that GPU is always threaded, we can support multicore with synchronous GPU.
---
 src/core/core.cpp                 |  2 +-
 src/core/settings.cpp             |  5 -----
 src/core/settings.h               |  3 ---
 src/yuzu/configuration/config.cpp |  2 --
 src/yuzu/main.cpp                 | 13 +++----------
 5 files changed, 4 insertions(+), 21 deletions(-)

diff --git a/src/core/core.cpp b/src/core/core.cpp
index 0961c0819b..4dc31ce669 100644
--- a/src/core/core.cpp
+++ b/src/core/core.cpp
@@ -159,7 +159,7 @@ struct System::Impl {
         device_memory = std::make_unique<Core::DeviceMemory>();
 
         is_multicore = Settings::values.use_multi_core.GetValue();
-        is_async_gpu = is_multicore || Settings::values.use_asynchronous_gpu_emulation.GetValue();
+        is_async_gpu = Settings::values.use_asynchronous_gpu_emulation.GetValue();
 
         kernel.SetMulticore(is_multicore);
         cpu_manager.SetMulticore(is_multicore);
diff --git a/src/core/settings.cpp b/src/core/settings.cpp
index 47d9ecf9ab..39306509a7 100644
--- a/src/core/settings.cpp
+++ b/src/core/settings.cpp
@@ -148,9 +148,4 @@ void RestoreGlobalState(bool is_powered_on) {
     values.motion_enabled.SetGlobal(true);
 }
 
-void Sanitize() {
-    values.use_asynchronous_gpu_emulation.SetValue(
-        values.use_asynchronous_gpu_emulation.GetValue() || values.use_multi_core.GetValue());
-}
-
 } // namespace Settings
diff --git a/src/core/settings.h b/src/core/settings.h
index d5f8d2b7ed..0cd3c0c847 100644
--- a/src/core/settings.h
+++ b/src/core/settings.h
@@ -257,7 +257,4 @@ void LogSettings();
 // Restore the global state of all applicable settings in the Values struct
 void RestoreGlobalState(bool is_powered_on);
 
-// Fixes settings that are known to cause issues with the emulator
-void Sanitize();
-
 } // namespace Settings
diff --git a/src/yuzu/configuration/config.cpp b/src/yuzu/configuration/config.cpp
index 9fb2549862..34c2a5f8bd 100644
--- a/src/yuzu/configuration/config.cpp
+++ b/src/yuzu/configuration/config.cpp
@@ -1589,14 +1589,12 @@ void Config::WriteSettingGlobal(const QString& name, const QVariant& value, bool
 
 void Config::Reload() {
     ReadValues();
-    Settings::Sanitize();
     // To apply default value changes
     SaveValues();
     Settings::Apply(Core::System::GetInstance());
 }
 
 void Config::Save() {
-    Settings::Sanitize();
     SaveValues();
 }
 
diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp
index 7aa5152263..ab66d7f935 100644
--- a/src/yuzu/main.cpp
+++ b/src/yuzu/main.cpp
@@ -580,9 +580,8 @@ void GMainWindow::InitializeWidgets() {
         if (emulation_running) {
             return;
         }
-        const bool is_async = !Settings::values.use_asynchronous_gpu_emulation.GetValue() ||
-                              Settings::values.use_multi_core.GetValue();
-        Settings::values.use_asynchronous_gpu_emulation.SetValue(is_async);
+        Settings::values.use_asynchronous_gpu_emulation.SetValue(
+            !Settings::values.use_asynchronous_gpu_emulation.GetValue());
         async_status_button->setChecked(Settings::values.use_asynchronous_gpu_emulation.GetValue());
         Settings::Apply(Core::System::GetInstance());
     });
@@ -599,16 +598,13 @@ void GMainWindow::InitializeWidgets() {
             return;
         }
         Settings::values.use_multi_core.SetValue(!Settings::values.use_multi_core.GetValue());
-        const bool is_async = Settings::values.use_asynchronous_gpu_emulation.GetValue() ||
-                              Settings::values.use_multi_core.GetValue();
-        Settings::values.use_asynchronous_gpu_emulation.SetValue(is_async);
-        async_status_button->setChecked(Settings::values.use_asynchronous_gpu_emulation.GetValue());
         multicore_status_button->setChecked(Settings::values.use_multi_core.GetValue());
         Settings::Apply(Core::System::GetInstance());
     });
     multicore_status_button->setText(tr("MULTICORE"));
     multicore_status_button->setCheckable(true);
     multicore_status_button->setChecked(Settings::values.use_multi_core.GetValue());
+
     statusBar()->insertPermanentWidget(0, multicore_status_button);
     statusBar()->insertPermanentWidget(0, async_status_button);
 
@@ -2533,9 +2529,6 @@ void GMainWindow::UpdateStatusBar() {
 void GMainWindow::UpdateStatusButtons() {
     dock_status_button->setChecked(Settings::values.use_docked_mode.GetValue());
     multicore_status_button->setChecked(Settings::values.use_multi_core.GetValue());
-    Settings::values.use_asynchronous_gpu_emulation.SetValue(
-        Settings::values.use_asynchronous_gpu_emulation.GetValue() ||
-        Settings::values.use_multi_core.GetValue());
     async_status_button->setChecked(Settings::values.use_asynchronous_gpu_emulation.GetValue());
     renderer_status_button->setChecked(Settings::values.renderer_backend.GetValue() ==
                                        Settings::RendererBackend::Vulkan);

From 4991620f899ce21bcde1e57f585fee4081e053d0 Mon Sep 17 00:00:00 2001
From: bunnei <bunneidev@gmail.com>
Date: Sat, 12 Dec 2020 01:37:06 -0800
Subject: [PATCH 11/26] video_core: gpu_thread: Do not wait when system is
 powered down.

---
 src/video_core/gpu_thread.cpp | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/src/video_core/gpu_thread.cpp b/src/video_core/gpu_thread.cpp
index 56b9621b18..1e95d80c3f 100644
--- a/src/video_core/gpu_thread.cpp
+++ b/src/video_core/gpu_thread.cpp
@@ -135,7 +135,8 @@ void ThreadManager::FlushAndInvalidateRegion(VAddr addr, u64 size) {
 }
 
 void ThreadManager::WaitIdle() const {
-    while (state.last_fence > state.signaled_fence.load(std::memory_order_relaxed)) {
+    while (state.last_fence > state.signaled_fence.load(std::memory_order_relaxed) &&
+           system.IsPoweredOn()) {
     }
 }
 

From 6d2f9428c5387abaae03478c9204d164a718ffe5 Mon Sep 17 00:00:00 2001
From: bunnei <bunneidev@gmail.com>
Date: Mon, 14 Dec 2020 17:57:40 -0800
Subject: [PATCH 12/26] core: kernel: Clear process list earlier.

---
 src/core/hle/kernel/kernel.cpp | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/src/core/hle/kernel/kernel.cpp b/src/core/hle/kernel/kernel.cpp
index 1bf4c33557..b3661e4c17 100644
--- a/src/core/hle/kernel/kernel.cpp
+++ b/src/core/hle/kernel/kernel.cpp
@@ -76,6 +76,8 @@ struct KernelCore::Impl {
     }
 
     void Shutdown() {
+        process_list.clear();
+
         next_object_id = 0;
         next_kernel_process_id = Process::InitialKIPIDMin;
         next_user_process_id = Process::ProcessIDMin;
@@ -89,8 +91,6 @@ struct KernelCore::Impl {
 
         cores.clear();
 
-        process_list.clear();
-
         current_process = nullptr;
 
         system_resource_limit = nullptr;

From bea51d948dd4297d6677249562a699d25ad728b0 Mon Sep 17 00:00:00 2001
From: bunnei <bunneidev@gmail.com>
Date: Mon, 14 Dec 2020 17:58:12 -0800
Subject: [PATCH 13/26] hle: service: Ensure system is powered on before
 writing IPC result.

---
 src/core/hle/service/service.cpp | 6 +++++-
 1 file changed, 5 insertions(+), 1 deletion(-)

diff --git a/src/core/hle/service/service.cpp b/src/core/hle/service/service.cpp
index abf3d1ea3b..d55fba831b 100644
--- a/src/core/hle/service/service.cpp
+++ b/src/core/hle/service/service.cpp
@@ -184,7 +184,11 @@ ResultCode ServiceFrameworkBase::HandleSyncRequest(Kernel::HLERequestContext& co
         UNIMPLEMENTED_MSG("command_type={}", context.GetCommandType());
     }
 
-    context.WriteToOutgoingCommandBuffer(context.GetThread());
+    // If emulation was shutdown, we are closing service threads, do not write the response back to
+    // memory that may be shutting down as well.
+    if (system.IsPoweredOn()) {
+        context.WriteToOutgoingCommandBuffer(context.GetThread());
+    }
 
     return RESULT_SUCCESS;
 }

From 6433b1dfd67f4c4f0c4b2e3742dc437a0d1e906e Mon Sep 17 00:00:00 2001
From: bunnei <bunneidev@gmail.com>
Date: Wed, 16 Dec 2020 21:09:06 -0800
Subject: [PATCH 14/26] service: nvflinger: Improve synchronization for
 BufferQueue.

- Use proper mechanisms for blocking on DequeueBuffer.
- Ensure service thread terminates on emulation Shutdown.
---
 .../hle/service/nvflinger/buffer_queue.cpp    | 40 +++++++++++++++++--
 src/core/hle/service/nvflinger/buffer_queue.h | 17 +++++++-
 src/core/hle/service/nvflinger/nvflinger.cpp  | 13 ++++--
 src/core/hle/service/nvflinger/nvflinger.h    |  2 +-
 src/core/hle/service/vi/vi.cpp                | 19 +++++----
 5 files changed, 72 insertions(+), 19 deletions(-)

diff --git a/src/core/hle/service/nvflinger/buffer_queue.cpp b/src/core/hle/service/nvflinger/buffer_queue.cpp
index 377f47e8ef..c8c6a4d645 100644
--- a/src/core/hle/service/nvflinger/buffer_queue.cpp
+++ b/src/core/hle/service/nvflinger/buffer_queue.cpp
@@ -25,7 +25,12 @@ void BufferQueue::SetPreallocatedBuffer(u32 slot, const IGBPBuffer& igbp_buffer)
     ASSERT(slot < buffer_slots);
     LOG_WARNING(Service, "Adding graphics buffer {}", slot);
 
-    free_buffers.push_back(slot);
+    {
+        std::unique_lock lock{queue_mutex};
+        free_buffers.push_back(slot);
+    }
+    condition.notify_one();
+
     buffers[slot] = {
         .slot = slot,
         .status = Buffer::Status::Free,
@@ -41,10 +46,20 @@ void BufferQueue::SetPreallocatedBuffer(u32 slot, const IGBPBuffer& igbp_buffer)
 
 std::optional<std::pair<u32, Service::Nvidia::MultiFence*>> BufferQueue::DequeueBuffer(u32 width,
                                                                                        u32 height) {
+    // Wait for first request before trying to dequeue
+    {
+        std::unique_lock lock{queue_mutex};
+        condition.wait(lock, [this] { return !free_buffers.empty() || !is_connect; });
+    }
 
-    if (free_buffers.empty()) {
+    if (!is_connect) {
+        // Buffer was disconnected while the thread was blocked, this is most likely due to
+        // emulation being stopped
         return std::nullopt;
     }
+
+    std::unique_lock lock{queue_mutex};
+
     auto f_itr = free_buffers.begin();
     auto slot = buffers.size();
 
@@ -97,7 +112,11 @@ void BufferQueue::CancelBuffer(u32 slot, const Service::Nvidia::MultiFence& mult
     buffers[slot].multi_fence = multi_fence;
     buffers[slot].swap_interval = 0;
 
-    free_buffers.push_back(slot);
+    {
+        std::unique_lock lock{queue_mutex};
+        free_buffers.push_back(slot);
+    }
+    condition.notify_one();
 
     buffer_wait_event.writable->Signal();
 }
@@ -127,15 +146,28 @@ void BufferQueue::ReleaseBuffer(u32 slot) {
     ASSERT(buffers[slot].slot == slot);
 
     buffers[slot].status = Buffer::Status::Free;
-    free_buffers.push_back(slot);
+    {
+        std::unique_lock lock{queue_mutex};
+        free_buffers.push_back(slot);
+    }
+    condition.notify_one();
 
     buffer_wait_event.writable->Signal();
 }
 
+void BufferQueue::Connect() {
+    queue_sequence.clear();
+    id = 1;
+    layer_id = 1;
+    is_connect = true;
+}
+
 void BufferQueue::Disconnect() {
     buffers.fill({});
     queue_sequence.clear();
     buffer_wait_event.writable->Signal();
+    is_connect = false;
+    condition.notify_one();
 }
 
 u32 BufferQueue::Query(QueryType type) {
diff --git a/src/core/hle/service/nvflinger/buffer_queue.h b/src/core/hle/service/nvflinger/buffer_queue.h
index e610923cb5..a2f60d9ebf 100644
--- a/src/core/hle/service/nvflinger/buffer_queue.h
+++ b/src/core/hle/service/nvflinger/buffer_queue.h
@@ -4,7 +4,9 @@
 
 #pragma once
 
+#include <condition_variable>
 #include <list>
+#include <mutex>
 #include <optional>
 #include <vector>
 
@@ -99,6 +101,7 @@ public:
     void CancelBuffer(u32 slot, const Service::Nvidia::MultiFence& multi_fence);
     std::optional<std::reference_wrapper<const Buffer>> AcquireBuffer();
     void ReleaseBuffer(u32 slot);
+    void Connect();
     void Disconnect();
     u32 Query(QueryType type);
 
@@ -106,18 +109,28 @@ public:
         return id;
     }
 
+    bool IsConnected() const {
+        return is_connect;
+    }
+
     std::shared_ptr<Kernel::WritableEvent> GetWritableBufferWaitEvent() const;
 
     std::shared_ptr<Kernel::ReadableEvent> GetBufferWaitEvent() const;
 
 private:
-    u32 id;
-    u64 layer_id;
+    BufferQueue(const BufferQueue&) = delete;
+
+    u32 id{};
+    u64 layer_id{};
+    std::atomic_bool is_connect{};
 
     std::list<u32> free_buffers;
     std::array<Buffer, buffer_slots> buffers;
     std::list<u32> queue_sequence;
     Kernel::EventPair buffer_wait_event;
+
+    std::mutex queue_mutex;
+    std::condition_variable condition;
 };
 
 } // namespace Service::NVFlinger
diff --git a/src/core/hle/service/nvflinger/nvflinger.cpp b/src/core/hle/service/nvflinger/nvflinger.cpp
index a7a679df1d..4b35819498 100644
--- a/src/core/hle/service/nvflinger/nvflinger.cpp
+++ b/src/core/hle/service/nvflinger/nvflinger.cpp
@@ -88,6 +88,10 @@ NVFlinger::NVFlinger(Core::System& system) : system(system) {
 }
 
 NVFlinger::~NVFlinger() {
+    for (auto& buffer_queue : buffer_queues) {
+        buffer_queue->Disconnect();
+    }
+
     if (system.IsMulticore()) {
         is_running = false;
         wait_event->Set();
@@ -132,8 +136,9 @@ std::optional<u64> NVFlinger::CreateLayer(u64 display_id) {
 
     const u64 layer_id = next_layer_id++;
     const u32 buffer_queue_id = next_buffer_queue_id++;
-    buffer_queues.emplace_back(system.Kernel(), buffer_queue_id, layer_id);
-    display->CreateLayer(layer_id, buffer_queues.back());
+    buffer_queues.emplace_back(
+        std::make_unique<BufferQueue>(system.Kernel(), buffer_queue_id, layer_id));
+    display->CreateLayer(layer_id, *buffer_queues.back());
     return layer_id;
 }
 
@@ -170,13 +175,13 @@ std::shared_ptr<Kernel::ReadableEvent> NVFlinger::FindVsyncEvent(u64 display_id)
 BufferQueue* NVFlinger::FindBufferQueue(u32 id) {
     const auto guard = Lock();
     const auto itr = std::find_if(buffer_queues.begin(), buffer_queues.end(),
-                                  [id](const auto& queue) { return queue.GetId() == id; });
+                                  [id](const auto& queue) { return queue->GetId() == id; });
 
     if (itr == buffer_queues.end()) {
         return nullptr;
     }
 
-    return &*itr;
+    return itr->get();
 }
 
 VI::Display* NVFlinger::FindDisplay(u64 display_id) {
diff --git a/src/core/hle/service/nvflinger/nvflinger.h b/src/core/hle/service/nvflinger/nvflinger.h
index ce1347d6da..c6765259f3 100644
--- a/src/core/hle/service/nvflinger/nvflinger.h
+++ b/src/core/hle/service/nvflinger/nvflinger.h
@@ -107,7 +107,7 @@ private:
     std::shared_ptr<Nvidia::Module> nvdrv;
 
     std::vector<VI::Display> displays;
-    std::vector<BufferQueue> buffer_queues;
+    std::vector<std::unique_ptr<BufferQueue>> buffer_queues;
 
     /// Id to use for the next layer that is created, this counter is shared among all displays.
     u64 next_layer_id = 1;
diff --git a/src/core/hle/service/vi/vi.cpp b/src/core/hle/service/vi/vi.cpp
index ce0272e590..1051000f81 100644
--- a/src/core/hle/service/vi/vi.cpp
+++ b/src/core/hle/service/vi/vi.cpp
@@ -544,6 +544,12 @@ private:
                                  Settings::values.resolution_factor.GetValue()),
                 static_cast<u32>(static_cast<u32>(DisplayResolution::UndockedHeight) *
                                  Settings::values.resolution_factor.GetValue())};
+
+            {
+                auto& buffer_queue = *nv_flinger.FindBufferQueue(id);
+                buffer_queue.Connect();
+            }
+
             ctx.WriteBuffer(response.Serialize());
             break;
         }
@@ -565,18 +571,15 @@ private:
             const u32 width{request.data.width};
             const u32 height{request.data.height};
 
-            std::optional<std::pair<u32, Service::Nvidia::MultiFence*>> result;
-
-            while (!result) {
-                auto& buffer_queue = *nv_flinger.FindBufferQueue(id);
-                result = buffer_queue.DequeueBuffer(width, height);
-
-                if (result) {
+            auto& buffer_queue = *nv_flinger.FindBufferQueue(id);
+            do {
+                if (auto result = buffer_queue.DequeueBuffer(width, height); result) {
                     // Buffer is available
                     IGBPDequeueBufferResponseParcel response{result->first, *result->second};
                     ctx.WriteBuffer(response.Serialize());
+                    break;
                 }
-            }
+            } while (buffer_queue.IsConnected());
 
             break;
         }

From 994a9fec4e8fb70df10ec7699c641db68d960795 Mon Sep 17 00:00:00 2001
From: bunnei <bunneidev@gmail.com>
Date: Sun, 20 Dec 2020 22:24:05 -0800
Subject: [PATCH 15/26] hle: service: vi: Refactor to grab buffer only once.

---
 src/core/hle/service/vi/vi.cpp | 19 ++++---------------
 1 file changed, 4 insertions(+), 15 deletions(-)

diff --git a/src/core/hle/service/vi/vi.cpp b/src/core/hle/service/vi/vi.cpp
index 1051000f81..968cd16b66 100644
--- a/src/core/hle/service/vi/vi.cpp
+++ b/src/core/hle/service/vi/vi.cpp
@@ -536,6 +536,8 @@ private:
         LOG_DEBUG(Service_VI, "called. id=0x{:08X} transaction={:X}, flags=0x{:08X}", id,
                   transaction, flags);
 
+        auto& buffer_queue = *nv_flinger.FindBufferQueue(id);
+
         switch (transaction) {
         case TransactionId::Connect: {
             IGBPConnectRequestParcel request{ctx.ReadBuffer()};
@@ -545,10 +547,7 @@ private:
                 static_cast<u32>(static_cast<u32>(DisplayResolution::UndockedHeight) *
                                  Settings::values.resolution_factor.GetValue())};
 
-            {
-                auto& buffer_queue = *nv_flinger.FindBufferQueue(id);
-                buffer_queue.Connect();
-            }
+            buffer_queue.Connect();
 
             ctx.WriteBuffer(response.Serialize());
             break;
@@ -556,11 +555,7 @@ private:
         case TransactionId::SetPreallocatedBuffer: {
             IGBPSetPreallocatedBufferRequestParcel request{ctx.ReadBuffer()};
 
-            {
-                auto& buffer_queue = *nv_flinger.FindBufferQueue(id);
-                buffer_queue.SetPreallocatedBuffer(request.data.slot,
-                                                   request.buffer_container.buffer);
-            }
+            buffer_queue.SetPreallocatedBuffer(request.data.slot, request.buffer_container.buffer);
 
             IGBPSetPreallocatedBufferResponseParcel response{};
             ctx.WriteBuffer(response.Serialize());
@@ -571,7 +566,6 @@ private:
             const u32 width{request.data.width};
             const u32 height{request.data.height};
 
-            auto& buffer_queue = *nv_flinger.FindBufferQueue(id);
             do {
                 if (auto result = buffer_queue.DequeueBuffer(width, height); result) {
                     // Buffer is available
@@ -586,7 +580,6 @@ private:
         case TransactionId::RequestBuffer: {
             IGBPRequestBufferRequestParcel request{ctx.ReadBuffer()};
 
-            auto& buffer_queue = *nv_flinger.FindBufferQueue(id);
             auto& buffer = buffer_queue.RequestBuffer(request.slot);
             IGBPRequestBufferResponseParcel response{buffer};
             ctx.WriteBuffer(response.Serialize());
@@ -596,7 +589,6 @@ private:
         case TransactionId::QueueBuffer: {
             IGBPQueueBufferRequestParcel request{ctx.ReadBuffer()};
 
-            auto& buffer_queue = *nv_flinger.FindBufferQueue(id);
             buffer_queue.QueueBuffer(request.data.slot, request.data.transform,
                                      request.data.GetCropRect(), request.data.swap_interval,
                                      request.data.multi_fence);
@@ -608,7 +600,6 @@ private:
         case TransactionId::Query: {
             IGBPQueryRequestParcel request{ctx.ReadBuffer()};
 
-            auto& buffer_queue = *nv_flinger.FindBufferQueue(id);
             const u32 value =
                 buffer_queue.Query(static_cast<NVFlinger::BufferQueue::QueryType>(request.type));
 
@@ -619,7 +610,6 @@ private:
         case TransactionId::CancelBuffer: {
             IGBPCancelBufferRequestParcel request{ctx.ReadBuffer()};
 
-            auto& buffer_queue = *nv_flinger.FindBufferQueue(id);
             buffer_queue.CancelBuffer(request.data.slot, request.data.multi_fence);
 
             IGBPCancelBufferResponseParcel response{};
@@ -630,7 +620,6 @@ private:
             LOG_WARNING(Service_VI, "(STUBBED) called, transaction=Disconnect");
             const auto buffer = ctx.ReadBuffer();
 
-            auto& buffer_queue = *nv_flinger.FindBufferQueue(id);
             buffer_queue.Disconnect();
 
             IGBPEmptyResponseParcel response{};

From 954341763a3d8e0b9734fc2234368c40d65bace4 Mon Sep 17 00:00:00 2001
From: bunnei <bunneidev@gmail.com>
Date: Thu, 24 Dec 2020 23:28:46 -0800
Subject: [PATCH 16/26] gpu: gpu_thread: Ensure MicroProfile is shutdown on
 exit.

---
 src/video_core/gpu_thread.cpp | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/src/video_core/gpu_thread.cpp b/src/video_core/gpu_thread.cpp
index 1e95d80c3f..7e490bcc30 100644
--- a/src/video_core/gpu_thread.cpp
+++ b/src/video_core/gpu_thread.cpp
@@ -4,6 +4,7 @@
 
 #include "common/assert.h"
 #include "common/microprofile.h"
+#include "common/scope_exit.h"
 #include "common/thread.h"
 #include "core/core.h"
 #include "core/frontend/emu_window.h"
@@ -21,6 +22,8 @@ static void RunThread(Core::System& system, VideoCore::RendererBase& renderer,
                       SynchState& state, Tegra::CDmaPusher& cdma_pusher) {
     std::string name = "yuzu:GPU";
     MicroProfileOnThreadCreate(name.c_str());
+    SCOPE_EXIT({ MicroProfileOnThreadExit(); });
+
     Common::SetCurrentThreadName(name.c_str());
     Common::SetCurrentThreadPriority(Common::ThreadPriority::High);
     system.RegisterHostThread();

From d0649d0971fa0e4486b7febe9f24b892c7864548 Mon Sep 17 00:00:00 2001
From: bunnei <bunneidev@gmail.com>
Date: Thu, 24 Dec 2020 23:29:14 -0800
Subject: [PATCH 17/26] core: hle: kernel: Clear process list on boot.

---
 src/core/hle/kernel/kernel.cpp | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/src/core/hle/kernel/kernel.cpp b/src/core/hle/kernel/kernel.cpp
index b3661e4c17..312c64c179 100644
--- a/src/core/hle/kernel/kernel.cpp
+++ b/src/core/hle/kernel/kernel.cpp
@@ -57,6 +57,8 @@ struct KernelCore::Impl {
     }
 
     void Initialize(KernelCore& kernel) {
+        process_list.clear();
+
         RegisterHostThread();
 
         global_scheduler_context = std::make_unique<Kernel::GlobalSchedulerContext>(kernel);
@@ -76,8 +78,6 @@ struct KernelCore::Impl {
     }
 
     void Shutdown() {
-        process_list.clear();
-
         next_object_id = 0;
         next_kernel_process_id = Process::InitialKIPIDMin;
         next_user_process_id = Process::ProcessIDMin;

From 06f8c3dc01d29d3e2926edd4c9689dccda085d1f Mon Sep 17 00:00:00 2001
From: bunnei <bunneidev@gmail.com>
Date: Thu, 24 Dec 2020 23:29:56 -0800
Subject: [PATCH 18/26] core: Do not reset device_memory on shutdown.

- This will be reset on initialization.
---
 src/core/core.cpp | 1 -
 1 file changed, 1 deletion(-)

diff --git a/src/core/core.cpp b/src/core/core.cpp
index 4dc31ce669..1a2002decb 100644
--- a/src/core/core.cpp
+++ b/src/core/core.cpp
@@ -307,7 +307,6 @@ struct System::Impl {
         service_manager.reset();
         cheat_engine.reset();
         telemetry_session.reset();
-        device_memory.reset();
 
         // Close all CPU/threading state
         cpu_manager.Shutdown();

From c7a06908ae1a1c0893876800944e494a0fa03918 Mon Sep 17 00:00:00 2001
From: bunnei <bunneidev@gmail.com>
Date: Sat, 26 Dec 2020 16:05:56 -0800
Subject: [PATCH 19/26] audio_core: stream: Ensure buffer is valid before
 release.

---
 src/audio_core/stream.cpp | 12 ++++++++++--
 1 file changed, 10 insertions(+), 2 deletions(-)

diff --git a/src/audio_core/stream.cpp b/src/audio_core/stream.cpp
index eca2965895..afe68c9ed7 100644
--- a/src/audio_core/stream.cpp
+++ b/src/audio_core/stream.cpp
@@ -130,7 +130,11 @@ bool Stream::ContainsBuffer([[maybe_unused]] Buffer::Tag tag) const {
 std::vector<Buffer::Tag> Stream::GetTagsAndReleaseBuffers(std::size_t max_count) {
     std::vector<Buffer::Tag> tags;
     for (std::size_t count = 0; count < max_count && !released_buffers.empty(); ++count) {
-        tags.push_back(released_buffers.front()->GetTag());
+        if (released_buffers.front()) {
+            tags.push_back(released_buffers.front()->GetTag());
+        } else {
+            ASSERT_MSG(false, "Invalid tag in released_buffers!");
+        }
         released_buffers.pop();
     }
     return tags;
@@ -140,7 +144,11 @@ std::vector<Buffer::Tag> Stream::GetTagsAndReleaseBuffers() {
     std::vector<Buffer::Tag> tags;
     tags.reserve(released_buffers.size());
     while (!released_buffers.empty()) {
-        tags.push_back(released_buffers.front()->GetTag());
+        if (released_buffers.front()) {
+            tags.push_back(released_buffers.front()->GetTag());
+        } else {
+            ASSERT_MSG(false, "Invalid tag in released_buffers!");
+        }
         released_buffers.pop();
     }
     return tags;

From 7d77a3f88f7a1e68d9846ca7c69cce051d1a33d2 Mon Sep 17 00:00:00 2001
From: bunnei <bunneidev@gmail.com>
Date: Mon, 28 Dec 2020 18:23:42 -0800
Subject: [PATCH 20/26] hle: service: Acquire and release a lock on requests.

- This makes it such that we can safely access service members from CoreTiming thread.
---
 src/audio_core/audio_renderer.cpp       | 13 +++++--------
 src/audio_core/audio_renderer.h         |  8 +-------
 src/core/hle/service/audio/audout_u.cpp |  6 ++++--
 src/core/hle/service/audio/audren_u.cpp | 14 +++++++-------
 src/core/hle/service/hid/hid.cpp        |  2 ++
 src/core/hle/service/service.cpp        | 22 ++++++++++------------
 src/core/hle/service/service.h          | 16 ++++++++++++----
 7 files changed, 41 insertions(+), 40 deletions(-)

diff --git a/src/audio_core/audio_renderer.cpp b/src/audio_core/audio_renderer.cpp
index 179560cd72..d2ce8c8147 100644
--- a/src/audio_core/audio_renderer.cpp
+++ b/src/audio_core/audio_renderer.cpp
@@ -11,7 +11,6 @@
 #include "audio_core/info_updater.h"
 #include "audio_core/voice_context.h"
 #include "common/logging/log.h"
-#include "core/hle/kernel/writable_event.h"
 #include "core/memory.h"
 #include "core/settings.h"
 
@@ -71,10 +70,9 @@ namespace {
 namespace AudioCore {
 AudioRenderer::AudioRenderer(Core::Timing::CoreTiming& core_timing, Core::Memory::Memory& memory_,
                              AudioCommon::AudioRendererParameter params,
-                             std::shared_ptr<Kernel::WritableEvent> buffer_event_,
+                             Stream::ReleaseCallback&& release_callback,
                              std::size_t instance_number)
-    : worker_params{params}, buffer_event{buffer_event_},
-      memory_pool_info(params.effect_count + params.voice_count * 4),
+    : worker_params{params}, memory_pool_info(params.effect_count + params.voice_count * 4),
       voice_context(params.voice_count), effect_context(params.effect_count), mix_context(),
       sink_context(params.sink_count), splitter_context(),
       voices(params.voice_count), memory{memory_},
@@ -85,10 +83,9 @@ AudioRenderer::AudioRenderer(Core::Timing::CoreTiming& core_timing, Core::Memory
                                 params.num_splitter_send_channels);
     mix_context.Initialize(behavior_info, params.submix_count + 1, params.effect_count);
     audio_out = std::make_unique<AudioCore::AudioOut>();
-    stream =
-        audio_out->OpenStream(core_timing, params.sample_rate, AudioCommon::STREAM_NUM_CHANNELS,
-                              fmt::format("AudioRenderer-Instance{}", instance_number),
-                              [=]() { buffer_event_->Signal(); });
+    stream = audio_out->OpenStream(
+        core_timing, params.sample_rate, AudioCommon::STREAM_NUM_CHANNELS,
+        fmt::format("AudioRenderer-Instance{}", instance_number), std::move(release_callback));
     audio_out->StartStream(stream);
 
     QueueMixedBuffer(0);
diff --git a/src/audio_core/audio_renderer.h b/src/audio_core/audio_renderer.h
index 90f7eafa4b..18567f6188 100644
--- a/src/audio_core/audio_renderer.h
+++ b/src/audio_core/audio_renderer.h
@@ -27,10 +27,6 @@ namespace Core::Timing {
 class CoreTiming;
 }
 
-namespace Kernel {
-class WritableEvent;
-}
-
 namespace Core::Memory {
 class Memory;
 }
@@ -44,8 +40,7 @@ class AudioRenderer {
 public:
     AudioRenderer(Core::Timing::CoreTiming& core_timing, Core::Memory::Memory& memory_,
                   AudioCommon::AudioRendererParameter params,
-                  std::shared_ptr<Kernel::WritableEvent> buffer_event_,
-                  std::size_t instance_number);
+                  Stream::ReleaseCallback&& release_callback, std::size_t instance_number);
     ~AudioRenderer();
 
     [[nodiscard]] ResultCode UpdateAudioRenderer(const std::vector<u8>& input_params,
@@ -61,7 +56,6 @@ private:
     BehaviorInfo behavior_info{};
 
     AudioCommon::AudioRendererParameter worker_params;
-    std::shared_ptr<Kernel::WritableEvent> buffer_event;
     std::vector<ServerMemoryPoolInfo> memory_pool_info;
     VoiceContext voice_context;
     EffectContext effect_context;
diff --git a/src/core/hle/service/audio/audout_u.cpp b/src/core/hle/service/audio/audout_u.cpp
index 145f47ee23..0cd7971096 100644
--- a/src/core/hle/service/audio/audout_u.cpp
+++ b/src/core/hle/service/audio/audout_u.cpp
@@ -70,8 +70,10 @@ public:
             Kernel::WritableEvent::CreateEventPair(system.Kernel(), "IAudioOutBufferReleased");
 
         stream = audio_core.OpenStream(system.CoreTiming(), audio_params.sample_rate,
-                                       audio_params.channel_count, std::move(unique_name),
-                                       [this] { buffer_event.writable->Signal(); });
+                                       audio_params.channel_count, std::move(unique_name), [this] {
+                                           const auto guard = LockService();
+                                           buffer_event.writable->Signal();
+                                       });
     }
 
 private:
diff --git a/src/core/hle/service/audio/audren_u.cpp b/src/core/hle/service/audio/audren_u.cpp
index 6e7b7316ce..c5c22d0537 100644
--- a/src/core/hle/service/audio/audren_u.cpp
+++ b/src/core/hle/service/audio/audren_u.cpp
@@ -49,16 +49,16 @@ public:
 
         system_event =
             Kernel::WritableEvent::CreateEventPair(system.Kernel(), "IAudioRenderer:SystemEvent");
-        renderer = std::make_unique<AudioCore::AudioRenderer>(system.CoreTiming(), system.Memory(),
-                                                              audren_params, system_event.writable,
-                                                              instance_number);
+        renderer = std::make_unique<AudioCore::AudioRenderer>(
+            system.CoreTiming(), system.Memory(), audren_params,
+            [this]() {
+                const auto guard = LockService();
+                system_event.writable->Signal();
+            },
+            instance_number);
     }
 
 private:
-    void UpdateAudioCallback() {
-        system_event.writable->Signal();
-    }
-
     void GetSampleRate(Kernel::HLERequestContext& ctx) {
         LOG_DEBUG(Service_Audio, "called");
 
diff --git a/src/core/hle/service/hid/hid.cpp b/src/core/hle/service/hid/hid.cpp
index b3c7234e11..8d95f74e6e 100644
--- a/src/core/hle/service/hid/hid.cpp
+++ b/src/core/hle/service/hid/hid.cpp
@@ -78,11 +78,13 @@ IAppletResource::IAppletResource(Core::System& system_)
     pad_update_event = Core::Timing::CreateEvent(
         "HID::UpdatePadCallback",
         [this](std::uintptr_t user_data, std::chrono::nanoseconds ns_late) {
+            const auto guard = LockService();
             UpdateControllers(user_data, ns_late);
         });
     motion_update_event = Core::Timing::CreateEvent(
         "HID::MotionPadCallback",
         [this](std::uintptr_t user_data, std::chrono::nanoseconds ns_late) {
+            const auto guard = LockService();
             UpdateMotion(user_data, ns_late);
         });
 
diff --git a/src/core/hle/service/service.cpp b/src/core/hle/service/service.cpp
index d55fba831b..ff2a5b1db9 100644
--- a/src/core/hle/service/service.cpp
+++ b/src/core/hle/service/service.cpp
@@ -95,9 +95,14 @@ ServiceFrameworkBase::ServiceFrameworkBase(Core::System& system_, const char* se
     : system{system_}, service_name{service_name_}, max_sessions{max_sessions_},
       handler_invoker{handler_invoker_} {}
 
-ServiceFrameworkBase::~ServiceFrameworkBase() = default;
+ServiceFrameworkBase::~ServiceFrameworkBase() {
+    // Wait for other threads to release access before destroying
+    const auto guard = LockService();
+}
 
 void ServiceFrameworkBase::InstallAsService(SM::ServiceManager& service_manager) {
+    const auto guard = LockService();
+
     ASSERT(!port_installed);
 
     auto port = service_manager.RegisterService(service_name, max_sessions).Unwrap();
@@ -106,6 +111,8 @@ void ServiceFrameworkBase::InstallAsService(SM::ServiceManager& service_manager)
 }
 
 void ServiceFrameworkBase::InstallAsNamedPort(Kernel::KernelCore& kernel) {
+    const auto guard = LockService();
+
     ASSERT(!port_installed);
 
     auto [server_port, client_port] =
@@ -115,17 +122,6 @@ void ServiceFrameworkBase::InstallAsNamedPort(Kernel::KernelCore& kernel) {
     port_installed = true;
 }
 
-std::shared_ptr<Kernel::ClientPort> ServiceFrameworkBase::CreatePort(Kernel::KernelCore& kernel) {
-    ASSERT(!port_installed);
-
-    auto [server_port, client_port] =
-        Kernel::ServerPort::CreatePortPair(kernel, max_sessions, service_name);
-    auto port = MakeResult(std::move(server_port)).Unwrap();
-    port->SetHleHandler(shared_from_this());
-    port_installed = true;
-    return client_port;
-}
-
 void ServiceFrameworkBase::RegisterHandlersBase(const FunctionInfoBase* functions, std::size_t n) {
     handlers.reserve(handlers.size() + n);
     for (std::size_t i = 0; i < n; ++i) {
@@ -164,6 +160,8 @@ void ServiceFrameworkBase::InvokeRequest(Kernel::HLERequestContext& ctx) {
 }
 
 ResultCode ServiceFrameworkBase::HandleSyncRequest(Kernel::HLERequestContext& context) {
+    const auto guard = LockService();
+
     switch (context.GetCommandType()) {
     case IPC::CommandType::Close: {
         IPC::ResponseBuilder rb{context, 2};
diff --git a/src/core/hle/service/service.h b/src/core/hle/service/service.h
index 62a1823105..9164455178 100644
--- a/src/core/hle/service/service.h
+++ b/src/core/hle/service/service.h
@@ -5,9 +5,11 @@
 #pragma once
 
 #include <cstddef>
+#include <mutex>
 #include <string>
 #include <boost/container/flat_map.hpp>
 #include "common/common_types.h"
+#include "common/spin_lock.h"
 #include "core/hle/kernel/hle_ipc.h"
 #include "core/hle/kernel/object.h"
 
@@ -68,11 +70,9 @@ public:
     void InstallAsService(SM::ServiceManager& service_manager);
     /// Creates a port pair and registers it on the kernel's global port registry.
     void InstallAsNamedPort(Kernel::KernelCore& kernel);
-    /// Creates and returns an unregistered port for the service.
-    std::shared_ptr<Kernel::ClientPort> CreatePort(Kernel::KernelCore& kernel);
-
+    /// Invokes a service request routine.
     void InvokeRequest(Kernel::HLERequestContext& ctx);
-
+    /// Handles a synchronization request for the service.
     ResultCode HandleSyncRequest(Kernel::HLERequestContext& context) override;
 
 protected:
@@ -80,6 +80,11 @@ protected:
     template <typename Self>
     using HandlerFnP = void (Self::*)(Kernel::HLERequestContext&);
 
+    /// Used to gain exclusive access to the service members, e.g. from CoreTiming thread.
+    [[nodiscard]] std::scoped_lock<Common::SpinLock> LockService() {
+        return std::scoped_lock{lock_service};
+    }
+
     /// System context that the service operates under.
     Core::System& system;
 
@@ -115,6 +120,9 @@ private:
     /// Function used to safely up-cast pointers to the derived class before invoking a handler.
     InvokerFn* handler_invoker;
     boost::container::flat_map<u32, FunctionInfoBase> handlers;
+
+    /// Used to gain exclusive access to the service members, e.g. from CoreTiming thread.
+    Common::SpinLock lock_service;
 };
 
 /**

From f57be2e62603ddd99fbdb1dea1bfc91ee2d7fc04 Mon Sep 17 00:00:00 2001
From: bunnei <bunneidev@gmail.com>
Date: Tue, 29 Dec 2020 01:06:39 -0800
Subject: [PATCH 21/26] hle: kernel: service_thread: Add thread name and take
 weak_ptr of ServerSession.

---
 src/core/hle/kernel/server_session.cpp |  2 +-
 src/core/hle/kernel/service_thread.cpp | 28 +++++++++++++++++---------
 src/core/hle/kernel/service_thread.h   |  3 ++-
 3 files changed, 22 insertions(+), 11 deletions(-)

diff --git a/src/core/hle/kernel/server_session.cpp b/src/core/hle/kernel/server_session.cpp
index ed42452ffd..947f4a133b 100644
--- a/src/core/hle/kernel/server_session.cpp
+++ b/src/core/hle/kernel/server_session.cpp
@@ -34,7 +34,7 @@ ResultVal<std::shared_ptr<ServerSession>> ServerSession::Create(KernelCore& kern
 
     session->name = std::move(name);
     session->parent = std::move(parent);
-    session->service_thread = std::make_unique<ServiceThread>(kernel, 1);
+    session->service_thread = std::make_unique<ServiceThread>(kernel, 1, session->name);
 
     return MakeResult(std::move(session));
 }
diff --git a/src/core/hle/kernel/service_thread.cpp b/src/core/hle/kernel/service_thread.cpp
index 4ceb7e56a4..1c134777f7 100644
--- a/src/core/hle/kernel/service_thread.cpp
+++ b/src/core/hle/kernel/service_thread.cpp
@@ -11,6 +11,7 @@
 
 #include "common/assert.h"
 #include "common/scope_exit.h"
+#include "common/thread.h"
 #include "core/core.h"
 #include "core/hle/kernel/kernel.h"
 #include "core/hle/kernel/server_session.h"
@@ -22,7 +23,7 @@ namespace Kernel {
 
 class ServiceThread::Impl final {
 public:
-    explicit Impl(KernelCore& kernel, std::size_t num_threads);
+    explicit Impl(KernelCore& kernel, std::size_t num_threads, const std::string& name);
     ~Impl();
 
     void QueueSyncRequest(ServerSession& session, std::shared_ptr<HLERequestContext>&& context);
@@ -32,12 +33,16 @@ private:
     std::queue<std::function<void()>> requests;
     std::mutex queue_mutex;
     std::condition_variable condition;
+    const std::string service_name;
     bool stop{};
 };
 
-ServiceThread::Impl::Impl(KernelCore& kernel, std::size_t num_threads) {
+ServiceThread::Impl::Impl(KernelCore& kernel, std::size_t num_threads, const std::string& name)
+    : service_name{name} {
     for (std::size_t i = 0; i < num_threads; ++i)
-        threads.emplace_back([&] {
+        threads.emplace_back([this, &kernel] {
+            Common::SetCurrentThreadName(std::string{"Hle_" + service_name}.c_str());
+
             // Wait for first request before trying to acquire a render context
             {
                 std::unique_lock lock{queue_mutex};
@@ -52,7 +57,7 @@ ServiceThread::Impl::Impl(KernelCore& kernel, std::size_t num_threads) {
                 {
                     std::unique_lock lock{queue_mutex};
                     condition.wait(lock, [this] { return stop || !requests.empty(); });
-                    if (stop && requests.empty()) {
+                    if (stop || requests.empty()) {
                         return;
                     }
                     task = std::move(requests.front());
@@ -68,9 +73,14 @@ void ServiceThread::Impl::QueueSyncRequest(ServerSession& session,
                                            std::shared_ptr<HLERequestContext>&& context) {
     {
         std::unique_lock lock{queue_mutex};
-        requests.emplace([session{SharedFrom(&session)}, context{std::move(context)}]() {
-            session->CompleteSyncRequest(*context);
-            return;
+
+        // ServerSession owns the service thread, so we cannot caption a strong pointer here in the
+        // event that the ServerSession is terminated.
+        std::weak_ptr<ServerSession> weak_ptr{SharedFrom(&session)};
+        requests.emplace([weak_ptr, context{std::move(context)}]() {
+            if (auto strong_ptr = weak_ptr.lock()) {
+                strong_ptr->CompleteSyncRequest(*context);
+            }
         });
     }
     condition.notify_one();
@@ -87,8 +97,8 @@ ServiceThread::Impl::~Impl() {
     }
 }
 
-ServiceThread::ServiceThread(KernelCore& kernel, std::size_t num_threads)
-    : impl{std::make_unique<Impl>(kernel, num_threads)} {}
+ServiceThread::ServiceThread(KernelCore& kernel, std::size_t num_threads, const std::string& name)
+    : impl{std::make_unique<Impl>(kernel, num_threads, name)} {}
 
 ServiceThread::~ServiceThread() = default;
 
diff --git a/src/core/hle/kernel/service_thread.h b/src/core/hle/kernel/service_thread.h
index 91ad7ae856..025ab8fb54 100644
--- a/src/core/hle/kernel/service_thread.h
+++ b/src/core/hle/kernel/service_thread.h
@@ -5,6 +5,7 @@
 #pragma once
 
 #include <memory>
+#include <string>
 
 namespace Kernel {
 
@@ -14,7 +15,7 @@ class ServerSession;
 
 class ServiceThread final {
 public:
-    explicit ServiceThread(KernelCore& kernel, std::size_t num_threads);
+    explicit ServiceThread(KernelCore& kernel, std::size_t num_threads, const std::string& name);
     ~ServiceThread();
 
     void QueueSyncRequest(ServerSession& session, std::shared_ptr<HLERequestContext>&& context);

From dfdac7d38af170683812f3b474ef9d686dfa9ef8 Mon Sep 17 00:00:00 2001
From: bunnei <bunneidev@gmail.com>
Date: Tue, 15 Dec 2020 00:41:48 -0800
Subject: [PATCH 22/26] hle: kernel: Move ServiceThread ownership to
 KernelCore.

- Fixes a circular dependency which prevented threads from being released on shutdown.
---
 src/core/hle/kernel/kernel.cpp         | 21 ++++++++++++++++++++-
 src/core/hle/kernel/kernel.h           | 17 +++++++++++++++++
 src/core/hle/kernel/server_session.cpp | 13 ++++++++++---
 src/core/hle/kernel/server_session.h   |  2 +-
 4 files changed, 48 insertions(+), 5 deletions(-)

diff --git a/src/core/hle/kernel/kernel.cpp b/src/core/hle/kernel/kernel.cpp
index 312c64c179..5f917686fe 100644
--- a/src/core/hle/kernel/kernel.cpp
+++ b/src/core/hle/kernel/kernel.cpp
@@ -8,7 +8,7 @@
 #include <functional>
 #include <memory>
 #include <thread>
-#include <unordered_map>
+#include <unordered_set>
 #include <utility>
 
 #include "common/assert.h"
@@ -35,6 +35,7 @@
 #include "core/hle/kernel/physical_core.h"
 #include "core/hle/kernel/process.h"
 #include "core/hle/kernel/resource_limit.h"
+#include "core/hle/kernel/service_thread.h"
 #include "core/hle/kernel/shared_memory.h"
 #include "core/hle/kernel/synchronization.h"
 #include "core/hle/kernel/thread.h"
@@ -107,6 +108,9 @@ struct KernelCore::Impl {
         std::fill(register_host_thread_keys.begin(), register_host_thread_keys.end(),
                   std::thread::id{});
         std::fill(register_host_thread_values.begin(), register_host_thread_values.end(), 0);
+
+        // Ensures all service threads gracefully shutdown
+        service_threads.clear();
     }
 
     void InitializePhysicalCores() {
@@ -345,6 +349,9 @@ struct KernelCore::Impl {
     std::shared_ptr<Kernel::SharedMemory> irs_shared_mem;
     std::shared_ptr<Kernel::SharedMemory> time_shared_mem;
 
+    // Threads used for services
+    std::unordered_set<std::shared_ptr<Kernel::ServiceThread>> service_threads;
+
     std::array<std::shared_ptr<Thread>, Core::Hardware::NUM_CPU_CORES> suspend_threads{};
     std::array<Core::CPUInterruptHandler, Core::Hardware::NUM_CPU_CORES> interrupts{};
     std::array<std::unique_ptr<Kernel::KScheduler>, Core::Hardware::NUM_CPU_CORES> schedulers{};
@@ -639,4 +646,16 @@ void KernelCore::ExitSVCProfile() {
     MicroProfileLeave(MICROPROFILE_TOKEN(Kernel_SVC), impl->svc_ticks[core]);
 }
 
+std::weak_ptr<Kernel::ServiceThread> KernelCore::CreateServiceThread(const std::string& name) {
+    auto service_thread = std::make_shared<Kernel::ServiceThread>(*this, 1, name);
+    impl->service_threads.emplace(service_thread);
+    return service_thread;
+}
+
+void KernelCore::ReleaseServiceThread(std::weak_ptr<Kernel::ServiceThread> service_thread) {
+    if (auto strong_ptr = service_thread.lock()) {
+        impl->service_threads.erase(strong_ptr);
+    }
+}
+
 } // namespace Kernel
diff --git a/src/core/hle/kernel/kernel.h b/src/core/hle/kernel/kernel.h
index 5846c3f39c..e3169f5a7e 100644
--- a/src/core/hle/kernel/kernel.h
+++ b/src/core/hle/kernel/kernel.h
@@ -42,6 +42,7 @@ class Process;
 class ResourceLimit;
 class KScheduler;
 class SharedMemory;
+class ServiceThread;
 class Synchronization;
 class Thread;
 class TimeManager;
@@ -227,6 +228,22 @@ public:
 
     void ExitSVCProfile();
 
+    /**
+     * Creates an HLE service thread, which are used to execute service routines asynchronously.
+     * While these are allocated per ServerSession, these need to be owned and managed outside of
+     * ServerSession to avoid a circular dependency.
+     * @param name String name for the ServerSession creating this thread, used for debug purposes.
+     * @returns The a weak pointer newly created service thread.
+     */
+    std::weak_ptr<Kernel::ServiceThread> CreateServiceThread(const std::string& name);
+
+    /**
+     * Releases a HLE service thread, instructing KernelCore to free it. This should be called when
+     * the ServerSession associated with the thread is destroyed.
+     * @param service_thread Service thread to release.
+     */
+    void ReleaseServiceThread(std::weak_ptr<Kernel::ServiceThread> service_thread);
+
 private:
     friend class Object;
     friend class Process;
diff --git a/src/core/hle/kernel/server_session.cpp b/src/core/hle/kernel/server_session.cpp
index 947f4a133b..b40fe39163 100644
--- a/src/core/hle/kernel/server_session.cpp
+++ b/src/core/hle/kernel/server_session.cpp
@@ -25,7 +25,10 @@
 namespace Kernel {
 
 ServerSession::ServerSession(KernelCore& kernel) : SynchronizationObject{kernel} {}
-ServerSession::~ServerSession() = default;
+
+ServerSession::~ServerSession() {
+    kernel.ReleaseServiceThread(service_thread);
+}
 
 ResultVal<std::shared_ptr<ServerSession>> ServerSession::Create(KernelCore& kernel,
                                                                 std::shared_ptr<Session> parent,
@@ -34,7 +37,7 @@ ResultVal<std::shared_ptr<ServerSession>> ServerSession::Create(KernelCore& kern
 
     session->name = std::move(name);
     session->parent = std::move(parent);
-    session->service_thread = std::make_unique<ServiceThread>(kernel, 1, session->name);
+    session->service_thread = kernel.CreateServiceThread(session->name);
 
     return MakeResult(std::move(session));
 }
@@ -139,7 +142,11 @@ ResultCode ServerSession::QueueSyncRequest(std::shared_ptr<Thread> thread,
         std::make_shared<HLERequestContext>(kernel, memory, SharedFrom(this), std::move(thread));
 
     context->PopulateFromIncomingCommandBuffer(kernel.CurrentProcess()->GetHandleTable(), cmd_buf);
-    service_thread->QueueSyncRequest(*this, std::move(context));
+
+    if (auto strong_ptr = service_thread.lock()) {
+        strong_ptr->QueueSyncRequest(*this, std::move(context));
+        return RESULT_SUCCESS;
+    }
 
     return RESULT_SUCCESS;
 }
diff --git a/src/core/hle/kernel/server_session.h b/src/core/hle/kernel/server_session.h
index 8466b03e60..e8d1d99ea9 100644
--- a/src/core/hle/kernel/server_session.h
+++ b/src/core/hle/kernel/server_session.h
@@ -167,7 +167,7 @@ private:
     std::string name;
 
     /// Thread to dispatch service requests
-    std::unique_ptr<ServiceThread> service_thread;
+    std::weak_ptr<ServiceThread> service_thread;
 };
 
 } // namespace Kernel

From c192da3f82ea3b2563ce43a340c88ac9a6602f26 Mon Sep 17 00:00:00 2001
From: bunnei <bunneidev@gmail.com>
Date: Tue, 29 Dec 2020 15:55:30 -0800
Subject: [PATCH 23/26] hle: kernel: Manage host thread IDs using TLS.

- Avoids the need to have a large map of host to guest thread IDs.
---
 src/core/hle/kernel/kernel.cpp | 77 ++++++++++++++--------------------
 1 file changed, 31 insertions(+), 46 deletions(-)

diff --git a/src/core/hle/kernel/kernel.cpp b/src/core/hle/kernel/kernel.cpp
index 5f917686fe..022cd413df 100644
--- a/src/core/hle/kernel/kernel.cpp
+++ b/src/core/hle/kernel/kernel.cpp
@@ -104,10 +104,8 @@ struct KernelCore::Impl {
 
         exclusive_monitor.reset();
 
-        num_host_threads = 0;
-        std::fill(register_host_thread_keys.begin(), register_host_thread_keys.end(),
-                  std::thread::id{});
-        std::fill(register_host_thread_values.begin(), register_host_thread_values.end(), 0);
+        // Next host thead ID to use, 0-3 IDs represent core threads, >3 represent others
+        next_host_thread_id = Core::Hardware::NUM_CPU_CORES;
 
         // Ensures all service threads gracefully shutdown
         service_threads.clear();
@@ -190,52 +188,46 @@ struct KernelCore::Impl {
         }
     }
 
+    /// Creates a new host thread ID, should only be called by GetHostThreadId
+    u32 AllocateHostThreadId(std::optional<std::size_t> core_id) {
+        if (core_id) {
+            // The first for slots are reserved for CPU core threads
+            ASSERT(*core_id < Core::Hardware::NUM_CPU_CORES);
+            return static_cast<u32>(*core_id);
+        } else {
+            return next_host_thread_id++;
+        }
+    }
+
+    /// Gets the host thread ID for the caller, allocating a new one if this is the first time
+    u32 GetHostThreadId(std::optional<std::size_t> core_id = std::nullopt) {
+        const thread_local auto host_thread_id{AllocateHostThreadId(core_id)};
+        return host_thread_id;
+    }
+
+    /// Registers a CPU core thread by allocating a host thread ID for it
     void RegisterCoreThread(std::size_t core_id) {
-        const std::thread::id this_id = std::this_thread::get_id();
+        ASSERT(core_id < Core::Hardware::NUM_CPU_CORES);
+        const auto this_id = GetHostThreadId(core_id);
         if (!is_multicore) {
             single_core_thread_id = this_id;
         }
-        const auto end =
-            register_host_thread_keys.begin() + static_cast<ptrdiff_t>(num_host_threads);
-        const auto it = std::find(register_host_thread_keys.begin(), end, this_id);
-        ASSERT(core_id < Core::Hardware::NUM_CPU_CORES);
-        ASSERT(it == end);
-        InsertHostThread(static_cast<u32>(core_id));
     }
 
+    /// Registers a new host thread by allocating a host thread ID for it
     void RegisterHostThread() {
-        const std::thread::id this_id = std::this_thread::get_id();
-        const auto end =
-            register_host_thread_keys.begin() + static_cast<ptrdiff_t>(num_host_threads);
-        const auto it = std::find(register_host_thread_keys.begin(), end, this_id);
-        if (it == end) {
-            InsertHostThread(registered_thread_ids++);
-        }
+        [[maybe_unused]] const auto this_id = GetHostThreadId();
     }
 
-    void InsertHostThread(u32 value) {
-        const size_t index = num_host_threads++;
-        ASSERT_MSG(index < NUM_REGISTRABLE_HOST_THREADS, "Too many host threads");
-        register_host_thread_values[index] = value;
-        register_host_thread_keys[index] = std::this_thread::get_id();
-    }
-
-    [[nodiscard]] u32 GetCurrentHostThreadID() const {
-        const std::thread::id this_id = std::this_thread::get_id();
+    [[nodiscard]] u32 GetCurrentHostThreadID() {
+        const auto this_id = GetHostThreadId();
         if (!is_multicore && single_core_thread_id == this_id) {
             return static_cast<u32>(system.GetCpuManager().CurrentCore());
         }
-        const auto end =
-            register_host_thread_keys.begin() + static_cast<ptrdiff_t>(num_host_threads);
-        const auto it = std::find(register_host_thread_keys.begin(), end, this_id);
-        if (it == end) {
-            return Core::INVALID_HOST_THREAD_ID;
-        }
-        return register_host_thread_values[static_cast<size_t>(
-            std::distance(register_host_thread_keys.begin(), it))];
+        return this_id;
     }
 
-    Core::EmuThreadHandle GetCurrentEmuThreadID() const {
+    [[nodiscard]] Core::EmuThreadHandle GetCurrentEmuThreadID() {
         Core::EmuThreadHandle result = Core::EmuThreadHandle::InvalidHandle();
         result.host_handle = GetCurrentHostThreadID();
         if (result.host_handle >= Core::Hardware::NUM_CPU_CORES) {
@@ -329,15 +321,8 @@ struct KernelCore::Impl {
     std::unique_ptr<Core::ExclusiveMonitor> exclusive_monitor;
     std::vector<Kernel::PhysicalCore> cores;
 
-    // 0-3 IDs represent core threads, >3 represent others
-    std::atomic<u32> registered_thread_ids{Core::Hardware::NUM_CPU_CORES};
-
-    // Number of host threads is a relatively high number to avoid overflowing
-    static constexpr size_t NUM_REGISTRABLE_HOST_THREADS = 1024;
-    std::atomic<size_t> num_host_threads{0};
-    std::array<std::atomic<std::thread::id>, NUM_REGISTRABLE_HOST_THREADS>
-        register_host_thread_keys{};
-    std::array<std::atomic<u32>, NUM_REGISTRABLE_HOST_THREADS> register_host_thread_values{};
+    // Next host thead ID to use, 0-3 IDs represent core threads, >3 represent others
+    std::atomic<u32> next_host_thread_id{Core::Hardware::NUM_CPU_CORES};
 
     // Kernel memory management
     std::unique_ptr<Memory::MemoryManager> memory_manager;
@@ -357,7 +342,7 @@ struct KernelCore::Impl {
     std::array<std::unique_ptr<Kernel::KScheduler>, Core::Hardware::NUM_CPU_CORES> schedulers{};
 
     bool is_multicore{};
-    std::thread::id single_core_thread_id{};
+    u32 single_core_thread_id{};
 
     std::array<u64, Core::Hardware::NUM_CPU_CORES> svc_ticks{};
 

From 69e82d01d5012c0078f65a294ecbb7118707f73c Mon Sep 17 00:00:00 2001
From: bunnei <bunneidev@gmail.com>
Date: Tue, 29 Dec 2020 16:36:35 -0800
Subject: [PATCH 24/26] common: ThreadWorker: Add class to help do asynchronous
 work.

---
 src/common/CMakeLists.txt    |  2 ++
 src/common/thread_worker.cpp | 58 ++++++++++++++++++++++++++++++++++++
 src/common/thread_worker.h   | 30 +++++++++++++++++++
 3 files changed, 90 insertions(+)
 create mode 100644 src/common/thread_worker.cpp
 create mode 100644 src/common/thread_worker.h

diff --git a/src/common/CMakeLists.txt b/src/common/CMakeLists.txt
index 943ff996ee..5c8003eb1f 100644
--- a/src/common/CMakeLists.txt
+++ b/src/common/CMakeLists.txt
@@ -162,6 +162,8 @@ add_library(common STATIC
     thread.cpp
     thread.h
     thread_queue_list.h
+    thread_worker.cpp
+    thread_worker.h
     threadsafe_queue.h
     time_zone.cpp
     time_zone.h
diff --git a/src/common/thread_worker.cpp b/src/common/thread_worker.cpp
new file mode 100644
index 0000000000..8f9bf447ae
--- /dev/null
+++ b/src/common/thread_worker.cpp
@@ -0,0 +1,58 @@
+// Copyright 2020 yuzu emulator team
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#include "common/thread.h"
+#include "common/thread_worker.h"
+
+namespace Common {
+
+ThreadWorker::ThreadWorker(std::size_t num_workers, const std::string& name) {
+    for (std::size_t i = 0; i < num_workers; ++i)
+        threads.emplace_back([this, thread_name{std::string{name}}] {
+            Common::SetCurrentThreadName(thread_name.c_str());
+
+            // Wait for first request
+            {
+                std::unique_lock lock{queue_mutex};
+                condition.wait(lock, [this] { return stop || !requests.empty(); });
+            }
+
+            while (true) {
+                std::function<void()> task;
+
+                {
+                    std::unique_lock lock{queue_mutex};
+                    condition.wait(lock, [this] { return stop || !requests.empty(); });
+                    if (stop || requests.empty()) {
+                        return;
+                    }
+                    task = std::move(requests.front());
+                    requests.pop();
+                }
+
+                task();
+            }
+        });
+}
+
+ThreadWorker::~ThreadWorker() {
+    {
+        std::unique_lock lock{queue_mutex};
+        stop = true;
+    }
+    condition.notify_all();
+    for (std::thread& thread : threads) {
+        thread.join();
+    }
+}
+
+void ThreadWorker::QueueWork(std::function<void()>&& work) {
+    {
+        std::unique_lock lock{queue_mutex};
+        requests.emplace(work);
+    }
+    condition.notify_one();
+}
+
+} // namespace Common
diff --git a/src/common/thread_worker.h b/src/common/thread_worker.h
new file mode 100644
index 0000000000..f1859971fc
--- /dev/null
+++ b/src/common/thread_worker.h
@@ -0,0 +1,30 @@
+// Copyright 2020 yuzu emulator team
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include <atomic>
+#include <functional>
+#include <mutex>
+#include <string>
+#include <vector>
+#include <queue>
+
+namespace Common {
+
+class ThreadWorker final {
+public:
+    explicit ThreadWorker(std::size_t num_workers, const std::string& name);
+    ~ThreadWorker();
+    void QueueWork(std::function<void()>&& work);
+
+private:
+    std::vector<std::thread> threads;
+    std::queue<std::function<void()>> requests;
+    std::mutex queue_mutex;
+    std::condition_variable condition;
+    std::atomic_bool stop{};
+};
+
+} // namespace Common

From a2a0f5318dba2c87578703a5bb69b9fbf7ec526d Mon Sep 17 00:00:00 2001
From: bunnei <bunneidev@gmail.com>
Date: Tue, 29 Dec 2020 16:39:04 -0800
Subject: [PATCH 25/26] hle: kernel: Manage service threads on another thread.

- This is to allow service threads to defer destruction of themselves.
---
 src/core/hle/kernel/kernel.cpp | 29 ++++++++++++++++++++---------
 1 file changed, 20 insertions(+), 9 deletions(-)

diff --git a/src/core/hle/kernel/kernel.cpp b/src/core/hle/kernel/kernel.cpp
index 022cd413df..e8ece81647 100644
--- a/src/core/hle/kernel/kernel.cpp
+++ b/src/core/hle/kernel/kernel.cpp
@@ -15,6 +15,7 @@
 #include "common/logging/log.h"
 #include "common/microprofile.h"
 #include "common/thread.h"
+#include "common/thread_worker.h"
 #include "core/arm/arm_interface.h"
 #include "core/arm/cpu_interrupt_handler.h"
 #include "core/arm/exclusive_monitor.h"
@@ -58,11 +59,11 @@ struct KernelCore::Impl {
     }
 
     void Initialize(KernelCore& kernel) {
-        process_list.clear();
-
         RegisterHostThread();
 
         global_scheduler_context = std::make_unique<Kernel::GlobalSchedulerContext>(kernel);
+        service_thread_manager =
+            std::make_unique<Common::ThreadWorker>(1, "yuzu:ServiceThreadManager");
 
         InitializePhysicalCores();
         InitializeSystemResourceLimit(kernel);
@@ -79,6 +80,12 @@ struct KernelCore::Impl {
     }
 
     void Shutdown() {
+        process_list.clear();
+
+        // Ensures all service threads gracefully shutdown
+        service_thread_manager.reset();
+        service_threads.clear();
+
         next_object_id = 0;
         next_kernel_process_id = Process::InitialKIPIDMin;
         next_user_process_id = Process::ProcessIDMin;
@@ -106,9 +113,6 @@ struct KernelCore::Impl {
 
         // Next host thead ID to use, 0-3 IDs represent core threads, >3 represent others
         next_host_thread_id = Core::Hardware::NUM_CPU_CORES;
-
-        // Ensures all service threads gracefully shutdown
-        service_threads.clear();
     }
 
     void InitializePhysicalCores() {
@@ -337,6 +341,10 @@ struct KernelCore::Impl {
     // Threads used for services
     std::unordered_set<std::shared_ptr<Kernel::ServiceThread>> service_threads;
 
+    // Service threads are managed by a worker thread, so that a calling service thread can queue up
+    // the release of itself
+    std::unique_ptr<Common::ThreadWorker> service_thread_manager;
+
     std::array<std::shared_ptr<Thread>, Core::Hardware::NUM_CPU_CORES> suspend_threads{};
     std::array<Core::CPUInterruptHandler, Core::Hardware::NUM_CPU_CORES> interrupts{};
     std::array<std::unique_ptr<Kernel::KScheduler>, Core::Hardware::NUM_CPU_CORES> schedulers{};
@@ -633,14 +641,17 @@ void KernelCore::ExitSVCProfile() {
 
 std::weak_ptr<Kernel::ServiceThread> KernelCore::CreateServiceThread(const std::string& name) {
     auto service_thread = std::make_shared<Kernel::ServiceThread>(*this, 1, name);
-    impl->service_threads.emplace(service_thread);
+    impl->service_thread_manager->QueueWork(
+        [this, service_thread] { impl->service_threads.emplace(service_thread); });
     return service_thread;
 }
 
 void KernelCore::ReleaseServiceThread(std::weak_ptr<Kernel::ServiceThread> service_thread) {
-    if (auto strong_ptr = service_thread.lock()) {
-        impl->service_threads.erase(strong_ptr);
-    }
+    impl->service_thread_manager->QueueWork([this, service_thread] {
+        if (auto strong_ptr = service_thread.lock()) {
+            impl->service_threads.erase(strong_ptr);
+        }
+    });
 }
 
 } // namespace Kernel

From 82e0eeed21d34accb5f69f7436b2d525b701e68e Mon Sep 17 00:00:00 2001
From: bunnei <bunneidev@gmail.com>
Date: Tue, 29 Dec 2020 16:40:47 -0800
Subject: [PATCH 26/26] hle: kernel: service_thread: Make thread naming more
 consistent.

---
 src/core/hle/kernel/service_thread.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/core/hle/kernel/service_thread.cpp b/src/core/hle/kernel/service_thread.cpp
index 1c134777f7..ee46f3e21b 100644
--- a/src/core/hle/kernel/service_thread.cpp
+++ b/src/core/hle/kernel/service_thread.cpp
@@ -41,7 +41,7 @@ ServiceThread::Impl::Impl(KernelCore& kernel, std::size_t num_threads, const std
     : service_name{name} {
     for (std::size_t i = 0; i < num_threads; ++i)
         threads.emplace_back([this, &kernel] {
-            Common::SetCurrentThreadName(std::string{"Hle_" + service_name}.c_str());
+            Common::SetCurrentThreadName(std::string{"yuzu:HleService:" + service_name}.c_str());
 
             // Wait for first request before trying to acquire a render context
             {