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:
commit
b2a99043e3
|
@ -161,6 +161,9 @@ QString WaitTreeThread::GetText() const {
|
||||||
case THREADSTATUS_WAIT_SYNCH_ANY:
|
case THREADSTATUS_WAIT_SYNCH_ANY:
|
||||||
status = tr("waiting for objects");
|
status = tr("waiting for objects");
|
||||||
break;
|
break;
|
||||||
|
case THREADSTATUS_WAIT_HLE_EVENT:
|
||||||
|
status = tr("waiting for HLE return");
|
||||||
|
break;
|
||||||
case THREADSTATUS_DORMANT:
|
case THREADSTATUS_DORMANT:
|
||||||
status = tr("dormant");
|
status = tr("dormant");
|
||||||
break;
|
break;
|
||||||
|
@ -189,6 +192,7 @@ QColor WaitTreeThread::GetColor() const {
|
||||||
return QColor(Qt::GlobalColor::darkCyan);
|
return QColor(Qt::GlobalColor::darkCyan);
|
||||||
case THREADSTATUS_WAIT_SYNCH_ALL:
|
case THREADSTATUS_WAIT_SYNCH_ALL:
|
||||||
case THREADSTATUS_WAIT_SYNCH_ANY:
|
case THREADSTATUS_WAIT_SYNCH_ANY:
|
||||||
|
case THREADSTATUS_WAIT_HLE_EVENT:
|
||||||
return QColor(Qt::GlobalColor::red);
|
return QColor(Qt::GlobalColor::red);
|
||||||
case THREADSTATUS_DORMANT:
|
case THREADSTATUS_DORMANT:
|
||||||
return QColor(Qt::GlobalColor::darkCyan);
|
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));
|
list.push_back(std::make_unique<WaitTreeMutexList>(thread.held_mutexes));
|
||||||
}
|
}
|
||||||
if (thread.status == THREADSTATUS_WAIT_SYNCH_ANY ||
|
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,
|
list.push_back(std::make_unique<WaitTreeObjectList>(thread.wait_objects,
|
||||||
thread.IsSleepingOnWaitAll()));
|
thread.IsSleepingOnWaitAll()));
|
||||||
}
|
}
|
||||||
|
|
|
@ -225,6 +225,7 @@ void Thread::ResumeFromWait() {
|
||||||
switch (status) {
|
switch (status) {
|
||||||
case THREADSTATUS_WAIT_SYNCH_ALL:
|
case THREADSTATUS_WAIT_SYNCH_ALL:
|
||||||
case THREADSTATUS_WAIT_SYNCH_ANY:
|
case THREADSTATUS_WAIT_SYNCH_ANY:
|
||||||
|
case THREADSTATUS_WAIT_HLE_EVENT:
|
||||||
case THREADSTATUS_WAIT_ARB:
|
case THREADSTATUS_WAIT_ARB:
|
||||||
case THREADSTATUS_WAIT_SLEEP:
|
case THREADSTATUS_WAIT_SLEEP:
|
||||||
case THREADSTATUS_WAIT_IPC:
|
case THREADSTATUS_WAIT_IPC:
|
||||||
|
|
|
@ -38,6 +38,7 @@ enum ThreadStatus {
|
||||||
THREADSTATUS_WAIT_IPC, ///< Waiting for the reply from an IPC request
|
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_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_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_DORMANT, ///< Created but not yet made ready
|
||||||
THREADSTATUS_DEAD ///< Run to completion, or forcefully terminated
|
THREADSTATUS_DEAD ///< Run to completion, or forcefully terminated
|
||||||
};
|
};
|
||||||
|
|
|
@ -39,7 +39,8 @@ SharedPtr<Thread> WaitObject::GetHighestPriorityReadyThread() {
|
||||||
for (const auto& thread : waiting_threads) {
|
for (const auto& thread : waiting_threads) {
|
||||||
// The list of waiting threads must not contain threads that are not waiting to be awakened.
|
// The list of waiting threads must not contain threads that are not waiting to be awakened.
|
||||||
ASSERT_MSG(thread->status == THREADSTATUS_WAIT_SYNCH_ANY ||
|
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");
|
"Inconsistent thread statuses in waiting_threads");
|
||||||
|
|
||||||
if (thread->current_priority >= candidate_priority)
|
if (thread->current_priority >= candidate_priority)
|
||||||
|
|
|
@ -9,6 +9,7 @@
|
||||||
#include "common/string_util.h"
|
#include "common/string_util.h"
|
||||||
#include "core/hle/ipc.h"
|
#include "core/hle/ipc.h"
|
||||||
#include "core/hle/kernel/client_port.h"
|
#include "core/hle/kernel/client_port.h"
|
||||||
|
#include "core/hle/kernel/event.h"
|
||||||
#include "core/hle/kernel/process.h"
|
#include "core/hle/kernel/process.h"
|
||||||
#include "core/hle/kernel/server_port.h"
|
#include "core/hle/kernel/server_port.h"
|
||||||
#include "core/hle/kernel/server_session.h"
|
#include "core/hle/kernel/server_session.h"
|
||||||
|
@ -210,6 +211,47 @@ void AddService(Interface* interface_) {
|
||||||
server_port->SetHleHandler(std::shared_ptr<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
|
/// Initialize ServiceManager
|
||||||
void Init() {
|
void Init() {
|
||||||
SM::g_service_manager = std::make_shared<SM::ServiceManager>();
|
SM::g_service_manager = std::make_shared<SM::ServiceManager>();
|
||||||
|
@ -279,4 +321,4 @@ void Shutdown() {
|
||||||
g_kernel_named_ports.clear();
|
g_kernel_named_ports.clear();
|
||||||
LOG_DEBUG(Service, "shutdown OK");
|
LOG_DEBUG(Service, "shutdown OK");
|
||||||
}
|
}
|
||||||
}
|
} // namespace Service
|
||||||
|
|
|
@ -20,7 +20,8 @@ namespace Kernel {
|
||||||
class ClientPort;
|
class ClientPort;
|
||||||
class ServerPort;
|
class ServerPort;
|
||||||
class ServerSession;
|
class ServerSession;
|
||||||
}
|
class Event;
|
||||||
|
} // namespace Kernel
|
||||||
|
|
||||||
namespace Service {
|
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
|
/// Initialize ServiceManager
|
||||||
void Init();
|
void Init();
|
||||||
|
|
||||||
|
@ -263,4 +303,4 @@ void AddNamedPort(std::string name, Kernel::SharedPtr<Kernel::ClientPort> port);
|
||||||
/// Adds a service to the services table
|
/// Adds a service to the services table
|
||||||
void AddService(Interface* interface_);
|
void AddService(Interface* interface_);
|
||||||
|
|
||||||
} // namespace
|
} // namespace Service
|
||||||
|
|
Reference in New Issue