progwrp/condvar.c
2025-12-13 23:37:26 +01:00

280 lines
6.8 KiB
C

#include "progwrp.h"
#include "export.h"
#include "implementations.h"
#define SEMAPHORE_BIT (1 << 31)
typedef struct
{
PULONG CondVarBitMasks;
ULONG NumberBitMasks;
}COND_VAR_BASE, * PCOND_VAR_BASE;
NTSYSAPI PVOID RtlAllocateHeap(
PVOID HeapHandle,
ULONG Flags,
SIZE_T Size
);
NTSYSAPI VOID RtlFreeHeap(
PVOID HeapHandle,
ULONG Flags,
PVOID BaseAddress
);
#define RtlMoveMemory RtlMoveMemory
VOID NTAPI RtlMoveMemory(
PVOID Destination,
PVOID Source,
SIZE_T Length
);
/*
A condition variable is a synchronization object which allows many
threads to synchronize on one variable, in a pseudo-queue.
I determined that the condition variable could consist of an expandable array of bit masks.
One bit mask has two bits per thread; one to indicate that the section of the bit mask is in use
and another to indicate that the bit mask has been signalled.
*/
typedef struct {
HANDLE SingleWakeEvent;
HANDLE AllWakeEvent;
HANDLE AccessSemaphore;
ULONG WaiterCount;
}CONDITION_VARIABLE_INT, * PCONDITION_VARIABLE_INT;
BOOL WINAPI Implementation_SleepConditionVariableSRW(
PCONDITION_VARIABLE ConditionVariable,
PSRWLOCK SRWLock,
DWORD dwMilliseconds,
ULONG Flags
)
{
int i;
DWORD dwWaitStatus;
DWORD dwCondVarIdx;
PTEB_CUSTOM Teb;
PCONDITION_VARIABLE_INT pCondVar;
if (!(!SRWLock && Flags == 2)) {
if (!ConditionVariable || !SRWLock || Flags & ~(CONDITION_VARIABLE_LOCKMODE_SHARED))
{
SetLastError(ERROR_INVALID_PARAMETER);
return FALSE;
}
}
Teb = (PTEB_CUSTOM)NtCurrentTeb();
if (!ConditionVariable->Ptr)
{
pCondVar = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(CONDITION_VARIABLE_INT));
if (!pCondVar)
return FALSE;
#ifdef _M_X64
if (InterlockedCompareExchange64(ConditionVariable, pCondVar, 0))
#else
if (InterlockedCompareExchange(ConditionVariable, pCondVar, 0))
#endif
HeapFree(GetProcessHeap(), 0, pCondVar);
else
{
pCondVar->WaiterCount = 0;
pCondVar->AccessSemaphore = CreateSemaphoreA(NULL, 1, 1, NULL);
pCondVar->AllWakeEvent = CreateEventA(NULL, TRUE, FALSE, NULL);
pCondVar->SingleWakeEvent = CreateEventA(NULL, FALSE, FALSE, NULL);
}
}
pCondVar = ConditionVariable->Ptr;
WaitForSingleObject(pCondVar->AccessSemaphore, INFINITE);
InterlockedIncrement(&pCondVar->WaiterCount);
ReleaseSemaphore(pCondVar->AccessSemaphore, 1, NULL);
if (Flags & CONDITION_VARIABLE_LOCKMODE_SHARED)
Implementation_ReleaseSRWLockShared(SRWLock);
else
Implementation_ReleaseSRWLockExclusive(SRWLock);
HANDLE MultipleHandles[2] = { pCondVar->AllWakeEvent, pCondVar->SingleWakeEvent };
dwWaitStatus = WaitForMultipleObjects(2, MultipleHandles, FALSE, dwMilliseconds);
if (!InterlockedDecrement(&pCondVar->WaiterCount)) {
ResetEvent(pCondVar->AllWakeEvent);
ResetEvent(pCondVar->SingleWakeEvent);
ReleaseSemaphore(pCondVar->AccessSemaphore, 1, NULL);
}
else if (dwWaitStatus == WAIT_OBJECT_0 + 1)
{
ReleaseSemaphore(pCondVar->AccessSemaphore, 1, NULL);
}
if (Flags & CONDITION_VARIABLE_LOCKMODE_SHARED)
Implementation_AcquireSRWLockShared(SRWLock);
else
Implementation_AcquireSRWLockExclusive(SRWLock);
if (dwWaitStatus != WAIT_OBJECT_0 &&
dwWaitStatus != WAIT_OBJECT_0 + 1)
{
SetLastError(ERROR_TIMEOUT);
return FALSE;
}
else
return TRUE;
}
BOOL WINAPI Implementation_SleepConditionVariableCS(
PCONDITION_VARIABLE ConditionVariable,
PCRITICAL_SECTION CriticalSection,
DWORD dwMilliseconds
)
{
int i;
DWORD dwWaitStatus;
DWORD dwCondVarIdx;
PTEB_CUSTOM Teb;
PCONDITION_VARIABLE_INT pCondVar;
if (!ConditionVariable || !CriticalSection)
{
SetLastError(ERROR_INVALID_PARAMETER);
return FALSE;
}
if (!ConditionVariable->Ptr)
{
pCondVar = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(CONDITION_VARIABLE_INT));
#ifdef _M_X64
if (InterlockedCompareExchange64(ConditionVariable, pCondVar, 0))
#else
if (InterlockedCompareExchange(ConditionVariable, pCondVar, 0))
#endif
HeapFree(GetProcessHeap(), 0, pCondVar);
else
{
pCondVar->WaiterCount = 0;
pCondVar->AccessSemaphore = CreateSemaphoreA(NULL, 1, 1, NULL);
pCondVar->AllWakeEvent = CreateEventA(NULL, TRUE, FALSE, NULL);
pCondVar->SingleWakeEvent = CreateEventA(NULL, FALSE, FALSE, NULL);
}
}
pCondVar = ConditionVariable->Ptr;
WaitForSingleObject(pCondVar->AccessSemaphore, INFINITE);
InterlockedIncrement(&pCondVar->WaiterCount);
ReleaseSemaphore(pCondVar->AccessSemaphore, 1, NULL);
LeaveCriticalSection(CriticalSection);
HANDLE MultipleHandles[2] = { pCondVar->AllWakeEvent, pCondVar->SingleWakeEvent };
dwWaitStatus = WaitForMultipleObjects(2, MultipleHandles, FALSE, dwMilliseconds);
if (!InterlockedDecrement(&pCondVar->WaiterCount)) {
ResetEvent(pCondVar->AllWakeEvent);
ResetEvent(pCondVar->SingleWakeEvent);
ReleaseSemaphore(pCondVar->AccessSemaphore, 1, NULL);
}
else if (dwWaitStatus == WAIT_OBJECT_0 + 1)
{
ReleaseSemaphore(pCondVar->AccessSemaphore, 1, NULL);
}
EnterCriticalSection(CriticalSection);
SetLastError(dwWaitStatus);
if (dwWaitStatus != WAIT_OBJECT_0 &&
dwWaitStatus != WAIT_OBJECT_0 + 1)
{
SetLastError(ERROR_TIMEOUT);
return FALSE;
}
else
return TRUE;
}
void WINAPI Implementation_InitializeConditionVariable(
PCONDITION_VARIABLE ConditionVariable
)
{
ConditionVariable->Ptr = 0;
}
typedef struct _THREAD_BASIC_INFORMATION {
NTSTATUS ExitStatus;
PVOID TebBaseAddress;
CLIENT_ID ClientId;
KAFFINITY AffinityMask;
KPRIORITY Priority;
KPRIORITY BasePriority;
} THREAD_BASIC_INFORMATION, * PTHREAD_BASIC_INFORMATION;
typedef struct _OBJECT_BASIC_INFORMATION {
ULONG Attributes;
ACCESS_MASK GrantedAccess;
ULONG HandleCount;
ULONG PointerCount;
ULONG PagedPoolCharge;
ULONG NonPagedPoolCharge;
ULONG Reserved[3];
ULONG NameInfoSize;
ULONG TypeInfoSize;
ULONG SecurityDescriptorSize;
LARGE_INTEGER CreationTime;
} OBJECT_BASIC_INFORMATION, * POBJECT_BASIC_INFORMATION;
void WINAPI Implementation_WakeConditionVariable(
PCONDITION_VARIABLE ConditionVariable
)
{
if (ConditionVariable)
{
PCONDITION_VARIABLE_INT pCondVar = ConditionVariable->Ptr;
if (ConditionVariable->Ptr) {
WaitForSingleObject(pCondVar->AccessSemaphore, INFINITE);
if (!pCondVar->WaiterCount) {
ReleaseSemaphore(pCondVar->AccessSemaphore, 1, NULL);
}
else {
SetEvent(pCondVar->SingleWakeEvent);
}
}
}
}
void WINAPI Implementation_WakeAllConditionVariable(
PCONDITION_VARIABLE ConditionVariable
)
{
if (ConditionVariable)
{
PCONDITION_VARIABLE_INT pCondVar = ConditionVariable->Ptr;
if (ConditionVariable->Ptr) {
WaitForSingleObject(pCondVar->AccessSemaphore, INFINITE);
if (!pCondVar->WaiterCount) {
ReleaseSemaphore(pCondVar->AccessSemaphore, 1, NULL);
}
else {
SetEvent(pCondVar->AllWakeEvent);
}
}
}
}