From c86af6939c9777d78fbc48b81eb090553762d3d4 Mon Sep 17 00:00:00 2001
From: bunnei <bunneidev@gmail.com>
Date: Sun, 18 Mar 2018 20:22:46 -0400
Subject: [PATCH] hle_ipc: Add SleepClientThread to block current thread within
 HLE routines.

---
 src/core/hle/kernel/hle_ipc.cpp | 27 +++++++++++++++++++++++++++
 src/core/hle/kernel/hle_ipc.h   | 20 ++++++++++++++++++++
 2 files changed, 47 insertions(+)

diff --git a/src/core/hle/kernel/hle_ipc.cpp b/src/core/hle/kernel/hle_ipc.cpp
index aae14f09e7..2937567902 100644
--- a/src/core/hle/kernel/hle_ipc.cpp
+++ b/src/core/hle/kernel/hle_ipc.cpp
@@ -7,6 +7,7 @@
 #include "common/common_funcs.h"
 #include "common/common_types.h"
 #include "core/hle/ipc_helpers.h"
+#include "core/hle/kernel/event.h"
 #include "core/hle/kernel/handle_table.h"
 #include "core/hle/kernel/hle_ipc.h"
 #include "core/hle/kernel/kernel.h"
@@ -26,6 +27,32 @@ void SessionRequestHandler::ClientDisconnected(SharedPtr<ServerSession> server_s
     boost::range::remove_erase(connected_sessions, server_session);
 }
 
+SharedPtr<Event> HLERequestContext::SleepClientThread(SharedPtr<Thread> thread,
+                                                      const std::string& reason, u64 timeout,
+                                                      WakeupCallback&& callback) {
+
+    // Put the client thread to sleep until the wait event is signaled or the timeout expires.
+    thread->wakeup_callback =
+        [context = *this, callback](ThreadWakeupReason reason, SharedPtr<Thread> thread,
+                                    SharedPtr<WaitObject> object, size_t index) mutable -> bool {
+        ASSERT(thread->status == THREADSTATUS_WAIT_HLE_EVENT);
+        callback(thread, context, reason);
+        context.WriteToOutgoingCommandBuffer(*thread);
+        return true;
+    };
+
+    auto event = Kernel::Event::Create(Kernel::ResetType::OneShot, "HLE Pause Event: " + reason);
+    thread->status = THREADSTATUS_WAIT_HLE_EVENT;
+    thread->wait_objects = {event};
+    event->AddWaitingThread(thread);
+
+    if (timeout > 0) {
+        thread->WakeAfterDelay(timeout);
+    }
+
+    return event;
+}
+
 HLERequestContext::HLERequestContext(SharedPtr<Kernel::ServerSession> server_session)
     : server_session(std::move(server_session)) {
     cmd_buf[0] = 0;
diff --git a/src/core/hle/kernel/hle_ipc.h b/src/core/hle/kernel/hle_ipc.h
index b5cc0d0af1..8b35da4c94 100644
--- a/src/core/hle/kernel/hle_ipc.h
+++ b/src/core/hle/kernel/hle_ipc.h
@@ -6,6 +6,7 @@
 
 #include <array>
 #include <memory>
+#include <string>
 #include <vector>
 #include <boost/container/small_vector.hpp>
 #include "common/common_types.h"
@@ -25,6 +26,7 @@ class Domain;
 class HandleTable;
 class HLERequestContext;
 class Process;
+class Event;
 
 /**
  * Interface implemented by HLE Session handlers.
@@ -103,6 +105,24 @@ public:
         return server_session;
     }
 
+    using WakeupCallback = std::function<void(SharedPtr<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 thread Thread to be put to sleep.
+     * @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.
+     * @returns Event that when signaled will resume the thread and call the callback function.
+     */
+    SharedPtr<Event> SleepClientThread(SharedPtr<Thread> thread, const std::string& reason,
+                                       u64 timeout, WakeupCallback&& callback);
+
     void ParseCommandBuffer(u32_le* src_cmdbuf, bool incoming);
 
     /// Populates this context with data from the requesting process/thread.
-- 
GitLab