Skip to content

Commit

Permalink
Bug #122, Windows XP使用CV等可能卡死在NtWaitForKeyedEvent
Browse files Browse the repository at this point in the history
  • Loading branch information
mingkuang-Chuyu committed Oct 15, 2024
1 parent a41b77a commit f5c99a8
Showing 1 changed file with 88 additions and 39 deletions.
127 changes: 88 additions & 39 deletions src/Thunks/api-ms-win-core-synch.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,13 @@ typedef struct __declspec(align(16)) _YY_SRWLOCK_WAIT_BLOCK
_YY_SRWLOCK_WAIT_BLOCK* next;
volatile size_t shareCount;
volatile size_t flag;

// 以下成员YY-Thunks特有

// 故意填充的成员,便于于YY_CV_WAIT_BLOCK ABI兼容
void* pReserved;
// 唤醒此任务的线程Id
volatile uint32_t uWakeupThreadId;
} YY_SRWLOCK_WAIT_BLOCK;


Expand All @@ -70,6 +77,11 @@ typedef struct __declspec(align(16)) _YY_CV_WAIT_BLOCK
volatile size_t shareCount;
volatile size_t flag;
volatile PSRWLOCK SRWLock;

// 以下成员YY-Thunks特有

// 唤醒此任务的线程Id
volatile uint32_t uWakeupThreadId;
} YY_CV_WAIT_BLOCK;


Expand All @@ -91,6 +103,10 @@ typedef struct __declspec(align(8)) _YY_ADDRESS_WAIT_BLOCK
// 似乎指向Root,但是Root时才指向自己,其余情况为 nullptr,这是一种安全性?
_YY_ADDRESS_WAIT_BLOCK* next;
volatile size_t flag;
// 以下成员YY-Thunks特有

// 唤醒此任务的线程Id
volatile uint32_t uWakeupThreadId;

} YY_ADDRESS_WAIT_BLOCK;

Expand All @@ -114,6 +130,65 @@ namespace YY::Thunks::internal
{
namespace
{
// Windows XP SecondWaitWorkaround有关问题可以参考:
// https://github.com/Chuyu-Team/YY-Thunks/issues/122
// 第二次调用NtWaitForKeyedEvent时可能
template<typename KeyType>
static NTSTATUS NTAPI SecondWaitWorkaroundNtWaitForKeyedEvent(
IN HANDLE KeyedEventHandle,
IN KeyType* Key,
IN BOOLEAN Alertable,
IN PLARGE_INTEGER Timeout OPTIONAL
)
{
#if !defined(__USING_NTDLL_LIB)
const auto NtWaitForKeyedEvent = try_get_NtWaitForKeyedEvent();
if (!NtWaitForKeyedEvent)
{
internal::RaiseStatus(STATUS_RESOURCE_NOT_OWNED);
}
#endif

#if (YY_Thunks_Target < __WindowsNT6)
if (Timeout == nullptr || NtCurrentTeb()->ProcessEnvironmentBlock->OSMajorVersion < 6)
{
LARGE_INTEGER _nTimeOut;
internal::BaseFormatTimeOut(&_nTimeOut, 0);
for (; Key->uWakeupThreadId == 0;)
{
auto _Status = NtWaitForKeyedEvent(KeyedEventHandle, (PVOID)Key, Alertable, &_nTimeOut);
if (_Status != STATUS_TIMEOUT)
return STATUS_TIMEOUT;
}

// 等5毫秒应该足够唤醒线程调用Release了
// 这里只是经验假设,可能不能彻底规避问题。但是正常情况下应该足以缓解死等问题。
NtWaitForKeyedEvent(KeyedEventHandle, Key, Alertable, internal::BaseFormatTimeOut(&_nTimeOut, 5));
return STATUS_SUCCESS;
}
#endif
return NtWaitForKeyedEvent(KeyedEventHandle, Key, Alertable, Timeout);
}

template<typename KeyType>
static NTSTATUS NTAPI SecondWaitWorkaroundNtReleaseKeyedEvent(
IN HANDLE KeyedEventHandle,
IN KeyType* Key,
IN BOOLEAN Alertable,
IN PLARGE_INTEGER Timeout OPTIONAL
)
{
#if !defined(__USING_NTDLL_LIB)
const auto NtReleaseKeyedEvent = try_get_NtReleaseKeyedEvent();
if (!NtReleaseKeyedEvent)
{
RaiseStatus(STATUS_RESOURCE_NOT_OWNED);
}
#endif
InterlockedExchange(&Key->uWakeupThreadId, GetCurrentThreadId());
return NtReleaseKeyedEvent(KeyedEventHandle, Key, Alertable, Timeout);
}

static HANDLE __fastcall GetGlobalKeyedEventHandle()
{
#if (YY_Thunks_Target < __WindowsNT6)
Expand Down Expand Up @@ -157,14 +232,6 @@ namespace YY::Thunks::internal
static void __fastcall RtlpWakeSRWLock(SRWLOCK* SRWLock, size_t Status)
{
auto GlobalKeyedEventHandle = GetGlobalKeyedEventHandle();
#if !defined(__USING_NTDLL_LIB)
const auto NtReleaseKeyedEvent = try_get_NtReleaseKeyedEvent();
if (!NtReleaseKeyedEvent)
{
RaiseStatus(STATUS_RESOURCE_NOT_OWNED);
}
#endif

for (;;)
{
if ((Status & YY_SRWLOCK_Locked) == 0)
Expand Down Expand Up @@ -203,7 +270,7 @@ namespace YY::Thunks::internal

//if(!RtlpWaitCouldDeadlock())

NtReleaseKeyedEvent(GlobalKeyedEventHandle, notify, 0, nullptr);
internal::SecondWaitWorkaroundNtReleaseKeyedEvent(GlobalKeyedEventHandle, notify, 0, nullptr);
}

return;
Expand All @@ -227,7 +294,7 @@ namespace YY::Thunks::internal

//if(!RtlpWaitCouldDeadlock())

NtReleaseKeyedEvent(GlobalKeyedEventHandle, notify, 0, nullptr);
internal::SecondWaitWorkaroundNtReleaseKeyedEvent(GlobalKeyedEventHandle, notify, 0, nullptr);
}

notify = next;
Expand Down Expand Up @@ -346,14 +413,6 @@ namespace YY::Thunks::internal
static void __fastcall RtlpWakeConditionVariable(PCONDITION_VARIABLE ConditionVariable, size_t ConditionVariableStatus, size_t WakeCount)
{
auto GlobalKeyedEventHandle = GetGlobalKeyedEventHandle();
#if !defined(__USING_NTDLL_LIB)
const auto NtReleaseKeyedEvent = try_get_NtReleaseKeyedEvent();
if (!NtReleaseKeyedEvent)
{
RaiseStatus(STATUS_RESOURCE_NOT_OWNED);
}
#endif

//v16
YY_CV_WAIT_BLOCK* notify = nullptr;

Expand Down Expand Up @@ -455,7 +514,7 @@ namespace YY::Thunks::internal
{
if (pWake->SRWLock == nullptr || RtlpQueueWaitBlockToSRWLock(pWake, pWake->SRWLock, (pWake->flag >> 2) & 0x1) == FALSE)
{
NtReleaseKeyedEvent(GlobalKeyedEventHandle, pWake, 0, nullptr);
internal::SecondWaitWorkaroundNtReleaseKeyedEvent(GlobalKeyedEventHandle, pWake, 0, nullptr);
}
}

Expand Down Expand Up @@ -800,19 +859,13 @@ namespace YY::Thunks::internal
static void __fastcall RtlpWaitOnAddressWakeEntireList(YY_ADDRESS_WAIT_BLOCK* pBlock)
{
auto GlobalKeyedEventHandle = GetGlobalKeyedEventHandle();
#if !defined(__USING_NTDLL_LIB)
const auto NtReleaseKeyedEvent = try_get_NtReleaseKeyedEvent();
if (!NtReleaseKeyedEvent)
internal::RaiseStatus(STATUS_NOT_FOUND);
#endif

for (; pBlock;)
{
auto Tmp = pBlock->back;

if (InterlockedExchange(&pBlock->flag, 2) == 0)
{
NtReleaseKeyedEvent(GlobalKeyedEventHandle, pBlock, 0, nullptr);
internal::SecondWaitWorkaroundNtReleaseKeyedEvent(GlobalKeyedEventHandle, pBlock, 0, nullptr);
}


Expand Down Expand Up @@ -1072,7 +1125,7 @@ namespace YY::Thunks::internal
{
if (InterlockedExchange(&pWaitBlock->flag, 4) == 2)
{
Status = NtWaitForKeyedEvent(GlobalKeyedEventHandle, pWaitBlock, 0, nullptr);
Status = internal::SecondWaitWorkaroundNtWaitForKeyedEvent(GlobalKeyedEventHandle, pWaitBlock, 0, nullptr);
}
else
{
Expand Down Expand Up @@ -1650,6 +1703,7 @@ namespace YY::Thunks
//成功锁定
return;
}
StackWaitBlock.uWakeupThreadId = 0;

for (;;)
{
Expand Down Expand Up @@ -1846,7 +1900,7 @@ namespace YY::Thunks
return;
}


StackWaitBlock.uWakeupThreadId = 0;
size_t NewSRWLock;

for (;; OldSRWLock = *(volatile size_t *)SRWLock)
Expand Down Expand Up @@ -2141,6 +2195,7 @@ namespace YY::Thunks
StackWaitBlock.next = nullptr;
StackWaitBlock.flag = 2;
StackWaitBlock.SRWLock = nullptr;
StackWaitBlock.uWakeupThreadId = 0;

auto OldConditionVariable = *(size_t*)ConditionVariable;

Expand Down Expand Up @@ -2206,7 +2261,7 @@ namespace YY::Thunks

if (Status == STATUS_TIMEOUT && internal::RtlpWakeSingle(ConditionVariable, &StackWaitBlock) == FALSE)
{
NtWaitForKeyedEvent(GlobalKeyedEventHandle, (PVOID)&StackWaitBlock, 0, nullptr);
internal::SecondWaitWorkaroundNtWaitForKeyedEvent(GlobalKeyedEventHandle, &StackWaitBlock, 0, nullptr);
Status = 0;
}
}
Expand Down Expand Up @@ -2256,6 +2311,7 @@ namespace YY::Thunks
StackWaitBlock.next = nullptr;
StackWaitBlock.flag = 2;
StackWaitBlock.SRWLock = nullptr;
StackWaitBlock.uWakeupThreadId = 0;

if (Flags& CONDITION_VARIABLE_LOCKMODE_SHARED)
{
Expand Down Expand Up @@ -2328,7 +2384,7 @@ namespace YY::Thunks

if (Status == STATUS_TIMEOUT && internal::RtlpWakeSingle(ConditionVariable, &StackWaitBlock) == FALSE)
{
NtWaitForKeyedEvent(GlobalKeyedEventHandle, (PVOID)&StackWaitBlock, 0, nullptr);
internal::SecondWaitWorkaroundNtWaitForKeyedEvent(GlobalKeyedEventHandle, &StackWaitBlock, 0, nullptr);
Status = 0;
}
}
Expand Down Expand Up @@ -2432,21 +2488,13 @@ namespace YY::Thunks
if (Last == Current)
{
auto GlobalKeyedEventHandle = internal::GetGlobalKeyedEventHandle();
#if !defined(__USING_NTDLL_LIB)
const auto NtReleaseKeyedEvent = try_get_NtReleaseKeyedEvent();
if (!NtReleaseKeyedEvent)
{
internal::RaiseStatus(STATUS_RESOURCE_NOT_OWNED);
}
#endif

for (auto pBlock = YY_CV_GET_BLOCK(Current); pBlock;)
{
auto Tmp = pBlock->back;

if (!InterlockedBitTestAndReset((volatile LONG*)&pBlock->flag, 1))
{
NtReleaseKeyedEvent(GlobalKeyedEventHandle, pBlock, FALSE, nullptr);
internal::SecondWaitWorkaroundNtReleaseKeyedEvent(GlobalKeyedEventHandle, pBlock, FALSE, nullptr);
}

pBlock = Tmp;
Expand Down Expand Up @@ -2715,6 +2763,7 @@ namespace YY::Thunks
WaitBlock.notify = nullptr;
WaitBlock.next = nullptr;
WaitBlock.flag = 1;
WaitBlock.uWakeupThreadId = 0;

internal::RtlpAddWaitBlockToWaitList(&WaitBlock);

Expand Down

0 comments on commit f5c99a8

Please sign in to comment.