add a realtime scheduler class and priority-inheritance mutexes with timeouts and abandonment, add an HDA audio driver and a SYS_AUDIO_* syscall family, flesh out kernel32 with Win32 threads, events, mutexes, critical sections, and condition variables, add SYS_RESUME_THREAD for suspended thread creation, add KeInitializeMemoryTypes to reprogram MTRRs and install a canonical PAT for WB/WC/UC control, rework the display pipeline with a pitched present path and console output flush, bring back disk.vhd with mtools since vvfat fucking sucks, harden AHCI with serialized cache flush, USB mass storage with SCSI sense-data decoding, and xHCI with cleaner DMA teardown, fix the last stray for(;;) in HalStopProcessor to while(true), pile on smoke tests for audio, win32 sync primitives, and thread suspend, implement AHCI timeout, kill readahead since it was desperetaly trying to murder me, fix doom sound x2.

This commit is contained in:
maksw committed 2026-08-11 18:38:36 +02:00
1 parent 8b254f84f4
commit 4add6789ba
177 files changed
+22565 -3070

No files matched your search

+695 -65
View File
@@ -39,6 +39,54 @@ typedef long LONG;
typedef struct { LONG x; LONG y; } POINT;
typedef struct { SHORT X; SHORT Y; } COORD;
typedef struct { SHORT Left; SHORT Top; SHORT Right; SHORT Bottom; } SMALL_RECT;
typedef struct { uintptr_t _opaque[5]; } CRITICAL_SECTION;
typedef struct { void* _ptr; } CONDITION_VARIABLE;
// Forward declarations used by the process-local GetProcAddress surface.
__declspec(dllexport) HANDLE CreateEventA(void*, BOOL, BOOL, const char*);
__declspec(dllexport) HANDLE CreateEventW(void*, BOOL, BOOL, const unsigned short*);
__declspec(dllexport) BOOL SetEvent(HANDLE);
__declspec(dllexport) BOOL ResetEvent(HANDLE);
__declspec(dllexport) _Noreturn void ExitThread(DWORD);
__declspec(dllexport) HANDLE CreateThread(void*, size_t, void*, void*, DWORD, DWORD*);
__declspec(dllexport) DWORD WaitForSingleObject(HANDLE, DWORD);
__declspec(dllexport) HANDLE CreateMutexA(void*, BOOL, const char*);
__declspec(dllexport) HANDLE CreateMutexW(void*, BOOL, const unsigned short*);
__declspec(dllexport) BOOL ReleaseMutex(HANDLE);
__declspec(dllexport) BOOL CloseHandle(HANDLE);
__declspec(dllexport) void InitializeCriticalSection(CRITICAL_SECTION*);
__declspec(dllexport) BOOL InitializeCriticalSectionAndSpinCount(CRITICAL_SECTION*, DWORD);
__declspec(dllexport) void DeleteCriticalSection(CRITICAL_SECTION*);
__declspec(dllexport) void EnterCriticalSection(CRITICAL_SECTION*);
__declspec(dllexport) void LeaveCriticalSection(CRITICAL_SECTION*);
__declspec(dllexport) BOOL TryEnterCriticalSection(CRITICAL_SECTION*);
__declspec(dllexport) void InitializeConditionVariable(CONDITION_VARIABLE*);
__declspec(dllexport) BOOL SleepConditionVariableCS(CONDITION_VARIABLE*, CRITICAL_SECTION*, DWORD);
__declspec(dllexport) void WakeConditionVariable(CONDITION_VARIABLE*);
__declspec(dllexport) void WakeAllConditionVariable(CONDITION_VARIABLE*);
__declspec(dllexport) BOOL IsDebuggerPresent(void);
extern void waveOutGetNumDevs(void);
extern void waveOutOpen(void);
extern void waveOutClose(void);
extern void waveOutPrepareHeader(void);
extern void waveOutUnprepareHeader(void);
extern void waveOutWrite(void);
extern void waveOutPause(void);
extern void waveOutRestart(void);
extern void waveOutReset(void);
extern void waveOutSetVolume(void);
extern void waveOutGetVolume(void);
extern void waveOutGetPosition(void);
extern void waveOutGetID(void);
extern void waveOutGetDevCapsA(void);
extern void waveOutGetDevCapsW(void);
extern void waveOutGetErrorTextA(void);
extern void waveOutGetErrorTextW(void);
extern void waveOutBreakLoop(void);
extern void waveOutMessage(void);
extern void timeGetTime(void);
extern void timeBeginPeriod(void);
extern void timeEndPeriod(void);
typedef struct _CONSOLE_SCREEN_BUFFER_INFO {
COORD dwSize;
@@ -318,6 +366,9 @@ static DWORD* K32LastErrorSlot(void) {
#define gLastError (*K32LastErrorSlot())
// Returns a stable nonzero identifier for the calling thread.
static DWORD K32CurrentTidOrOne(void);
// Implements the file Exists A operation.
static BOOL KiFileExistsA(const char* path) {
if (!path || !*path) return FALSE;
@@ -976,11 +1027,189 @@ __declspec(dllexport) BOOL DeleteFileA(const char* lpFileName) {
return TRUE;
}
// CloseHandle - unified close for files, threads, mutexes, semaphores, events, queues, and processes.
__declspec(dllexport) BOOL CloseHandle(HANDLE hObject) {
if (!hObject || hObject == INVALID_HANDLE_VALUE) { gLastError = 6; return FALSE; }
#define K32_MUTEX_MAGIC 0x58544D4Bu
SysCloseHandle(HANDLE_TO_KH(hObject));
typedef struct K32_MUTEX_HANDLE {
struct K32_MUTEX_HANDLE* Next;
uint32_t Magic;
volatile long StateLock;
volatile long ApiUsers;
uint32_t NativeHandle;
DWORD OwnerTid;
uint32_t Recursion;
uint32_t Waiters;
volatile long Closing;
} K32_MUTEX_HANDLE;
static K32_MUTEX_HANDLE* gK32MutexRegistry;
static volatile long gK32MutexRegistryLock;
// Acquires one short process-local mutex registry lock.
static void K32MutexRegistryLock(void) {
while (_InterlockedCompareExchange(&gK32MutexRegistryLock, 1, 0) != 0)
(void)SysYield();
}
// Releases the process-local mutex registry lock.
static void K32MutexRegistryUnlock(void) {
_InterlockedExchange(&gK32MutexRegistryLock, 0);
}
// Acquires one short mutex-wrapper state lock.
static void K32MutexStateLock(K32_MUTEX_HANDLE* mutex) {
while (_InterlockedCompareExchange(&mutex->StateLock, 1, 0) != 0)
(void)SysYield();
}
// Releases one mutex-wrapper state lock.
static void K32MutexStateUnlock(K32_MUTEX_HANDLE* mutex) {
_InterlockedExchange(&mutex->StateLock, 0);
}
// Registers one process-local Win32 mutex wrapper.
static void K32RegisterMutex(K32_MUTEX_HANDLE* mutex) {
K32MutexRegistryLock();
mutex->Next = gK32MutexRegistry;
gK32MutexRegistry = mutex;
K32MutexRegistryUnlock();
}
// References one registered Win32 mutex without dereferencing unknown handles.
static K32_MUTEX_HANDLE* K32ReferenceMutex(HANDLE handle) {
K32_MUTEX_HANDLE* found = NULL;
K32MutexRegistryLock();
for (K32_MUTEX_HANDLE* current = gK32MutexRegistry;
current;
current = current->Next) {
if ((HANDLE)current == handle &&
current->Magic == K32_MUTEX_MAGIC &&
_InterlockedCompareExchange(&current->Closing, 0, 0) == 0) {
_InterlockedIncrement(&current->ApiUsers);
found = current;
break;
}
}
K32MutexRegistryUnlock();
return found;
}
// Releases one mutex-wrapper API reference.
static void K32DereferenceMutex(K32_MUTEX_HANDLE* mutex) {
if (mutex)
_InterlockedDecrement(&mutex->ApiUsers);
}
// Removes one mutex wrapper so no new API references can be acquired.
static BOOL K32RemoveMutex(K32_MUTEX_HANDLE* mutex) {
BOOL removed = FALSE;
K32MutexRegistryLock();
K32_MUTEX_HANDLE** link = &gK32MutexRegistry;
while (*link) {
if (*link == mutex) {
*link = mutex->Next;
mutex->Next = NULL;
removed = TRUE;
break;
}
link = &(*link)->Next;
}
K32MutexRegistryUnlock();
return removed;
}
// Waits on one recursive Win32 mutex wrapper.
static DWORD K32WaitMutex(K32_MUTEX_HANDLE* mutex, DWORD milliseconds) {
DWORD tid = K32CurrentTidOrOne();
K32MutexStateLock(mutex);
if (_InterlockedCompareExchange(&mutex->Closing, 0, 0) != 0) {
K32MutexStateUnlock(mutex);
gLastError = 6;
return 0xFFFFFFFFUL;
}
if (mutex->OwnerTid == tid) {
if (mutex->Recursion == UINT32_MAX) {
K32MutexStateUnlock(mutex);
gLastError = 8;
return 0xFFFFFFFFUL;
}
mutex->Recursion++;
K32MutexStateUnlock(mutex);
return 0;
}
mutex->Waiters++;
K32MutexStateUnlock(mutex);
uint64_t timeout = milliseconds == 0xFFFFFFFFUL
? SYS_WAIT_TIMEOUT_INFINITE
: (uint64_t)milliseconds;
uint32_t status = SysWaitSingle(mutex->NativeHandle, SYS_WAIT_FLAG_NONE, timeout);
K32MutexStateLock(mutex);
if (mutex->Waiters)
mutex->Waiters--;
if (status == NTLSTATUS_SUCCESS || status == NTLSTATUS_ABANDONED) {
mutex->OwnerTid = tid;
mutex->Recursion = 1;
}
K32MutexStateUnlock(mutex);
if (status == NTLSTATUS_SUCCESS)
return 0;
if (status == NTLSTATUS_ABANDONED)
return 0x00000080UL;
if (status == NTLSTATUS_TIMEOUT)
return 0x00000102UL;
gLastError = 6;
return 0xFFFFFFFFUL;
}
// Closes one idle Win32 mutex wrapper.
static BOOL K32CloseMutex(K32_MUTEX_HANDLE* mutex) {
K32MutexStateLock(mutex);
BOOL busy = mutex->OwnerTid != 0 || mutex->Waiters != 0;
if (!busy && _InterlockedCompareExchange(&mutex->Closing, 1, 0) != 0)
busy = TRUE;
K32MutexStateUnlock(mutex);
if (busy) {
gLastError = 170;
K32DereferenceMutex(mutex);
return FALSE;
}
if (!K32RemoveMutex(mutex)) {
gLastError = 6;
K32DereferenceMutex(mutex);
return FALSE;
}
while (mutex->ApiUsers > 1)
(void)SysYield();
uint32_t status = SysCloseHandle(mutex->NativeHandle);
mutex->Magic = 0;
K32DereferenceMutex(mutex);
free(mutex);
if (status != NTLSTATUS_SUCCESS) {
gLastError = 6;
return FALSE;
}
return TRUE;
}
// Closes one Win32 or native kernel handle.
__declspec(dllexport) BOOL CloseHandle(HANDLE hObject) {
if (!hObject || hObject == INVALID_HANDLE_VALUE) {
gLastError = 6;
return FALSE;
}
K32_MUTEX_HANDLE* mutex = K32ReferenceMutex(hObject);
if (mutex)
return K32CloseMutex(mutex);
uint32_t status = SysCloseHandle(HANDLE_TO_KH(hObject));
if (status != NTLSTATUS_SUCCESS) {
gLastError = 6;
return FALSE;
}
return TRUE;
}
@@ -1087,12 +1316,32 @@ static int KiStricmpA(const char* a, const char* b) {
return ((unsigned char)*a | 0x20) - ((unsigned char)*b | 0x20);
}
// Checks whether win32 Dll Name A.
// Matches one Win32 module basename with its optional DLL suffix.
static BOOL KiMatchesWin32DllNameA(const char* name, const char* baseName) {
if (!name || !baseName)
return FALSE;
while (*name && *baseName &&
(((unsigned char)*name | 0x20u) == ((unsigned char)*baseName | 0x20u))) {
name++;
baseName++;
}
if (*baseName)
return FALSE;
if (!*name)
return TRUE;
return KiStricmpA(name, ".dll") == 0;
}
// Checks whether one name identifies a process-local Win32 personality module.
static BOOL KiIsWin32DllNameA(const char* name) {
return name && (
!KiStricmpA(name, "kernel32.dll") || !KiStricmpA(name, "user32.dll") ||
!KiStricmpA(name, "gdi32.dll") || !KiStricmpA(name, "ole32.dll") ||
!KiStricmpA(name, "winmm.dll") || !KiStricmpA(name, "win32.dll"));
return KiMatchesWin32DllNameA(name, "kernel32") ||
KiMatchesWin32DllNameA(name, "user32") ||
KiMatchesWin32DllNameA(name, "gdi32") ||
KiMatchesWin32DllNameA(name, "ole32") ||
KiMatchesWin32DllNameA(name, "winmm") ||
KiMatchesWin32DllNameA(name, "win32");
}
// Implements the dbg Write A operation.
@@ -1100,11 +1349,13 @@ static void KiDbgWriteA(const char* s) {
if (s) K32WriteConsoleSpan(s, strlen(s));
}
// Returns module Handle A.
// Returns the process-local module handle for one ANSI module name.
__declspec(dllexport) HANDLE GetModuleHandleA(const char* lpModuleName) {
if (!lpModuleName) return WIN32_HMODULE_EXE;
if (KiIsWin32DllNameA(lpModuleName)) return WIN32_HMODULE_WIN32;
gLastError = 126; // ERROR_MOD_NOT_FOUND
if (!lpModuleName)
return WIN32_HMODULE_EXE;
if (KiIsWin32DllNameA(lpModuleName))
return WIN32_HMODULE_WIN32;
gLastError = 126; // ERROR_MOD_NOT_FOUND.
return NULL;
}
@@ -1175,13 +1426,31 @@ __declspec(dllexport) void* GetProcAddress(HANDLE hModule, const char* lpProcNam
return (void*)address;
}
#define GP(name) if (strcmp(lpProcName, #name) == 0) return (void*)&name
#define GP(name) do { \
if (strcmp(lpProcName, #name) == 0) return (void*)&name; \
} while (0)
GP(GetLastError); GP(SetLastError); GP(LoadLibraryA); GP(GetProcAddress);
GP(IsDebuggerPresent);
GP(FreeLibrary); GP(VirtualAlloc); GP(VirtualFree); GP(VirtualProtect);
GP(VirtualQuery); GP(GetTickCount); GP(GetTickCount64); GP(Sleep);
GP(CreateEventA); GP(CreateEventW); GP(SetEvent); GP(ResetEvent);
GP(CreateThread); GP(ExitThread); GP(WaitForSingleObject);
GP(CreateMutexA); GP(CreateMutexW); GP(ReleaseMutex); GP(CloseHandle);
GP(InitializeCriticalSection); GP(InitializeCriticalSectionAndSpinCount);
GP(DeleteCriticalSection); GP(EnterCriticalSection); GP(LeaveCriticalSection);
GP(TryEnterCriticalSection); GP(InitializeConditionVariable);
GP(SleepConditionVariableCS); GP(WakeConditionVariable); GP(WakeAllConditionVariable);
GP(TlsAlloc); GP(TlsFree); GP(TlsGetValue); GP(TlsSetValue);
GP(GetConsoleScreenBufferInfo); GP(FillConsoleOutputCharacterA);
GP(FillConsoleOutputAttribute); GP(SetConsoleCursorPosition);
GP(waveOutGetNumDevs); GP(waveOutOpen); GP(waveOutClose);
GP(waveOutPrepareHeader); GP(waveOutUnprepareHeader); GP(waveOutWrite);
GP(waveOutPause); GP(waveOutRestart); GP(waveOutReset);
GP(waveOutSetVolume); GP(waveOutGetVolume); GP(waveOutGetPosition); GP(waveOutGetID);
GP(waveOutGetDevCapsA); GP(waveOutGetDevCapsW);
GP(waveOutGetErrorTextA); GP(waveOutGetErrorTextW);
GP(waveOutBreakLoop); GP(waveOutMessage);
GP(timeGetTime); GP(timeBeginPeriod); GP(timeEndPeriod);
#undef GP
KiDbgWriteA("GetProcAddress miss: ");
KiDbgWriteA(lpProcName);
@@ -1275,53 +1544,163 @@ __declspec(dllexport) char* GetCommandLineA(void) {
#define WAIT_OBJECT_0 0x00000000UL
#define WAIT_TIMEOUT 0x00000102UL
#define INFINITE 0xFFFFFFFFUL
#define CREATE_SUSPENDED 0x00000004UL
typedef DWORD (__stdcall *K32_THREAD_START_ROUTINE)(void* parameter);
typedef struct K32_THREAD_START_CONTEXT {
K32_THREAD_START_ROUTINE StartRoutine;
void* Parameter;
} K32_THREAD_START_CONTEXT;
// Invokes one Win32 thread procedure and terminates the native thread on return.
static void K32ThreadStart(uintptr_t argument) {
K32_THREAD_START_CONTEXT* context = (K32_THREAD_START_CONTEXT*)argument;
K32_THREAD_START_ROUTINE startRoutine = context->StartRoutine;
void* parameter = context->Parameter;
free(context);
DWORD exitCode = startRoutine(parameter);
SysExitThread((int32_t)exitCode);
while (true) {
(void)SysYield();
}
}
// Creates one Win32 thread with return-to-ExitThread semantics.
__declspec(dllexport) HANDLE CreateThread(void* lpThreadAttributes,
size_t dwStackSize,
void* lpStartAddress,
void* lpParameter,
DWORD dwCreationFlags,
DWORD* lpThreadId) {
(void)lpThreadAttributes; (void)dwStackSize; (void)dwCreationFlags;
(void)lpThreadAttributes;
(void)dwStackSize;
if (!lpStartAddress || (dwCreationFlags & ~CREATE_SUSPENDED)) {
gLastError = 87;
return NULL;
}
K32_THREAD_START_CONTEXT* context =
(K32_THREAD_START_CONTEXT*)malloc(sizeof(*context));
if (!context) {
gLastError = 8;
return NULL;
}
context->StartRoutine = (K32_THREAD_START_ROUTINE)lpStartAddress;
context->Parameter = lpParameter;
uint32_t tid = 0;
uint32_t kh = 0;
SYS_CREATE_THREAD_REQUEST req = {
(uintptr_t)lpStartAddress,
(uintptr_t)lpParameter,
0,
(uintptr_t)K32ThreadStart,
(uintptr_t)context,
0,
(dwCreationFlags & CREATE_SUSPENDED)
? SYS_CREATE_THREAD_FLAG_SUSPENDED
: SYS_CREATE_THREAD_FLAG_NONE,
&kh,
&tid
};
uint32_t s = SysCreateThread(&req);
if (s != 0) { gLastError = 11; return INVALID_HANDLE_VALUE; }
if (lpThreadId) *lpThreadId = (DWORD)tid;
uint32_t status = SysCreateThread(&req);
if (status != NTLSTATUS_SUCCESS) {
free(context);
gLastError = status == NTLSTATUS_NO_MEMORY ? 8 : 11;
return NULL;
}
if (lpThreadId)
*lpThreadId = (DWORD)tid;
return KH_TO_HANDLE(kh);
}
// WaitForSingleObject - blocks on native waitable handles. A zero timeout is
// only a probe for mutexes/semaphores until native timeout waits exist.
// Terminates the calling Win32 thread with the supplied exit code.
__declspec(dllexport) _Noreturn void ExitThread(DWORD exitCode) {
SysExitThread((int32_t)exitCode);
while (true) {
(void)SysYield();
}
}
// Waits for one Win32 mutex wrapper or native waitable handle.
__declspec(dllexport) DWORD WaitForSingleObject(HANDLE hHandle, DWORD dwMilliseconds) {
if (!IS_VALID_KERNEL_HANDLE(hHandle)) { gLastError = 6; return 0xFFFFFFFFUL; }
uint32_t kh = HANDLE_TO_KH(hHandle);
if (dwMilliseconds == 0) {
uint32_t s = SysTryAcquireMutex(kh);
if (s == 0) return WAIT_OBJECT_0;
s = SysTryWaitSem(kh);
if (s == 0) return WAIT_OBJECT_0;
return WAIT_TIMEOUT;
if (!hHandle || hHandle == INVALID_HANDLE_VALUE) {
gLastError = 6;
return 0xFFFFFFFFUL;
}
uint32_t s = SysWait(kh, SYS_WAIT_FLAG_NONE);
if (s == 0) return WAIT_OBJECT_0;
K32_MUTEX_HANDLE* mutex = K32ReferenceMutex(hHandle);
if (mutex) {
DWORD result = K32WaitMutex(mutex, dwMilliseconds);
K32DereferenceMutex(mutex);
return result;
}
if (!IS_VALID_KERNEL_HANDLE(hHandle)) {
gLastError = 6;
return 0xFFFFFFFFUL;
}
uint64_t timeout = dwMilliseconds == INFINITE
? SYS_WAIT_TIMEOUT_INFINITE
: (uint64_t)dwMilliseconds;
uint32_t s = SysWaitSingle(HANDLE_TO_KH(hHandle), SYS_WAIT_FLAG_NONE, timeout);
if (s == NTLSTATUS_SUCCESS) return WAIT_OBJECT_0;
if (s == NTLSTATUS_ABANDONED) return 0x00000080UL;
if (s == NTLSTATUS_TIMEOUT) return WAIT_TIMEOUT;
gLastError = 6;
return 0xFFFFFFFFUL;
}
// Creates one unnamed native event with Win32 reset semantics.
__declspec(dllexport) HANDLE CreateEventA(void* lpEventAttributes,
BOOL bManualReset,
BOOL bInitialState,
const char* lpName) {
(void)lpEventAttributes;
if (lpName && lpName[0]) {
gLastError = 120;
return NULL;
}
uint32_t handle = 0;
uint32_t status = SysCreateEvent(bManualReset ? 1u : 0u,
bInitialState ? 1u : 0u,
&handle);
if (status != NTLSTATUS_SUCCESS) {
gLastError = status == NTLSTATUS_NO_MEMORY ? 8 : 87;
return NULL;
}
return KH_TO_HANDLE(handle);
}
// Creates one unnamed native event from the wide Win32 entry point.
__declspec(dllexport) HANDLE CreateEventW(void* lpEventAttributes,
BOOL bManualReset,
BOOL bInitialState,
const unsigned short* lpName) {
if (lpName && lpName[0]) {
gLastError = 120;
return NULL;
}
return CreateEventA(lpEventAttributes, bManualReset, bInitialState, NULL);
}
// Signals one native event.
__declspec(dllexport) BOOL SetEvent(HANDLE hEvent) {
if (!IS_VALID_KERNEL_HANDLE(hEvent)) { gLastError = 6; return FALSE; }
uint32_t status = SysSignalEvent(HANDLE_TO_KH(hEvent));
if (status != NTLSTATUS_SUCCESS) { gLastError = 6; return FALSE; }
return TRUE;
}
// Clears one native event.
__declspec(dllexport) BOOL ResetEvent(HANDLE hEvent) {
if (!IS_VALID_KERNEL_HANDLE(hEvent)) { gLastError = 6; return FALSE; }
uint32_t status = SysClearEvent(HANDLE_TO_KH(hEvent));
if (status != NTLSTATUS_SUCCESS) { gLastError = 6; return FALSE; }
return TRUE;
}
// Implements the extract Application Path A operation.
static BOOL ExtractApplicationPathA(const char* commandLine, char* out, size_t outSize) {
if (!commandLine || !out || outSize == 0) return FALSE;
@@ -1449,10 +1828,22 @@ __declspec(dllexport) BOOL GetExitCodeProcess(HANDLE hProcess, DWORD* lpExitCode
return TRUE;
}
// Implements the resume Thread operation.
// Decrements a thread's suspend count and makes it runnable at zero.
__declspec(dllexport) DWORD ResumeThread(HANDLE hThread) {
(void)hThread;
return 0; // previous suspend count
if (!hThread || hThread == INVALID_HANDLE_VALUE) {
gLastError = 6;
return 0xFFFFFFFFu;
}
uint32_t previousSuspendCount = 0;
uint32_t status = SysResumeThread(HANDLE_TO_KH(hThread),
&previousSuspendCount);
if (status != NTLSTATUS_SUCCESS) {
gLastError = status == NTLSTATUS_ACCESS_DENIED ? 5 : 6;
return 0xFFFFFFFFu;
}
return previousSuspendCount;
}
#define THREAD_PRIORITY_NORMAL 0
@@ -1876,7 +2267,7 @@ __declspec(dllexport) HANDLE FindFirstFileA(const char* lpFileName,
// Read the first entry that matches the pattern.
KDIR_ENTRY ke;
for (;;) {
while (true) {
uint32_t rs = SysReadDir(kh, &ke);
if (rs != 0) {
// Directory exhausted or error.
@@ -1902,7 +2293,7 @@ __declspec(dllexport) BOOL FindNextFileA(HANDLE hFindFile,
FIND_HANDLE* fh = (FIND_HANDLE*)hFindFile;
KDIR_ENTRY ke;
for (;;) {
while (true) {
uint32_t rs = SysReadDir(fh->kh, &ke);
if (rs != 0) {
gLastError = 18; // ERROR_NO_MORE_FILES
@@ -1979,28 +2370,88 @@ __declspec(dllexport) DWORD GetCurrentDirectoryA(DWORD nBufferLength, char* lpBu
return (DWORD)(needed - 1); // length without NUL, matching Win32
}
// Creates one unnamed recursive Win32 mutex over a native kernel mutex.
__declspec(dllexport) HANDLE CreateMutexA(void* lpMutexAttributes, BOOL bInitialOwner,
const char* lpName) {
(void)lpMutexAttributes; (void)lpName;
uint32_t kh = 0;
uint32_t s = SysCreateMutex(&kh);
if (s != 0 || !kh) { gLastError = 8; return NULL; }
(void)lpMutexAttributes;
if (lpName && lpName[0]) {
gLastError = 120;
return NULL;
}
K32_MUTEX_HANDLE* mutex = (K32_MUTEX_HANDLE*)calloc(1, sizeof(*mutex));
if (!mutex) {
gLastError = 8;
return NULL;
}
uint32_t status = SysCreateMutex(&mutex->NativeHandle);
if (status != NTLSTATUS_SUCCESS || !mutex->NativeHandle) {
free(mutex);
gLastError = status == NTLSTATUS_NO_MEMORY ? 8 : 1;
return NULL;
}
mutex->Magic = K32_MUTEX_MAGIC;
if (bInitialOwner) {
s = SysWait(kh, SYS_WAIT_FLAG_NONE);
if (s != 0) {
SysCloseHandle(kh);
status = SysWait(mutex->NativeHandle, SYS_WAIT_FLAG_NONE);
if (status != NTLSTATUS_SUCCESS && status != NTLSTATUS_ABANDONED) {
(void)SysCloseHandle(mutex->NativeHandle);
free(mutex);
gLastError = 1;
return NULL;
}
mutex->OwnerTid = K32CurrentTidOrOne();
mutex->Recursion = 1;
}
return KH_TO_HANDLE(kh);
K32RegisterMutex(mutex);
return (HANDLE)mutex;
}
// Releases mutex.
// Creates one unnamed recursive Win32 mutex from a wide-name request.
__declspec(dllexport) HANDLE CreateMutexW(void* lpMutexAttributes, BOOL bInitialOwner,
const unsigned short* lpName) {
if (lpName && lpName[0]) {
gLastError = 120;
return NULL;
}
return CreateMutexA(lpMutexAttributes, bInitialOwner, NULL);
}
// Releases one recursion level from a Win32 mutex wrapper.
__declspec(dllexport) BOOL ReleaseMutex(HANDLE hMutex) {
if (!IS_VALID_KERNEL_HANDLE(hMutex)) { gLastError = 6; return FALSE; }
uint32_t s = SysReleaseMutex(HANDLE_TO_KH(hMutex));
if (s != 0) { gLastError = 6; return FALSE; }
K32_MUTEX_HANDLE* mutex = K32ReferenceMutex(hMutex);
if (!mutex) {
gLastError = 6;
return FALSE;
}
DWORD tid = K32CurrentTidOrOne();
K32MutexStateLock(mutex);
if (mutex->OwnerTid != tid || mutex->Recursion == 0) {
K32MutexStateUnlock(mutex);
K32DereferenceMutex(mutex);
gLastError = 288;
return FALSE;
}
mutex->Recursion--;
if (mutex->Recursion != 0) {
K32MutexStateUnlock(mutex);
K32DereferenceMutex(mutex);
return TRUE;
}
mutex->OwnerTid = 0;
K32MutexStateUnlock(mutex);
uint32_t status = SysReleaseMutex(mutex->NativeHandle);
if (status != NTLSTATUS_SUCCESS) {
K32MutexStateLock(mutex);
mutex->OwnerTid = tid;
mutex->Recursion = 1;
K32MutexStateUnlock(mutex);
K32DereferenceMutex(mutex);
gLastError = 288;
return FALSE;
}
K32DereferenceMutex(mutex);
return TRUE;
}
@@ -2030,8 +2481,19 @@ __declspec(dllexport) BOOL ReleaseSemaphore(HANDLE hSemaphore, long lReleaseCoun
// using the storage as a small recursive spin lock. Bink uses these from its
// worker/copy paths, so the old no-op implementation allowed internal state
// races as soon as CreateThread started working.
typedef struct { uintptr_t _opaque[5]; } CRITICAL_SECTION;
typedef struct { void* _ptr; } CONDITION_VARIABLE;
typedef struct K32_CONDITION_WAITER K32_CONDITION_WAITER;
typedef struct K32_CONDITION_STATE {
volatile long Lock;
K32_CONDITION_WAITER* Head;
K32_CONDITION_WAITER* Tail;
} K32_CONDITION_STATE;
struct K32_CONDITION_WAITER {
K32_CONDITION_WAITER* Previous;
K32_CONDITION_WAITER* Next;
uint32_t Event;
BOOL Queued;
};
#define K32_CS_LOCK 0
#define K32_CS_OWNER 1
@@ -2067,8 +2529,12 @@ __declspec(dllexport) void EnterCriticalSection(CRITICAL_SECTION* lpCS) {
lpCS->_opaque[K32_CS_RECURSION]++;
return;
}
while (_InterlockedCompareExchange(K32CsLockWord(lpCS), 1, 0) != 0)
uint32_t spins = 0;
while (_InterlockedCompareExchange(K32CsLockWord(lpCS), 1, 0) != 0) {
_mm_pause();
if ((++spins & 0xFFFu) == 0)
(void)SysYield();
}
lpCS->_opaque[K32_CS_OWNER] = tid;
lpCS->_opaque[K32_CS_RECURSION] = 1;
}
@@ -2100,19 +2566,183 @@ __declspec(dllexport) BOOL TryEnterCriticalSection(CRITICAL_SECTION* lpCS) {
return TRUE;
}
// Initializes condition Variable.
__declspec(dllexport) void InitializeConditionVariable(CONDITION_VARIABLE* cv) { (void)cv; }
// Acquires one condition-variable queue lock.
static void K32ConditionLock(K32_CONDITION_STATE* state) {
uint32_t spins = 0;
while (_InterlockedCompareExchange(&state->Lock, 1, 0) != 0) {
_mm_pause();
if ((++spins & 0xFFFu) == 0)
(void)SysYield();
}
}
// Releases one condition-variable queue lock.
static void K32ConditionUnlock(K32_CONDITION_STATE* state) {
_ReadWriteBarrier();
_InterlockedExchange(&state->Lock, 0);
}
// Returns the lazily allocated process-local condition-variable state.
static K32_CONDITION_STATE* K32ConditionState(CONDITION_VARIABLE* cv, BOOL create) {
if (!cv)
return NULL;
K32_CONDITION_STATE* state = (K32_CONDITION_STATE*)cv->_ptr;
if (state || !create)
return state;
K32_CONDITION_STATE* allocated = (K32_CONDITION_STATE*)calloc(1, sizeof(*allocated));
if (!allocated)
return NULL;
void* previous = _InterlockedCompareExchangePointer((void* volatile*)&cv->_ptr,
allocated,
NULL);
if (previous) {
free(allocated);
state = (K32_CONDITION_STATE*)previous;
} else {
state = allocated;
}
return state;
}
// Removes one waiter while the condition-variable queue lock is held.
static void K32ConditionRemoveWaiter(K32_CONDITION_STATE* state,
K32_CONDITION_WAITER* waiter) {
if (!waiter->Queued)
return;
if (waiter->Previous)
waiter->Previous->Next = waiter->Next;
else
state->Head = waiter->Next;
if (waiter->Next)
waiter->Next->Previous = waiter->Previous;
else
state->Tail = waiter->Previous;
waiter->Previous = NULL;
waiter->Next = NULL;
waiter->Queued = FALSE;
}
// Initializes one process-local condition variable.
__declspec(dllexport) void InitializeConditionVariable(CONDITION_VARIABLE* cv) {
if (cv)
cv->_ptr = NULL;
}
// Atomically releases a critical section and waits for one condition wake.
__declspec(dllexport) BOOL SleepConditionVariableCS(CONDITION_VARIABLE* cv,
CRITICAL_SECTION* cs,
DWORD dwMilliseconds) {
(void)cv; (void)cs; (void)dwMilliseconds;
return TRUE;
if (!cv || !cs) {
gLastError = 87;
return FALSE;
}
DWORD tid = K32CurrentTidOrOne();
uintptr_t recursion = cs->_opaque[K32_CS_RECURSION];
if ((DWORD)cs->_opaque[K32_CS_OWNER] != tid || recursion == 0) {
gLastError = 87;
return FALSE;
}
K32_CONDITION_STATE* state = K32ConditionState(cv, TRUE);
if (!state) {
gLastError = 8;
return FALSE;
}
uint32_t event = 0;
KSTATUS status = SysCreateEvent(0, 0, &event);
if (status != NTLSTATUS_SUCCESS) {
gLastError = status == NTLSTATUS_NO_MEMORY ? 8 : 87;
return FALSE;
}
// The waiter is heap-backed because process termination can stop a user
// thread at syscall return before it can remove a stack-backed queue node.
K32_CONDITION_WAITER* waiter = (K32_CONDITION_WAITER*)calloc(1, sizeof(*waiter));
if (!waiter) {
(void)SysCloseHandle(event);
gLastError = 8;
return FALSE;
}
waiter->Event = event;
waiter->Queued = TRUE;
K32ConditionLock(state);
waiter->Previous = state->Tail;
if (state->Tail)
state->Tail->Next = waiter;
else
state->Head = waiter;
state->Tail = waiter;
K32ConditionUnlock(state);
// The waiter is visible before the lock is released, so a wake that races
// this transition is retained by the private auto-reset event.
cs->_opaque[K32_CS_RECURSION] = 0;
cs->_opaque[K32_CS_OWNER] = 0;
_ReadWriteBarrier();
_InterlockedExchange(K32CsLockWord(cs), 0);
uint64_t timeout = dwMilliseconds == INFINITE
? SYS_WAIT_TIMEOUT_INFINITE
: (uint64_t)dwMilliseconds;
status = SysWaitSingle(event, SYS_WAIT_FLAG_NONE, timeout);
K32ConditionLock(state);
BOOL wakeWon = waiter->Queued ? FALSE : TRUE;
K32ConditionRemoveWaiter(state, waiter);
K32ConditionUnlock(state);
EnterCriticalSection(cs);
cs->_opaque[K32_CS_RECURSION] = recursion;
(void)SysCloseHandle(event);
free(waiter);
if (status == NTLSTATUS_SUCCESS || wakeWon)
return TRUE;
if (status == NTLSTATUS_TIMEOUT)
gLastError = 1460;
else
gLastError = 6;
return FALSE;
}
// Wakes the oldest waiter on one condition variable.
__declspec(dllexport) void WakeConditionVariable(CONDITION_VARIABLE* cv) {
K32_CONDITION_STATE* state = K32ConditionState(cv, FALSE);
if (!state)
return;
K32ConditionLock(state);
K32_CONDITION_WAITER* waiter = state->Head;
if (waiter) {
K32ConditionRemoveWaiter(state, waiter);
(void)SysSignalEvent(waiter->Event);
}
K32ConditionUnlock(state);
}
// Wakes every waiter currently queued on one condition variable.
__declspec(dllexport) void WakeAllConditionVariable(CONDITION_VARIABLE* cv) {
K32_CONDITION_STATE* state = K32ConditionState(cv, FALSE);
if (!state)
return;
K32ConditionLock(state);
while (state->Head) {
K32_CONDITION_WAITER* waiter = state->Head;
K32ConditionRemoveWaiter(state, waiter);
(void)SysSignalEvent(waiter->Event);
}
K32ConditionUnlock(state);
}
// Implements the wake Condition Variable operation.
__declspec(dllexport) void WakeConditionVariable(CONDITION_VARIABLE* cv) { (void)cv; }
// Implements the wake All Condition Variable operation.
__declspec(dllexport) void WakeAllConditionVariable(CONDITION_VARIABLE* cv) { (void)cv; }
// Timing & Performance Counters.
@@ -2961,7 +3591,7 @@ SetUnhandledExceptionFilter(LPTOP_LEVEL_EXCEPTION_FILTER lpFilter) {
return NULL; // previous filter: none
}
// Checks whether debugger Present.
// Reports that no user-mode debugger is attached.
__declspec(dllexport) BOOL IsDebuggerPresent(void) {
return FALSE;
}