Merge pull request #2968 from Subv/hle_thread_pause
Kernel/Threads: Add a new thread status that will allow using a Kernel::Event to put a guest thread to sleep inside an HLE handler until said event is signaled
This commit is contained in:
		@@ -161,6 +161,9 @@ QString WaitTreeThread::GetText() const {
 | 
			
		||||
    case THREADSTATUS_WAIT_SYNCH_ANY:
 | 
			
		||||
        status = tr("waiting for objects");
 | 
			
		||||
        break;
 | 
			
		||||
    case THREADSTATUS_WAIT_HLE_EVENT:
 | 
			
		||||
        status = tr("waiting for HLE return");
 | 
			
		||||
        break;
 | 
			
		||||
    case THREADSTATUS_DORMANT:
 | 
			
		||||
        status = tr("dormant");
 | 
			
		||||
        break;
 | 
			
		||||
@@ -189,6 +192,7 @@ QColor WaitTreeThread::GetColor() const {
 | 
			
		||||
        return QColor(Qt::GlobalColor::darkCyan);
 | 
			
		||||
    case THREADSTATUS_WAIT_SYNCH_ALL:
 | 
			
		||||
    case THREADSTATUS_WAIT_SYNCH_ANY:
 | 
			
		||||
    case THREADSTATUS_WAIT_HLE_EVENT:
 | 
			
		||||
        return QColor(Qt::GlobalColor::red);
 | 
			
		||||
    case THREADSTATUS_DORMANT:
 | 
			
		||||
        return QColor(Qt::GlobalColor::darkCyan);
 | 
			
		||||
@@ -237,7 +241,8 @@ std::vector<std::unique_ptr<WaitTreeItem>> WaitTreeThread::GetChildren() const {
 | 
			
		||||
        list.push_back(std::make_unique<WaitTreeMutexList>(thread.held_mutexes));
 | 
			
		||||
    }
 | 
			
		||||
    if (thread.status == THREADSTATUS_WAIT_SYNCH_ANY ||
 | 
			
		||||
        thread.status == THREADSTATUS_WAIT_SYNCH_ALL) {
 | 
			
		||||
        thread.status == THREADSTATUS_WAIT_SYNCH_ALL ||
 | 
			
		||||
        thread.status == THREADSTATUS_WAIT_HLE_EVENT) {
 | 
			
		||||
        list.push_back(std::make_unique<WaitTreeObjectList>(thread.wait_objects,
 | 
			
		||||
                                                            thread.IsSleepingOnWaitAll()));
 | 
			
		||||
    }
 | 
			
		||||
 
 | 
			
		||||
@@ -225,6 +225,7 @@ void Thread::ResumeFromWait() {
 | 
			
		||||
    switch (status) {
 | 
			
		||||
    case THREADSTATUS_WAIT_SYNCH_ALL:
 | 
			
		||||
    case THREADSTATUS_WAIT_SYNCH_ANY:
 | 
			
		||||
    case THREADSTATUS_WAIT_HLE_EVENT:
 | 
			
		||||
    case THREADSTATUS_WAIT_ARB:
 | 
			
		||||
    case THREADSTATUS_WAIT_SLEEP:
 | 
			
		||||
    case THREADSTATUS_WAIT_IPC:
 | 
			
		||||
 
 | 
			
		||||
@@ -38,6 +38,7 @@ enum ThreadStatus {
 | 
			
		||||
    THREADSTATUS_WAIT_IPC,       ///< Waiting for the reply from an IPC request
 | 
			
		||||
    THREADSTATUS_WAIT_SYNCH_ANY, ///< Waiting due to WaitSynch1 or WaitSynchN with wait_all = false
 | 
			
		||||
    THREADSTATUS_WAIT_SYNCH_ALL, ///< Waiting due to WaitSynchronizationN with wait_all = true
 | 
			
		||||
    THREADSTATUS_WAIT_HLE_EVENT, ///< Waiting due to an HLE handler pausing the thread
 | 
			
		||||
    THREADSTATUS_DORMANT,        ///< Created but not yet made ready
 | 
			
		||||
    THREADSTATUS_DEAD            ///< Run to completion, or forcefully terminated
 | 
			
		||||
};
 | 
			
		||||
 
 | 
			
		||||
@@ -39,7 +39,8 @@ SharedPtr<Thread> WaitObject::GetHighestPriorityReadyThread() {
 | 
			
		||||
    for (const auto& thread : waiting_threads) {
 | 
			
		||||
        // The list of waiting threads must not contain threads that are not waiting to be awakened.
 | 
			
		||||
        ASSERT_MSG(thread->status == THREADSTATUS_WAIT_SYNCH_ANY ||
 | 
			
		||||
                       thread->status == THREADSTATUS_WAIT_SYNCH_ALL,
 | 
			
		||||
                       thread->status == THREADSTATUS_WAIT_SYNCH_ALL ||
 | 
			
		||||
                       thread->status == THREADSTATUS_WAIT_HLE_EVENT,
 | 
			
		||||
                   "Inconsistent thread statuses in waiting_threads");
 | 
			
		||||
 | 
			
		||||
        if (thread->current_priority >= candidate_priority)
 | 
			
		||||
 
 | 
			
		||||
@@ -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>();
 | 
			
		||||
@@ -279,4 +321,4 @@ void Shutdown() {
 | 
			
		||||
    g_kernel_named_ports.clear();
 | 
			
		||||
    LOG_DEBUG(Service, "shutdown OK");
 | 
			
		||||
}
 | 
			
		||||
}
 | 
			
		||||
} // namespace Service
 | 
			
		||||
 
 | 
			
		||||
@@ -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
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user