From f9d55ecf3f80d580c7cf8fe2179118cb1362eb11 Mon Sep 17 00:00:00 2001 From: Subv Date: Fri, 29 Sep 2017 10:00:28 -0500 Subject: [PATCH] HLE: Implemented SleepClientThread and ContinueClientThread functions 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 ThreadContinuationToken that should be stored and used with ContinueClientThread to resume the guest thread when the host async operation completes. --- src/core/hle/service/service.cpp | 44 +++++++++++++++++++++++++++++++- src/core/hle/service/service.h | 44 ++++++++++++++++++++++++++++++-- 2 files changed, 85 insertions(+), 3 deletions(-) diff --git a/src/core/hle/service/service.cpp b/src/core/hle/service/service.cpp index f267aad74..51e8a015e 100644 --- a/src/core/hle/service/service.cpp +++ b/src/core/hle/service/service.cpp @@ -9,6 +9,7 @@ #include "common/string_util.h" #include "core/hle/ipc.h" #include "core/hle/kernel/client_port.h" +#include "core/hle/kernel/event.h" #include "core/hle/kernel/process.h" #include "core/hle/kernel/server_port.h" #include "core/hle/kernel/server_session.h" @@ -210,6 +211,47 @@ void AddService(Interface* interface_) { server_port->SetHleHandler(std::shared_ptr(interface_)); } +bool ThreadContinuationToken::IsValid() { + return thread != nullptr && event != nullptr; +} + +ThreadContinuationToken SleepClientThread(const std::string& reason, + ThreadContinuationToken::Callback callback) { + auto thread = Kernel::GetCurrentThread(); + + ASSERT(thread->status == THREADSTATUS_RUNNING); + + ThreadContinuationToken token; + + token.event = Kernel::Event::Create(Kernel::ResetType::OneShot, "HLE Pause Event: " + reason); + token.thread = thread; + token.callback = std::move(callback); + token.pause_reason = std::move(reason); + + // Make the thread wait on our newly created event, it will be signaled when + // ContinueClientThread is called. + thread->status = THREADSTATUS_WAIT_HLE_EVENT; + thread->wait_objects = {token.event}; + token.event->AddWaitingThread(thread); + + return token; +} + +void ContinueClientThread(ThreadContinuationToken& token) { + ASSERT_MSG(token.IsValid(), "Invalid continuation token"); + ASSERT(token.thread->status == THREADSTATUS_WAIT_HLE_EVENT); + + // Signal the event to wake up the thread + token.event->Signal(); + ASSERT(token.thread->status == THREADSTATUS_READY); + + token.callback(token.thread); + + token.event = nullptr; + token.thread = nullptr; + token.callback = nullptr; +} + /// Initialize ServiceManager void Init() { SM::g_service_manager = std::make_shared(); @@ -280,4 +322,4 @@ void Shutdown() { g_kernel_named_ports.clear(); LOG_DEBUG(Service, "shutdown OK"); } -} +} // namespace Service diff --git a/src/core/hle/service/service.h b/src/core/hle/service/service.h index 281ff99bb..084994816 100644 --- a/src/core/hle/service/service.h +++ b/src/core/hle/service/service.h @@ -20,7 +20,8 @@ namespace Kernel { class ClientPort; class ServerPort; class ServerSession; -} +class Event; +} // namespace Kernel namespace Service { @@ -249,6 +250,45 @@ private: } }; +/* + * Token representing a pause request for a guest thread from an HLE service function. + * Using this token a function can put a guest thread to sleep to defer returning a result from + * SendSyncRequest until an async operation completes on the host. To use it, call SleepClientThread + * to create a specific continuation token for the current thread, perform your async operation, and + * then call ContinueClientThread passing in the returned token as a parameter. + */ +class ThreadContinuationToken { +public: + using Callback = std::function thread)>; + friend ThreadContinuationToken SleepClientThread(const std::string& reason, Callback callback); + friend void ContinueClientThread(ThreadContinuationToken& token); + + bool IsValid(); + +private: + Kernel::SharedPtr event; + Kernel::SharedPtr thread; + Callback callback; + std::string pause_reason; +}; + +/* + * Puts the current guest thread to sleep and returns a ThreadContinuationToken to be used with + * ContinueClientThread. + * @param reason Reason for pausing the thread, to be used for debugging purposes. + * @param callback Callback to be invoked when the thread is resumed by ContinueClientThread. + * @returns ThreadContinuationToken representing the pause request. + */ +ThreadContinuationToken SleepClientThread(const std::string& reason, + ThreadContinuationToken::Callback callback); + +/* + * Completes a continuation request and resumes the associated guest thread. + * This function invalidates the token. + * @param token The continuation token associated with the continuation request. + */ +void ContinueClientThread(ThreadContinuationToken& token); + /// Initialize ServiceManager void Init(); @@ -263,4 +303,4 @@ void AddNamedPort(std::string name, Kernel::SharedPtr port); /// Adds a service to the services table void AddService(Interface* interface_); -} // namespace +} // namespace Service