148 lines
5.1 KiB
C
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);
|
|
}
|