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.
This commit is contained in:
Subv 2017-09-29 10:00:28 -05:00
parent 0a308e224c
commit f9d55ecf3f
2 changed files with 85 additions and 3 deletions

View File

@ -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>(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<SM::ServiceManager>();
@ -280,4 +322,4 @@ void Shutdown() {
g_kernel_named_ports.clear();
LOG_DEBUG(Service, "shutdown OK");
}
}
} // namespace Service

View File

@ -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<void(Kernel::SharedPtr<Kernel::Thread> thread)>;
friend ThreadContinuationToken SleepClientThread(const std::string& reason, Callback callback);
friend void ContinueClientThread(ThreadContinuationToken& token);
bool IsValid();
private:
Kernel::SharedPtr<Kernel::Event> event;
Kernel::SharedPtr<Kernel::Thread> 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<Kernel::ClientPort> port);
/// Adds a service to the services table
void AddService(Interface* interface_);
} // namespace
} // namespace Service