Tests for handleapi, synchapi, processes

This commit is contained in:
Luke Street 2025-10-06 10:14:03 -06:00
parent 166b9036fd
commit f52ca2803f
11 changed files with 452 additions and 38 deletions

View File

@ -210,6 +210,39 @@ if(BUILD_TESTING)
${CMAKE_CURRENT_SOURCE_DIR}/test/test_threading.c
${CMAKE_CURRENT_SOURCE_DIR}/test/test_assert.h)
add_custom_command(
OUTPUT ${WIBO_TEST_BIN_DIR}/test_handleapi.exe
COMMAND ${WIBO_MINGW_CC} -Wall -Wextra -O2
-I${CMAKE_CURRENT_SOURCE_DIR}/test
-o test_handleapi.exe
${CMAKE_CURRENT_SOURCE_DIR}/test/test_handleapi.c
WORKING_DIRECTORY ${WIBO_TEST_BIN_DIR}
DEPENDS
${CMAKE_CURRENT_SOURCE_DIR}/test/test_handleapi.c
${CMAKE_CURRENT_SOURCE_DIR}/test/test_assert.h)
add_custom_command(
OUTPUT ${WIBO_TEST_BIN_DIR}/test_synchapi.exe
COMMAND ${WIBO_MINGW_CC} -Wall -Wextra -O2
-I${CMAKE_CURRENT_SOURCE_DIR}/test
-o test_synchapi.exe
${CMAKE_CURRENT_SOURCE_DIR}/test/test_synchapi.c
WORKING_DIRECTORY ${WIBO_TEST_BIN_DIR}
DEPENDS
${CMAKE_CURRENT_SOURCE_DIR}/test/test_synchapi.c
${CMAKE_CURRENT_SOURCE_DIR}/test/test_assert.h)
add_custom_command(
OUTPUT ${WIBO_TEST_BIN_DIR}/test_processes.exe
COMMAND ${WIBO_MINGW_CC} -Wall -Wextra -O2
-I${CMAKE_CURRENT_SOURCE_DIR}/test
-o test_processes.exe
${CMAKE_CURRENT_SOURCE_DIR}/test/test_processes.c
WORKING_DIRECTORY ${WIBO_TEST_BIN_DIR}
DEPENDS
${CMAKE_CURRENT_SOURCE_DIR}/test/test_processes.c
${CMAKE_CURRENT_SOURCE_DIR}/test/test_assert.h)
add_custom_command(
OUTPUT ${WIBO_TEST_BIN_DIR}/test_heap.exe
COMMAND ${WIBO_MINGW_CC} -Wall -Wextra -O2
@ -342,6 +375,9 @@ if(BUILD_TESTING)
${WIBO_TEST_BIN_DIR}/test_bcrypt.exe
${WIBO_TEST_BIN_DIR}/test_resources.exe
${WIBO_TEST_BIN_DIR}/test_threading.exe
${WIBO_TEST_BIN_DIR}/test_handleapi.exe
${WIBO_TEST_BIN_DIR}/test_synchapi.exe
${WIBO_TEST_BIN_DIR}/test_processes.exe
${WIBO_TEST_BIN_DIR}/test_heap.exe
${WIBO_TEST_BIN_DIR}/test_actctx.exe
${WIBO_TEST_BIN_DIR}/test_overlapped_io.exe
@ -388,6 +424,24 @@ if(BUILD_TESTING)
WORKING_DIRECTORY ${WIBO_TEST_BIN_DIR}
DEPENDS wibo.build_fixtures)
add_test(NAME wibo.test_handleapi
COMMAND $<TARGET_FILE:wibo> ${WIBO_TEST_BIN_DIR}/test_handleapi.exe)
set_tests_properties(wibo.test_handleapi PROPERTIES
WORKING_DIRECTORY ${WIBO_TEST_BIN_DIR}
DEPENDS wibo.build_fixtures)
add_test(NAME wibo.test_synchapi
COMMAND $<TARGET_FILE:wibo> ${WIBO_TEST_BIN_DIR}/test_synchapi.exe)
set_tests_properties(wibo.test_synchapi PROPERTIES
WORKING_DIRECTORY ${WIBO_TEST_BIN_DIR}
DEPENDS wibo.build_fixtures)
add_test(NAME wibo.test_processes
COMMAND $<TARGET_FILE:wibo> ${WIBO_TEST_BIN_DIR}/test_processes.exe)
set_tests_properties(wibo.test_processes PROPERTIES
WORKING_DIRECTORY ${WIBO_TEST_BIN_DIR}
DEPENDS wibo.build_fixtures)
add_test(NAME wibo.test_heap
COMMAND $<TARGET_FILE:wibo> ${WIBO_TEST_BIN_DIR}/test_heap.exe)
set_tests_properties(wibo.test_heap PROPERTIES

View File

@ -189,7 +189,7 @@ HANDLE WIN_FUNC CreateSemaphoreA(LPSECURITY_ATTRIBUTES lpSemaphoreAttributes, LO
BOOL WIN_FUNC ReleaseSemaphore(HANDLE hSemaphore, LONG lReleaseCount, PLONG lpPreviousCount) {
HOST_CONTEXT_GUARD();
DEBUG_LOG("ReleaseSemaphore(%p, %ld, %p)\n", hSemaphore, lReleaseCount, lpPreviousCount);
if (lReleaseCount <= 0) {
if (lReleaseCount < 0) {
wibo::lastError = ERROR_INVALID_PARAMETER;
return FALSE;
}
@ -330,9 +330,8 @@ DWORD WIN_FUNC WaitForSingleObject(HANDLE hHandle, DWORD dwMilliseconds) {
pthread_t self = pthread_self();
std::unique_lock lk(th->m);
if (pthread_equal(th->thread, self)) {
// Cannot wait on self
wibo::lastError = ERROR_INVALID_HANDLE;
return WAIT_FAILED;
// Windows actually allows you to wait on your own thread, but why bother?
return WAIT_TIMEOUT;
}
bool ok = doWait(lk, th->cv, [&] { return th->signaled.load(std::memory_order_acquire); });
return ok ? WAIT_OBJECT_0 : WAIT_TIMEOUT;
@ -341,9 +340,8 @@ DWORD WIN_FUNC WaitForSingleObject(HANDLE hHandle, DWORD dwMilliseconds) {
auto po = std::move(obj).downcast<ProcessObject>();
std::unique_lock lk(po->m);
if (po->pidfd == -1) {
// Cannot wait on self
wibo::lastError = ERROR_INVALID_HANDLE;
return WAIT_FAILED;
// Windows actually allows you to wait on your own process, but why bother?
return WAIT_TIMEOUT;
}
bool ok = doWait(lk, po->cv, [&] { return po->signaled.load(std::memory_order_acquire); });
return ok ? WAIT_OBJECT_0 : WAIT_TIMEOUT;

View File

@ -186,6 +186,13 @@ bool Handles::duplicateTo(HANDLE src, Handles &dst, HANDLE &out, uint32_t desire
uint32_t effAccess = (options & DUPLICATE_SAME_ACCESS) ? meta.grantedAccess : (desiredAccess & meta.grantedAccess);
const uint32_t flags = (inherit ? HANDLE_FLAG_INHERIT : 0);
// Reuse the same handle if duplicating within the same table and no changes
if (&dst == this && closeSource && effAccess == meta.grantedAccess && flags == meta.flags) {
out = src;
return true;
}
out = dst.alloc(std::move(obj), effAccess, flags);
if (closeSource) {

View File

@ -51,22 +51,21 @@ static void check_invalid_parameters(void) {
data.cbSize = sizeof(data);
GUID fakeGuid = {0};
SetLastError(0);
BOOL ok = FindActCtxSectionStringW(0, &fakeGuid, ACTIVATION_CONTEXT_SECTION_DLL_REDIRECTION, L"msvcr80.dll", &data);
TEST_CHECK(!ok);
TEST_CHECK(
!FindActCtxSectionStringW(0, &fakeGuid, ACTIVATION_CONTEXT_SECTION_DLL_REDIRECTION, L"msvcr80.dll", &data));
TEST_CHECK_EQ(ERROR_INVALID_PARAMETER, GetLastError());
ACTCTX_SECTION_KEYED_DATA sized = {0};
sized.cbSize = sizeof(data) - 4;
SetLastError(0);
ok = FindActCtxSectionStringW(0, NULL, ACTIVATION_CONTEXT_SECTION_DLL_REDIRECTION, L"msvcr80.dll", &sized);
TEST_CHECK(!ok);
TEST_CHECK(!FindActCtxSectionStringW(0, NULL, ACTIVATION_CONTEXT_SECTION_DLL_REDIRECTION, L"msvcr80.dll", &sized));
TEST_CHECK_EQ(ERROR_INSUFFICIENT_BUFFER, GetLastError());
ACTCTX_SECTION_KEYED_DATA flags = {0};
flags.cbSize = sizeof(flags);
SetLastError(0);
ok = FindActCtxSectionStringW(0x2, NULL, ACTIVATION_CONTEXT_SECTION_DLL_REDIRECTION, L"msvcr80.dll", &flags);
TEST_CHECK(!ok);
TEST_CHECK(
!FindActCtxSectionStringW(0x2, NULL, ACTIVATION_CONTEXT_SECTION_DLL_REDIRECTION, L"msvcr80.dll", &flags));
TEST_CHECK_EQ(ERROR_INVALID_PARAMETER, GetLastError());
}
@ -74,17 +73,15 @@ static void check_missing_entries(void) {
ACTCTX_SECTION_KEYED_DATA data = {0};
data.cbSize = sizeof(data);
SetLastError(0);
BOOL ok =
FindActCtxSectionStringW(0, NULL, ACTIVATION_CONTEXT_SECTION_DLL_REDIRECTION, L"totally_missing.dll", &data);
TEST_CHECK(!ok);
TEST_CHECK(
!FindActCtxSectionStringW(0, NULL, ACTIVATION_CONTEXT_SECTION_DLL_REDIRECTION, L"totally_missing.dll", &data));
TEST_CHECK_EQ(ERROR_SXS_KEY_NOT_FOUND, GetLastError());
ACTCTX_SECTION_KEYED_DATA wrongSection = {0};
wrongSection.cbSize = sizeof(wrongSection);
SetLastError(0);
ok = FindActCtxSectionStringW(0, NULL, ACTIVATION_CONTEXT_SECTION_ASSEMBLY_INFORMATION, L"msvcr80.dll",
&wrongSection);
TEST_CHECK(!ok);
TEST_CHECK(!FindActCtxSectionStringW(0, NULL, ACTIVATION_CONTEXT_SECTION_ASSEMBLY_INFORMATION, L"msvcr80.dll",
&wrongSection));
TEST_CHECK_EQ(ERROR_SXS_KEY_NOT_FOUND, GetLastError());
}

101
test/test_handleapi.c Normal file
View File

@ -0,0 +1,101 @@
#include "test_assert.h"
#include <stdint.h>
#include <stdlib.h>
#include <windows.h>
static void test_duplicate_handle_basic(void) {
HANDLE evt = CreateEventA(NULL, TRUE, FALSE, NULL);
TEST_CHECK(evt != NULL);
HANDLE dup = NULL;
BOOL ok = DuplicateHandle(GetCurrentProcess(), evt, GetCurrentProcess(), &dup, 0, FALSE, DUPLICATE_SAME_ACCESS);
TEST_CHECK(ok);
TEST_CHECK(dup != NULL);
TEST_CHECK(dup != evt);
TEST_CHECK(SetEvent(evt));
DWORD waitResult = WaitForSingleObject(dup, 0);
TEST_CHECK_EQ(WAIT_OBJECT_0, waitResult);
TEST_CHECK(ResetEvent(evt));
TEST_CHECK(CloseHandle(dup));
TEST_CHECK(CloseHandle(evt));
}
static void test_duplicate_handle_close_source(void) {
HANDLE evt = CreateEventA(NULL, FALSE, FALSE, NULL);
TEST_CHECK(evt != NULL);
HANDLE dup = NULL;
BOOL ok = DuplicateHandle(GetCurrentProcess(), evt, GetCurrentProcess(), &dup, 0, FALSE,
DUPLICATE_SAME_ACCESS | DUPLICATE_CLOSE_SOURCE);
TEST_CHECK(ok);
TEST_CHECK(dup != NULL);
// Since we're duplicating within the same process with DUPLICATE_CLOSE_SOURCE,
// we should get back the same handle value
TEST_CHECK(dup == evt);
TEST_CHECK(SetEvent(dup));
TEST_CHECK_EQ(WAIT_OBJECT_0, WaitForSingleObject(dup, 0));
TEST_CHECK(CloseHandle(dup));
}
static void test_duplicate_handle_invalid_source(void) {
HANDLE bogus = (HANDLE)(uintptr_t)0x1234;
HANDLE out = NULL;
SetLastError(0);
TEST_CHECK(
!DuplicateHandle(GetCurrentProcess(), bogus, GetCurrentProcess(), &out, 0, FALSE, DUPLICATE_SAME_ACCESS));
TEST_CHECK_EQ(ERROR_INVALID_HANDLE, GetLastError());
TEST_CHECK(out == NULL);
}
static void test_duplicate_handle_invalid_target_process(void) {
HANDLE evt = CreateEventA(NULL, TRUE, FALSE, NULL);
TEST_CHECK(evt != NULL);
HANDLE dup = NULL;
SetLastError(0xDEADBEEF);
TEST_CHECK(!DuplicateHandle(GetCurrentProcess(), evt, NULL, &dup, 0, FALSE, DUPLICATE_SAME_ACCESS));
TEST_CHECK_EQ(ERROR_INVALID_HANDLE, GetLastError());
TEST_CHECK(dup == NULL);
TEST_CHECK(CloseHandle(evt));
}
static void test_duplicate_pseudo_process_handle(void) {
HANDLE pseudo = GetCurrentProcess();
HANDLE procHandle = NULL;
BOOL ok = DuplicateHandle(pseudo, pseudo, pseudo, &procHandle, 0, FALSE, DUPLICATE_SAME_ACCESS);
TEST_CHECK(ok);
TEST_CHECK(procHandle != NULL);
TEST_CHECK(procHandle != pseudo);
TEST_CHECK_EQ(WAIT_TIMEOUT, WaitForSingleObject(procHandle, 0));
TEST_CHECK(CloseHandle(procHandle));
}
static void test_duplicate_handle_after_close(void) {
HANDLE evt = CreateEventA(NULL, TRUE, FALSE, NULL);
TEST_CHECK(evt != NULL);
TEST_CHECK(CloseHandle(evt));
HANDLE dup = NULL;
SetLastError(0);
TEST_CHECK(!DuplicateHandle(GetCurrentProcess(), evt, GetCurrentProcess(), &dup, 0, FALSE, DUPLICATE_SAME_ACCESS));
TEST_CHECK_EQ(ERROR_INVALID_HANDLE, GetLastError());
TEST_CHECK(dup == NULL);
}
int main(void) {
test_duplicate_handle_basic();
test_duplicate_handle_close_source();
test_duplicate_handle_invalid_source();
test_duplicate_handle_invalid_target_process();
test_duplicate_pseudo_process_handle();
test_duplicate_handle_after_close();
return EXIT_SUCCESS;
}

View File

@ -63,7 +63,7 @@ static void test_overlapped_read_with_event(void) {
TEST_CHECK_EQ(ERROR_IO_PENDING, GetLastError());
}
TEST_CHECK(WaitForSingleObject(ov.hEvent, INFINITE) == WAIT_OBJECT_0);
TEST_CHECK(WaitForSingleObject(ov.hEvent, 1000) == WAIT_OBJECT_0);
DWORD transferred = 0;
TEST_CHECK(GetOverlappedResult(file, &ov, &transferred, FALSE));
@ -91,7 +91,7 @@ static void test_overlapped_eof(void) {
TEST_CHECK_EQ(ERROR_IO_PENDING, GetLastError());
}
TEST_CHECK(WaitForSingleObject(ov.hEvent, INFINITE) == WAIT_OBJECT_0);
TEST_CHECK(WaitForSingleObject(ov.hEvent, 1000) == WAIT_OBJECT_0);
DWORD transferred = 1234;
TEST_CHECK(!GetOverlappedResult(file, &ov, &transferred, FALSE));
@ -127,7 +127,7 @@ static void test_overlapped_write(void) {
if (!issued) {
TEST_CHECK_EQ(ERROR_IO_PENDING, GetLastError());
}
TEST_CHECK(WaitForSingleObject(ov.hEvent, INFINITE) == WAIT_OBJECT_0);
TEST_CHECK(WaitForSingleObject(ov.hEvent, 1000) == WAIT_OBJECT_0);
DWORD transferred = 0;
TEST_CHECK(GetOverlappedResult(file, &ov, &transferred, FALSE));

View File

@ -75,8 +75,7 @@ int main(void) {
bytesRead = 123;
SetLastError(ERROR_GEN_FAILURE);
BOOL ok = ReadFile(readPipe, buffer, sizeof(buffer), &bytesRead, NULL);
TEST_CHECK(!ok);
TEST_CHECK(!ReadFile(readPipe, buffer, sizeof(buffer), &bytesRead, NULL));
TEST_CHECK_EQ(0u, (unsigned int)bytesRead);
TEST_CHECK_EQ(ERROR_BROKEN_PIPE, GetLastError());
TEST_CHECK(ResetEvent(event));

116
test/test_processes.c Normal file
View File

@ -0,0 +1,116 @@
#include "test_assert.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <windows.h>
static DWORD parse_exit_code(const char *value) {
TEST_CHECK(value != NULL);
DWORD result = 0;
for (const char *p = value; *p; ++p) {
TEST_CHECK(*p >= '0' && *p <= '9');
result = result * 10u + (DWORD)(*p - '0');
}
return result;
}
static int child_main(int argc, char **argv) {
TEST_CHECK(argc >= 2);
(void)argv;
char exitBuffer[16];
DWORD exitLen = GetEnvironmentVariableA("WIBO_TEST_PROC_EXIT", exitBuffer, sizeof(exitBuffer));
TEST_CHECK(exitLen > 0 && exitLen < sizeof(exitBuffer));
DWORD desiredExit = parse_exit_code(exitBuffer);
Sleep(200);
return (int)desiredExit;
}
static void test_createprocess_failure(void) {
STARTUPINFOA si;
PROCESS_INFORMATION pi;
ZeroMemory(&si, sizeof(si));
si.cb = sizeof(si);
ZeroMemory(&pi, sizeof(pi));
SetLastError(0);
char bogusCommandLine[] = "child";
TEST_CHECK(
!CreateProcessA("Z:/definitely/missing.exe", bogusCommandLine, NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi));
DWORD error = GetLastError();
TEST_CHECK_MSG(error == ERROR_FILE_NOT_FOUND || error == ERROR_PATH_NOT_FOUND, "CreateProcessA missing file -> %lu",
(unsigned long)error);
}
static int parent_main(void) {
test_createprocess_failure();
char modulePath[MAX_PATH];
DWORD pathLen = GetModuleFileNameA(NULL, modulePath, (DWORD)sizeof(modulePath));
TEST_CHECK(pathLen > 0 && pathLen < sizeof(modulePath));
const DWORD childExitCode = 0x24u;
char commandLine[256];
snprintf(commandLine, sizeof(commandLine), "child placeholder %lu", (unsigned long)childExitCode);
char exitEnv[16];
snprintf(exitEnv, sizeof(exitEnv), "%lu", (unsigned long)childExitCode);
TEST_CHECK(SetEnvironmentVariableA("WIBO_TEST_PROC_EXIT", exitEnv));
TEST_CHECK(SetEnvironmentVariableA("WIBO_TEST_PROC_ROLE", "child"));
STARTUPINFOA si;
PROCESS_INFORMATION pi;
ZeroMemory(&si, sizeof(si));
si.cb = sizeof(si);
GetStartupInfoA(&si);
ZeroMemory(&pi, sizeof(pi));
TEST_CHECK(CreateProcessA(modulePath, commandLine, NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi));
TEST_CHECK(pi.hProcess != NULL);
SetEnvironmentVariableA("WIBO_TEST_PROC_EXIT", NULL);
SetEnvironmentVariableA("WIBO_TEST_PROC_ROLE", NULL);
HANDLE processHandle = NULL;
TEST_CHECK(DuplicateHandle(GetCurrentProcess(), pi.hProcess, GetCurrentProcess(), &processHandle, 0, FALSE,
DUPLICATE_SAME_ACCESS));
TEST_CHECK(processHandle != NULL);
TEST_CHECK(processHandle != pi.hProcess);
TEST_CHECK(CloseHandle(pi.hProcess));
TEST_CHECK_EQ(WAIT_FAILED, WaitForSingleObject(pi.hProcess, 0));
pi.hProcess = NULL;
Sleep(50);
TEST_CHECK_EQ(WAIT_TIMEOUT, WaitForSingleObject(processHandle, 0));
DWORD exitCode = 0;
TEST_CHECK(GetExitCodeProcess(processHandle, &exitCode));
TEST_CHECK_EQ(STILL_ACTIVE, exitCode);
TEST_CHECK_EQ(WAIT_TIMEOUT, WaitForSingleObject(processHandle, 10));
TEST_CHECK_EQ(WAIT_OBJECT_0, WaitForSingleObject(processHandle, 5000));
TEST_CHECK(GetExitCodeProcess(processHandle, &exitCode));
TEST_CHECK_EQ(childExitCode, exitCode);
TEST_CHECK(CloseHandle(processHandle));
if (pi.hThread) {
TEST_CHECK(CloseHandle(pi.hThread));
}
return EXIT_SUCCESS;
}
int main(int argc, char **argv) {
char role[16];
DWORD roleLen = GetEnvironmentVariableA("WIBO_TEST_PROC_ROLE", role, sizeof(role));
if (roleLen > 0 && roleLen < sizeof(role) && strcmp(role, "child") == 0) {
return child_main(argc, argv);
}
if (argc > 1 && strcmp(argv[1], "child") == 0) {
return child_main(argc, argv);
}
return parent_main();
}

142
test/test_synchapi.c Normal file
View File

@ -0,0 +1,142 @@
#include "test_assert.h"
#include <stdlib.h>
#include <windows.h>
typedef struct {
HANDLE mutex;
HANDLE acquiredEvent;
HANDLE releaseEvent;
HANDLE doneEvent;
} MutexWorkerContext;
static DWORD WINAPI mutex_worker(LPVOID param) {
MutexWorkerContext *ctx = (MutexWorkerContext *)param;
DWORD wait = WaitForSingleObject(ctx->mutex, 1000);
TEST_CHECK_EQ(WAIT_OBJECT_0, wait);
TEST_CHECK(SetEvent(ctx->acquiredEvent));
wait = WaitForSingleObject(ctx->releaseEvent, 1000);
TEST_CHECK_EQ(WAIT_OBJECT_0, wait);
TEST_CHECK(ReleaseMutex(ctx->mutex));
TEST_CHECK(SetEvent(ctx->doneEvent));
return 0;
}
typedef struct {
HANDLE semaphore;
HANDLE ackEvent;
HANDLE doneEvent;
int iterations;
} SemaphoreWorkerContext;
static DWORD WINAPI semaphore_worker(LPVOID param) {
SemaphoreWorkerContext *ctx = (SemaphoreWorkerContext *)param;
for (int i = 0; i < ctx->iterations; ++i) {
DWORD wait = WaitForSingleObject(ctx->semaphore, 1000);
TEST_CHECK_EQ(WAIT_OBJECT_0, wait);
TEST_CHECK(SetEvent(ctx->ackEvent));
}
TEST_CHECK(SetEvent(ctx->doneEvent));
return 0;
}
static void test_mutex_contention(void) {
HANDLE mutex = CreateMutexA(NULL, FALSE, NULL);
TEST_CHECK(mutex != NULL);
HANDLE acquired = CreateEventA(NULL, FALSE, FALSE, NULL);
HANDLE releaseSignal = CreateEventA(NULL, FALSE, FALSE, NULL);
HANDLE done = CreateEventA(NULL, FALSE, FALSE, NULL);
TEST_CHECK(acquired != NULL && releaseSignal != NULL && done != NULL);
MutexWorkerContext ctx = {
.mutex = mutex,
.acquiredEvent = acquired,
.releaseEvent = releaseSignal,
.doneEvent = done,
};
HANDLE thread = CreateThread(NULL, 0, mutex_worker, &ctx, 0, NULL);
TEST_CHECK(thread != NULL);
DWORD wait = WaitForSingleObject(acquired, 1000);
TEST_CHECK_EQ(WAIT_OBJECT_0, wait);
wait = WaitForSingleObject(mutex, 10);
TEST_CHECK_EQ(WAIT_TIMEOUT, wait);
TEST_CHECK(SetEvent(releaseSignal));
wait = WaitForSingleObject(done, 1000);
TEST_CHECK_EQ(WAIT_OBJECT_0, wait);
wait = WaitForSingleObject(mutex, 1000);
TEST_CHECK_EQ(WAIT_OBJECT_0, wait);
wait = WaitForSingleObject(mutex, 0);
TEST_CHECK_EQ(WAIT_OBJECT_0, wait);
TEST_CHECK(ReleaseMutex(mutex));
TEST_CHECK(ReleaseMutex(mutex));
TEST_CHECK(CloseHandle(thread));
TEST_CHECK(CloseHandle(acquired));
TEST_CHECK(CloseHandle(releaseSignal));
TEST_CHECK(CloseHandle(done));
TEST_CHECK(CloseHandle(mutex));
}
static void test_semaphore_waits(void) {
HANDLE semaphore = CreateSemaphoreA(NULL, 0, 3, NULL);
TEST_CHECK(semaphore != NULL);
DWORD wait = WaitForSingleObject(semaphore, 10);
TEST_CHECK_EQ(WAIT_TIMEOUT, wait);
HANDLE ack = CreateEventA(NULL, FALSE, FALSE, NULL);
HANDLE done = CreateEventA(NULL, FALSE, FALSE, NULL);
TEST_CHECK(ack != NULL && done != NULL);
SemaphoreWorkerContext ctx = {
.semaphore = semaphore,
.ackEvent = ack,
.doneEvent = done,
.iterations = 3,
};
HANDLE thread = CreateThread(NULL, 0, semaphore_worker, &ctx, 0, NULL);
TEST_CHECK(thread != NULL);
for (int i = 0; i < ctx.iterations; ++i) {
LONG previous = -1;
BOOL ok = ReleaseSemaphore(semaphore, 1, &previous);
TEST_CHECK(ok);
TEST_CHECK_EQ(0, previous);
DWORD ackWait = WaitForSingleObject(ack, 1000);
TEST_CHECK_EQ(WAIT_OBJECT_0, ackWait);
}
DWORD doneWait = WaitForSingleObject(done, 1000);
TEST_CHECK_EQ(WAIT_OBJECT_0, doneWait);
// lReleaseCount = 0 permitted; no effect
TEST_CHECK(ReleaseSemaphore(semaphore, 0, NULL));
HANDLE limited = CreateSemaphoreA(NULL, 1, 1, NULL);
TEST_CHECK(limited != NULL);
SetLastError(0);
TEST_CHECK(!ReleaseSemaphore(limited, 1, NULL));
TEST_CHECK_EQ(ERROR_TOO_MANY_POSTS, GetLastError());
TEST_CHECK(CloseHandle(thread));
TEST_CHECK(CloseHandle(ack));
TEST_CHECK(CloseHandle(done));
TEST_CHECK(CloseHandle(semaphore));
TEST_CHECK(CloseHandle(limited));
}
int main(void) {
test_mutex_contention();
test_semaphore_waits();
return EXIT_SUCCESS;
}

View File

@ -52,9 +52,9 @@ int main(void) {
HANDLE thread = CreateThread(NULL, 0, workerProc, NULL, 0, NULL);
TEST_CHECK_MSG(thread != NULL, "CreateThread failed: %lu", (unsigned long)GetLastError());
DWORD wait_result = WaitForSingleObject(thread, INFINITE);
DWORD wait_result = WaitForSingleObject(thread, 1000);
TEST_CHECK_EQ(WAIT_OBJECT_0, wait_result);
TEST_CHECK_MSG(CloseHandle(thread) != 0, "CloseHandle(thread) failed: %lu", (unsigned long)GetLastError());
TEST_CHECK(CloseHandle(thread));
TEST_CHECK_EQ(1, getAttach());
TEST_CHECK_EQ(1, getDetach());
@ -68,9 +68,9 @@ int main(void) {
thread = CreateThread(NULL, 0, workerProc, NULL, 0, NULL);
TEST_CHECK_MSG(thread != NULL, "CreateThread after disable failed: %lu", (unsigned long)GetLastError());
wait_result = WaitForSingleObject(thread, INFINITE);
wait_result = WaitForSingleObject(thread, 1000);
TEST_CHECK_EQ(WAIT_OBJECT_0, wait_result);
TEST_CHECK_MSG(CloseHandle(thread) != 0, "CloseHandle(second thread) failed: %lu", (unsigned long)GetLastError());
TEST_CHECK(CloseHandle(thread));
if (!isRunningUnderWine()) {
TEST_CHECK_EQ(0, getAttach());
@ -80,7 +80,7 @@ int main(void) {
LONG final_attach = getAttach();
LONG final_detach = getDetach();
TEST_CHECK_MSG(FreeLibrary(mod) != 0, "FreeLibrary failed: %lu", (unsigned long)GetLastError());
TEST_CHECK(FreeLibrary(mod));
printf("thread_notifications: attach=%ld detach=%ld\n", (long)final_attach, (long)final_detach);
return EXIT_SUCCESS;

View File

@ -11,7 +11,7 @@ typedef struct {
static DWORD WINAPI worker_main(LPVOID param) {
WorkerContext *ctx = (WorkerContext *)param;
TEST_CHECK(SetEvent(ctx->readyEvent));
DWORD waitResult = WaitForSingleObject(ctx->goEvent, INFINITE);
DWORD waitResult = WaitForSingleObject(ctx->goEvent, 1000);
TEST_CHECK_EQ(WAIT_OBJECT_0, waitResult);
return ctx->exitCode;
}
@ -37,14 +37,14 @@ int main(void) {
HANDLE thread = CreateThread(NULL, 0, worker_main, &ctx, 0, NULL);
TEST_CHECK(thread != NULL);
DWORD waitResult = WaitForSingleObject(readyEvent, INFINITE);
DWORD waitResult = WaitForSingleObject(readyEvent, 1000);
TEST_CHECK_EQ(WAIT_OBJECT_0, waitResult);
TEST_CHECK(ResetEvent(readyEvent));
TEST_CHECK(SetEvent(goEvent));
waitResult = WaitForSingleObject(thread, INFINITE);
waitResult = WaitForSingleObject(thread, 1000);
TEST_CHECK_EQ(WAIT_OBJECT_0, waitResult);
DWORD exitCode = 0;
@ -56,21 +56,21 @@ int main(void) {
HANDLE autoEvent = CreateEventA(NULL, FALSE, FALSE, NULL);
TEST_CHECK(autoEvent != NULL);
TEST_CHECK(SetEvent(autoEvent));
waitResult = WaitForSingleObject(autoEvent, INFINITE);
waitResult = WaitForSingleObject(autoEvent, 0);
TEST_CHECK_EQ(WAIT_OBJECT_0, waitResult);
TEST_CHECK(SetEvent(autoEvent));
waitResult = WaitForSingleObject(autoEvent, INFINITE);
waitResult = WaitForSingleObject(autoEvent, 0);
TEST_CHECK_EQ(WAIT_OBJECT_0, waitResult);
TEST_CHECK(CloseHandle(autoEvent));
HANDLE manualEvent = CreateEventA(NULL, TRUE, FALSE, NULL);
TEST_CHECK(manualEvent != NULL);
TEST_CHECK(SetEvent(manualEvent));
waitResult = WaitForSingleObject(manualEvent, INFINITE);
waitResult = WaitForSingleObject(manualEvent, 0);
TEST_CHECK_EQ(WAIT_OBJECT_0, waitResult);
TEST_CHECK(ResetEvent(manualEvent));
TEST_CHECK(SetEvent(manualEvent));
waitResult = WaitForSingleObject(manualEvent, INFINITE);
waitResult = WaitForSingleObject(manualEvent, 0);
TEST_CHECK_EQ(WAIT_OBJECT_0, waitResult);
TEST_CHECK(CloseHandle(manualEvent));
@ -80,7 +80,7 @@ int main(void) {
HANDLE mutex = CreateMutexA(NULL, FALSE, NULL);
TEST_CHECK(mutex != NULL);
waitResult = WaitForSingleObject(mutex, INFINITE);
waitResult = WaitForSingleObject(mutex, 0);
TEST_CHECK_EQ(WAIT_OBJECT_0, waitResult);
TEST_CHECK(ReleaseMutex(mutex));
TEST_CHECK(CloseHandle(mutex));
@ -88,7 +88,7 @@ int main(void) {
DWORD secondExitCode = 0x55AA;
HANDLE exitThread = CreateThread(NULL, 0, exit_thread_worker, &secondExitCode, 0, NULL);
TEST_CHECK(exitThread != NULL);
waitResult = WaitForSingleObject(exitThread, INFINITE);
waitResult = WaitForSingleObject(exitThread, 1000);
TEST_CHECK_EQ(WAIT_OBJECT_0, waitResult);
exitCode = 0;
TEST_CHECK(GetExitCodeThread(exitThread, &exitCode));