Files
nt-lite/code/user/tests/win32_mutex_smoke/win32_mutex_smoke.c
T

148 lines
5.1 KiB
C

// Win32 recursive mutex smoke test for Bink worker teardown.
#include <stdint.h>
#include <stddef.h>
#include <ntlite/syscall.h>
typedef void* HANDLE;
typedef unsigned long DWORD;
typedef int BOOL;
typedef DWORD (__stdcall *THREAD_START_ROUTINE)(void* parameter);
#define FALSE 0
#define TRUE 1
#define INFINITE 0xFFFFFFFFu
#define WAIT_OBJECT_0 0u
#define WAIT_TIMEOUT 0x00000102u
#define WAIT_FAILED 0xFFFFFFFFu
#define INVALID_HANDLE_VALUE ((HANDLE)(intptr_t)-1)
__declspec(dllimport) HANDLE CreateMutexA(void* attributes, BOOL initialOwner, const char* name);
__declspec(dllimport) BOOL ReleaseMutex(HANDLE mutex);
__declspec(dllimport) HANDLE CreateThread(void* attributes,
size_t stackSize,
THREAD_START_ROUTINE startRoutine,
void* parameter,
DWORD creationFlags,
DWORD* threadId);
__declspec(dllimport) DWORD WaitForSingleObject(HANDLE handle, DWORD milliseconds);
__declspec(dllimport) BOOL CloseHandle(HANDLE handle);
__declspec(dllimport) DWORD GetLastError(void);
static HANDLE gMutex;
static DWORD gWorkerWait;
static DWORD gWorkerError;
static BOOL gWorkerRelease;
// Writes one test message to the native console.
static void print(const char* text) {
SYS_WRITE_CONSOLE_REQUEST request = { SysSvFromCstr(text) };
(void)SysWriteConsole(&request);
}
// Terminates the test after one contract failure.
static void fail(const char* message) {
print("win32_mutex_smoke: ");
print(message);
print("\n");
while (true) (void)SysExitProcess(1);
}
// Writes one fixed-width hexadecimal value to the native console.
static void print_hex32(DWORD value) {
static const char digits[] = "0123456789ABCDEF";
char text[] = "0x00000000";
for (unsigned i = 0; i < 8; i++) {
text[9 - i] = digits[value & 0xFu];
value >>= 4;
}
print(text);
}
// Reports a failed worker wait with both Win32 result channels.
static void fail_worker_wait(const char* message) {
print("win32_mutex_smoke: ");
print(message);
print(" result=");
print_hex32(gWorkerWait);
print(" error=");
print_hex32(gWorkerError);
print("\n");
while (true) (void)SysExitProcess(1);
}
// Attempts one wait and releases the mutex only after successful acquisition.
static DWORD __stdcall mutex_worker(void* parameter) {
DWORD timeout = (DWORD)(uintptr_t)parameter;
gWorkerWait = WaitForSingleObject(gMutex, timeout);
gWorkerError = gWorkerWait == WAIT_FAILED ? GetLastError() : 0;
if (gWorkerWait == WAIT_OBJECT_0)
gWorkerRelease = ReleaseMutex(gMutex);
return 0;
}
// Runs one worker and waits for its normal return.
static void run_worker(DWORD timeout) {
gWorkerWait = 0xFFFFFFFFu;
gWorkerError = 0;
gWorkerRelease = FALSE;
HANDLE worker = CreateThread(NULL, 0, mutex_worker,
(void*)(uintptr_t)timeout, 0, NULL);
if (!worker || worker == INVALID_HANDLE_VALUE)
fail("CreateThread failed");
if (WaitForSingleObject(worker, 5000u) != WAIT_OBJECT_0)
fail("worker did not return");
if (!CloseHandle(worker))
fail("worker CloseHandle failed");
}
// Verifies recursive ownership, non-owner safety, and final release transfer.
int main(void) {
print("win32_mutex_smoke: begin\n");
gMutex = CreateMutexA(NULL, FALSE, NULL);
if (!gMutex || gMutex == INVALID_HANDLE_VALUE)
fail("CreateMutexA failed");
if (WaitForSingleObject(gMutex, INFINITE) != WAIT_OBJECT_0 ||
WaitForSingleObject(gMutex, INFINITE) != WAIT_OBJECT_0)
fail("recursive acquisition failed");
run_worker(25u);
if (gWorkerWait == WAIT_FAILED)
fail_worker_wait("doubly-owned finite wait failed");
if (gWorkerWait != WAIT_TIMEOUT || gWorkerRelease)
fail("worker acquired doubly-owned mutex");
if (!ReleaseMutex(gMutex))
fail("first recursive release failed");
run_worker(25u);
if (gWorkerWait == WAIT_FAILED)
fail_worker_wait("singly-owned finite wait failed");
if (gWorkerWait != WAIT_TIMEOUT || gWorkerRelease)
fail("worker acquired singly-owned mutex");
if (!ReleaseMutex(gMutex))
fail("final recursive release failed");
run_worker(1000u);
if (gWorkerWait == WAIT_FAILED)
fail_worker_wait("released finite wait failed");
if (gWorkerWait != WAIT_OBJECT_0 || !gWorkerRelease)
fail("worker did not acquire released mutex");
if (!CloseHandle(gMutex))
fail("mutex CloseHandle failed");
gMutex = CreateMutexA(NULL, TRUE, NULL);
if (!gMutex || gMutex == INVALID_HANDLE_VALUE)
fail("initial-owner mutex failed");
if (WaitForSingleObject(gMutex, INFINITE) != WAIT_OBJECT_0)
fail("initial-owner recursion failed");
if (!ReleaseMutex(gMutex) || !ReleaseMutex(gMutex))
fail("initial-owner release failed");
if (!CloseHandle(gMutex))
fail("initial-owner close failed");
print("win32_mutex_smoke: PASS\n");
while (true) (void)SysExitProcess(0);
}