[Linux/Threading] Self-suspend with condition variable instead of spin

This commit is contained in:
Herman S.
2025-12-25 12:08:05 +09:00
parent 9f8961c185
commit f45a254774
3 changed files with 51 additions and 21 deletions

View File

@@ -711,7 +711,9 @@ uint32_t XThread::start_address() {
X_STATUS XThread::Resume(uint32_t* out_suspend_count) {
auto guest_thread = guest_object<X_KTHREAD>();
uint32_t unused_host_suspend_count = 0;
#if XE_PLATFORM_WIN32
uint8_t previous_suspend_count =
reinterpret_cast<std::atomic_uint8_t*>(&guest_thread->suspend_count)
->fetch_sub(1);
@@ -719,21 +721,30 @@ X_STATUS XThread::Resume(uint32_t* out_suspend_count) {
*out_suspend_count = previous_suspend_count;
}
uint32_t unused_host_suspend_count = 0;
#if XE_PLATFORM_WIN32
if (thread_->Resume(&unused_host_suspend_count)) {
return X_STATUS_SUCCESS;
} else {
return X_STATUS_UNSUCCESSFUL;
}
#elif XE_PLATFORM_LINUX
// On Linux, only resume if suspend count is 0
// Resume might fail if thread was self-suspended - this is expected
if (guest_thread->suspend_count == 0) {
if (!thread_->Resume(&unused_host_suspend_count)) {
XELOGD("Host thread resume skipped for thread {:X} (was self-suspended)",
handle());
// Use mutex to protect suspend_count access and coordinate with SelfSuspend.
bool should_resume_host = false;
{
std::lock_guard<std::mutex> lock(suspend_mutex_);
uint8_t previous = guest_thread->suspend_count;
if (previous > 0) {
guest_thread->suspend_count--;
}
if (out_suspend_count) {
*out_suspend_count = previous;
}
should_resume_host = (guest_thread->suspend_count == 0);
suspend_cv_.notify_all();
}
// Try to resume host thread if fully resumed (for non-self-suspended case).
if (should_resume_host) {
thread_->Resume(&unused_host_suspend_count);
}
return X_STATUS_SUCCESS;
#else
@@ -769,6 +780,18 @@ X_STATUS XThread::Suspend(uint32_t* out_suspend_count) {
}
}
#if XE_PLATFORM_LINUX
uint32_t XThread::SelfSuspend() {
auto guest_thread = guest_object<X_KTHREAD>();
std::unique_lock<std::mutex> lock(suspend_mutex_);
uint32_t previous = guest_thread->suspend_count;
guest_thread->suspend_count++;
suspend_cv_.wait(
lock, [guest_thread]() { return guest_thread->suspend_count == 0; });
return previous;
}
#endif
X_STATUS XThread::Delay(uint32_t processor_mode, uint32_t alertable,
uint64_t interval) {
int64_t timeout_ticks = interval;