Skip to content

Commit 27b09e4

Browse files
committed
Resumed self-suspending guest threads safely
1 parent cf50cad commit 27b09e4

1 file changed

Lines changed: 90 additions & 12 deletions

File tree

src/cxbx/src/hle/kernel/kernel_emulation.cpp

Lines changed: 90 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -136,18 +136,29 @@ static bool EmuIsThreadObjectType(PVOID ObjectType)
136136

137137
static ULONG EmuProbeThreadSuspendCount(HANDLE ThreadHandle)
138138
{
139+
if(GetThreadId(ThreadHandle) == GetCurrentThreadId())
140+
{
141+
return 0;
142+
}
143+
139144
DWORD Previous = SuspendThread(ThreadHandle);
140145
if(Previous == (DWORD)-1)
146+
{
141147
return 0;
148+
}
142149

143-
ResumeThread(ThreadHandle);
150+
if(ResumeThread(ThreadHandle) == (DWORD)-1)
151+
{
152+
return 0;
153+
}
144154
return Previous;
145155
}
146156

147157
struct EmuThreadSuspendState
148158
{
149159
ULONG LogicalCount = 0;
150160
ULONG HostCount = 0;
161+
bool SelfSuspendPending = false;
151162
};
152163

153164
// Handles can be duplicated, so suspend state is shared by host thread ID.
@@ -578,10 +589,31 @@ static bool EmuResumeThreadState(
578589
return true;
579590
}
580591

592+
if(SuspendState.SelfSuspendPending)
593+
{
594+
// The suspending thread publishes its intent before calling the host
595+
// API. A resumer can arrive in that narrow interval, so wait until the
596+
// host count becomes observable rather than losing the resume.
597+
DWORD HostPreviousCount = 0;
598+
do
599+
{
600+
HostPreviousCount = ResumeThread(ThreadHandle);
601+
if(HostPreviousCount == (DWORD)-1)
602+
{
603+
return false;
604+
}
605+
if(HostPreviousCount == 0)
606+
{
607+
SwitchToThread();
608+
}
609+
} while(HostPreviousCount == 0);
610+
611+
SuspendState.SelfSuspendPending = false;
612+
}
581613
// Logical-only suspensions sit above any real host suspension. Resume the
582614
// host only when the final logical layer covering it is removed.
583-
if(SuspendState.HostCount == SuspendState.LogicalCount &&
584-
SuspendState.HostCount != 0)
615+
else if(SuspendState.HostCount == SuspendState.LogicalCount &&
616+
SuspendState.HostCount != 0)
585617
{
586618
if(ResumeThread(ThreadHandle) == (DWORD)-1)
587619
{
@@ -594,6 +626,23 @@ static bool EmuResumeThreadState(
594626
return true;
595627
}
596628

629+
static bool EmuSuspendCurrentThread(
630+
HANDLE ThreadHandle, std::unique_lock<std::mutex>& Lock,
631+
EmuThreadSuspendState& SuspendState)
632+
{
633+
SuspendState.SelfSuspendPending = true;
634+
Lock.unlock();
635+
const DWORD HostPreviousCount = SuspendThread(ThreadHandle);
636+
Lock.lock();
637+
if(HostPreviousCount == (DWORD)-1)
638+
{
639+
SuspendState.SelfSuspendPending = false;
640+
return false;
641+
}
642+
643+
return true;
644+
}
645+
597646
static EmuThreadObjectHeader *EmuThreadHeaderFromThread(xboxkrnl::PKTHREAD Thread)
598647
{
599648
EmuThreadObjectHeader *Header = (EmuThreadObjectHeader*)((BYTE*)Thread - 16);
@@ -6342,7 +6391,7 @@ extern "C" ULONG NTAPI EmuKeSuspendThread(xboxkrnl::PKTHREAD Thread)
63426391
return 0;
63436392
}
63446393

6345-
const std::lock_guard<std::mutex> Lock(
6394+
std::unique_lock<std::mutex> Lock(
63466395
g_EmuThreadSuspendCountsMutex);
63476396
EmuThreadSuspendState& SuspendState =
63486397
EmuThreadSuspendStateForHandleLocked(ThreadHeader->HostHandle);
@@ -6374,12 +6423,27 @@ extern "C" ULONG NTAPI EmuKeSuspendThread(xboxkrnl::PKTHREAD Thread)
63746423
return PreviousCount;
63756424
}
63766425

6377-
if(EmuSuspendThreadAtGuestBoundary(ThreadHeader->HostHandle))
6426+
const DWORD ThreadId = GetThreadId(ThreadHeader->HostHandle);
6427+
if(ThreadId == GetCurrentThreadId())
6428+
{
6429+
SuspendState.LogicalCount++;
6430+
ThreadHeader->SuspendCount = SuspendState.LogicalCount;
6431+
if(!EmuSuspendCurrentThread(
6432+
ThreadHeader->HostHandle, Lock, SuspendState))
6433+
{
6434+
SuspendState.LogicalCount--;
6435+
}
6436+
ThreadHeader->SuspendCount = SuspendState.LogicalCount;
6437+
}
6438+
else
63786439
{
6379-
SuspendState.HostCount++;
6440+
if(EmuSuspendThreadAtGuestBoundary(ThreadHeader->HostHandle))
6441+
{
6442+
SuspendState.HostCount++;
6443+
}
6444+
SuspendState.LogicalCount++;
6445+
ThreadHeader->SuspendCount = SuspendState.LogicalCount;
63806446
}
6381-
SuspendState.LogicalCount++;
6382-
ThreadHeader->SuspendCount = SuspendState.LogicalCount;
63836447

63846448
EmuSwapFS(); // Xbox FS
63856449

@@ -10843,7 +10907,7 @@ extern "C" NTSTATUS NTAPI EmuNtSuspendThread
1084310907
}
1084410908
else
1084510909
{
10846-
const std::lock_guard<std::mutex> Lock(
10910+
std::unique_lock<std::mutex> Lock(
1084710911
g_EmuThreadSuspendCountsMutex);
1084810912
EmuThreadSuspendState& SuspendState =
1084910913
EmuThreadSuspendStateForHandleLocked(ThreadHandle);
@@ -10860,11 +10924,25 @@ extern "C" NTSTATUS NTAPI EmuNtSuspendThread
1086010924
}
1086110925
else
1086210926
{
10863-
if(EmuSuspendThreadAtGuestBoundary(ThreadHandle))
10927+
const DWORD ThreadId = GetThreadId(ThreadHandle);
10928+
if(ThreadId == GetCurrentThreadId())
1086410929
{
10865-
SuspendState.HostCount++;
10930+
SuspendState.LogicalCount++;
10931+
if(!EmuSuspendCurrentThread(
10932+
ThreadHandle, Lock, SuspendState))
10933+
{
10934+
SuspendState.LogicalCount--;
10935+
ret = 0xC0000008;
10936+
}
10937+
}
10938+
else
10939+
{
10940+
if(EmuSuspendThreadAtGuestBoundary(ThreadHandle))
10941+
{
10942+
SuspendState.HostCount++;
10943+
}
10944+
SuspendState.LogicalCount++;
1086610945
}
10867-
SuspendState.LogicalCount++;
1086810946
}
1086910947
}
1087010948

0 commit comments

Comments
 (0)