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