From 3d000c834b6ffb8a36b9dfb4b33a20822eac3926 Mon Sep 17 00:00:00 2001 From: Subv Date: Thu, 9 Nov 2017 18:13:11 -0500 Subject: [PATCH 1/4] Kernel/Threads: Implement an SleepClientThread function for HLERequestContext-based services to make performing async tasks on the host while in an HLE service function easier. An HLE service function that wants to perform an async operation should put the caller guest thread to sleep using SleepClientThread, passing in a callback to execute when the thread is resumed. SleepClientThread returns a Kernel::Event that should be signaled to resume the guest thread when the host async operation completes. --- src/core/hle/kernel/hle_ipc.cpp | 35 ++++++++++++++++++++++++++++++++ src/core/hle/kernel/hle_ipc.h | 21 +++++++++++++++++++ src/core/hle/kernel/thread.cpp | 3 ++- src/core/hle/service/service.cpp | 12 +++++++++-- 4 files changed, 68 insertions(+), 3 deletions(-) diff --git a/src/core/hle/kernel/hle_ipc.cpp b/src/core/hle/kernel/hle_ipc.cpp index 303b898f17..68e6ff30a0 100644 --- a/src/core/hle/kernel/hle_ipc.cpp +++ b/src/core/hle/kernel/hle_ipc.cpp @@ -6,6 +6,7 @@ #include #include "common/assert.h" #include "common/common_types.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" @@ -24,6 +25,40 @@ void SessionRequestHandler::ClientDisconnected(SharedPtr server_s boost::range::remove_erase(connected_sessions, server_session); } +SharedPtr HLERequestContext::SleepClientThread(SharedPtr 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, + SharedPtr object) mutable { + ASSERT(thread->status == THREADSTATUS_WAIT_HLE_EVENT); + callback(thread, context, reason); + + auto& process = thread->owner_process; + // We must copy the entire command buffer *plus* the entire static buffers area, since + // the translation might need to read from it in order to retrieve the StaticBuffer + // target addresses. + std::array cmd_buff; + Memory::ReadBlock(*process, thread->GetCommandBufferAddress(), cmd_buff.data(), + cmd_buff.size() * sizeof(u32)); + context.WriteToOutgoingCommandBuffer(cmd_buff.data(), *process, Kernel::g_handle_table); + // Copy the translated command buffer back into the thread's command buffer area. + Memory::WriteBlock(*process, thread->GetCommandBufferAddress(), cmd_buff.data(), + cmd_buff.size() * sizeof(u32)); + }; + + 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 session) : session(std::move(session)) { cmd_buf[0] = 0; diff --git a/src/core/hle/kernel/hle_ipc.h b/src/core/hle/kernel/hle_ipc.h index 2a246fa27c..856bb54be2 100644 --- a/src/core/hle/kernel/hle_ipc.h +++ b/src/core/hle/kernel/hle_ipc.h @@ -6,6 +6,7 @@ #include #include +#include #include #include #include "common/common_types.h" @@ -22,6 +23,8 @@ namespace Kernel { class HandleTable; class Process; +class Thread; +class Event; /** * Interface implemented by HLE Session handlers. @@ -140,6 +143,24 @@ public: return session; } + using WakeupCallback = std::function 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 SleepClientThread(SharedPtr thread, const std::string& reason, + u64 timeout, WakeupCallback&& callback); + /** * Resolves a object id from the request command buffer into a pointer to an object. See the * "HLE handle protocol" section in the class documentation for more details. diff --git a/src/core/hle/kernel/thread.cpp b/src/core/hle/kernel/thread.cpp index 7360074017..2d447bf89a 100644 --- a/src/core/hle/kernel/thread.cpp +++ b/src/core/hle/kernel/thread.cpp @@ -196,7 +196,8 @@ static void ThreadWakeupCallback(u64 thread_handle, int cycles_late) { } if (thread->status == THREADSTATUS_WAIT_SYNCH_ANY || - thread->status == THREADSTATUS_WAIT_SYNCH_ALL || thread->status == THREADSTATUS_WAIT_ARB) { + thread->status == THREADSTATUS_WAIT_SYNCH_ALL || thread->status == THREADSTATUS_WAIT_ARB || + thread->status == THREADSTATUS_WAIT_HLE_EVENT) { // Invoke the wakeup callback before clearing the wait objects if (thread->wakeup_callback) diff --git a/src/core/hle/service/service.cpp b/src/core/hle/service/service.cpp index 039c86a07b..60bfd48042 100644 --- a/src/core/hle/service/service.cpp +++ b/src/core/hle/service/service.cpp @@ -181,8 +181,16 @@ void ServiceFrameworkBase::HandleSyncRequest(SharedPtr server_ses LOG_TRACE(Service, "%s", MakeFunctionString(info->name, GetServiceName().c_str(), cmd_buf).c_str()); handler_invoker(this, info->handler_callback, context); - context.WriteToOutgoingCommandBuffer(cmd_buf, *Kernel::g_current_process, - Kernel::g_handle_table); + + auto thread = Kernel::GetCurrentThread(); + ASSERT(thread->status == THREADSTATUS_RUNNING || thread->status == THREADSTATUS_WAIT_HLE_EVENT); + // Only write the response immediately if the thread is still running. If the HLE handler put + // the thread to sleep then the writing of the command buffer will be deferred to the wakeup + // callback. + if (thread->status == THREADSTATUS_RUNNING) { + context.WriteToOutgoingCommandBuffer(cmd_buf, *Kernel::g_current_process, + Kernel::g_handle_table); + } } //////////////////////////////////////////////////////////////////////////////////////////////////// From 2052a201c0f60cfff1b043cf1ff631e57f2ad688 Mon Sep 17 00:00:00 2001 From: CDAGaming Date: Wed, 29 Nov 2017 17:28:58 -0600 Subject: [PATCH 2/4] Fix Clang Format Error --- src/core/hle/kernel/hle_ipc.cpp | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/core/hle/kernel/hle_ipc.cpp b/src/core/hle/kernel/hle_ipc.cpp index 68e6ff30a0..19d6d1bca4 100644 --- a/src/core/hle/kernel/hle_ipc.cpp +++ b/src/core/hle/kernel/hle_ipc.cpp @@ -29,9 +29,8 @@ SharedPtr HLERequestContext::SleepClientThread(SharedPtr 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, - SharedPtr object) mutable { + thread->wakeup_callback = [ context = *this, callback ]( + ThreadWakeupReason reason, SharedPtr thread, SharedPtr object) mutable { ASSERT(thread->status == THREADSTATUS_WAIT_HLE_EVENT); callback(thread, context, reason); From b0f43902475fb109cf08c1c9f8250cf64d7929dd Mon Sep 17 00:00:00 2001 From: Subv Date: Wed, 21 Feb 2018 22:03:46 -0500 Subject: [PATCH 3/4] HLE: Use std::chrono::nanoseconds instead of a plain u64 in SleepClientThread. --- src/core/hle/kernel/hle_ipc.cpp | 7 ++++--- src/core/hle/kernel/hle_ipc.h | 5 +++-- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/src/core/hle/kernel/hle_ipc.cpp b/src/core/hle/kernel/hle_ipc.cpp index 19d6d1bca4..60e19ed5cc 100644 --- a/src/core/hle/kernel/hle_ipc.cpp +++ b/src/core/hle/kernel/hle_ipc.cpp @@ -26,7 +26,8 @@ void SessionRequestHandler::ClientDisconnected(SharedPtr server_s } SharedPtr HLERequestContext::SleepClientThread(SharedPtr thread, - const std::string& reason, u64 timeout, + const std::string& reason, + std::chrono::nanoseconds 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 ]( @@ -52,8 +53,8 @@ SharedPtr HLERequestContext::SleepClientThread(SharedPtr thread, thread->wait_objects = {event}; event->AddWaitingThread(thread); - if (timeout > 0) - thread->WakeAfterDelay(timeout); + if (timeout.count() > 0) + thread->WakeAfterDelay(timeout.count()); return event; } diff --git a/src/core/hle/kernel/hle_ipc.h b/src/core/hle/kernel/hle_ipc.h index 856bb54be2..89928ac56a 100644 --- a/src/core/hle/kernel/hle_ipc.h +++ b/src/core/hle/kernel/hle_ipc.h @@ -5,6 +5,7 @@ #pragma once #include +#include #include #include #include @@ -146,7 +147,7 @@ public: using WakeupCallback = std::function 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. @@ -159,7 +160,7 @@ public: * @returns Event that when signaled will resume the thread and call the callback function. */ SharedPtr SleepClientThread(SharedPtr thread, const std::string& reason, - u64 timeout, WakeupCallback&& callback); + std::chrono::nanoseconds timeout, WakeupCallback&& callback); /** * Resolves a object id from the request command buffer into a pointer to an object. See the From 42ab8d9d0be74727cd25c05953932e149a34af20 Mon Sep 17 00:00:00 2001 From: wwylele Date: Thu, 22 Feb 2018 16:12:39 +0200 Subject: [PATCH 4/4] HLE: specify that the command buffer is an array of u32_le --- src/core/hle/kernel/hle_ipc.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/core/hle/kernel/hle_ipc.cpp b/src/core/hle/kernel/hle_ipc.cpp index 60e19ed5cc..d408f1e1dc 100644 --- a/src/core/hle/kernel/hle_ipc.cpp +++ b/src/core/hle/kernel/hle_ipc.cpp @@ -39,7 +39,7 @@ SharedPtr HLERequestContext::SleepClientThread(SharedPtr thread, // We must copy the entire command buffer *plus* the entire static buffers area, since // the translation might need to read from it in order to retrieve the StaticBuffer // target addresses. - std::array cmd_buff; + std::array cmd_buff; Memory::ReadBlock(*process, thread->GetCommandBufferAddress(), cmd_buff.data(), cmd_buff.size() * sizeof(u32)); context.WriteToOutgoingCommandBuffer(cmd_buff.data(), *process, Kernel::g_handle_table);