Milestone 5: deliver embedded RDP sessions and lifecycle hardening
This commit is contained in:
40
third_party/FreeRDP/winpr/libwinpr/synch/test/CMakeLists.txt
vendored
Normal file
40
third_party/FreeRDP/winpr/libwinpr/synch/test/CMakeLists.txt
vendored
Normal file
@@ -0,0 +1,40 @@
|
||||
set(MODULE_NAME "TestSynch")
|
||||
set(MODULE_PREFIX "TEST_SYNCH")
|
||||
|
||||
disable_warnings_for_directory(${CMAKE_CURRENT_BINARY_DIR})
|
||||
|
||||
set(${MODULE_PREFIX}_DRIVER ${MODULE_NAME}.c)
|
||||
|
||||
set(${MODULE_PREFIX}_TESTS
|
||||
TestSynchInit.c
|
||||
TestSynchEvent.c
|
||||
TestSynchMutex.c
|
||||
TestSynchBarrier.c
|
||||
TestSynchCritical.c
|
||||
TestSynchSemaphore.c
|
||||
TestSynchThread.c
|
||||
# TestSynchMultipleThreads.c
|
||||
TestSynchTimerQueue.c
|
||||
TestSynchWaitableTimer.c
|
||||
TestSynchWaitableTimerAPC.c
|
||||
TestSynchAPC.c
|
||||
)
|
||||
|
||||
create_test_sourcelist(${MODULE_PREFIX}_SRCS ${${MODULE_PREFIX}_DRIVER} ${${MODULE_PREFIX}_TESTS})
|
||||
|
||||
if(FREEBSD)
|
||||
include_directories(SYSTEM ${EPOLLSHIM_INCLUDE_DIR})
|
||||
endif()
|
||||
|
||||
add_executable(${MODULE_NAME} ${${MODULE_PREFIX}_SRCS})
|
||||
|
||||
target_link_libraries(${MODULE_NAME} winpr)
|
||||
|
||||
set_target_properties(${MODULE_NAME} PROPERTIES RUNTIME_OUTPUT_DIRECTORY "${TESTING_OUTPUT_DIRECTORY}")
|
||||
|
||||
foreach(test ${${MODULE_PREFIX}_TESTS})
|
||||
get_filename_component(TestName ${test} NAME_WE)
|
||||
add_test(${TestName} ${TESTING_OUTPUT_DIRECTORY}/${MODULE_NAME} ${TestName})
|
||||
endforeach()
|
||||
|
||||
set_property(TARGET ${MODULE_NAME} PROPERTY FOLDER "WinPR/Test")
|
||||
151
third_party/FreeRDP/winpr/libwinpr/synch/test/TestSynchAPC.c
vendored
Normal file
151
third_party/FreeRDP/winpr/libwinpr/synch/test/TestSynchAPC.c
vendored
Normal file
@@ -0,0 +1,151 @@
|
||||
/**
|
||||
* FreeRDP: A Remote Desktop Protocol Implementation
|
||||
* TestSyncAPC
|
||||
*
|
||||
* Copyright 2021 David Fort <contact@hardening-consulting.com>
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
#include <winpr/wtypes.h>
|
||||
#include <winpr/thread.h>
|
||||
#include <winpr/synch.h>
|
||||
|
||||
typedef struct
|
||||
{
|
||||
BOOL error;
|
||||
BOOL called;
|
||||
} UserApcArg;
|
||||
|
||||
static void CALLBACK userApc(ULONG_PTR arg)
|
||||
{
|
||||
UserApcArg* userArg = (UserApcArg*)arg;
|
||||
userArg->called = TRUE;
|
||||
}
|
||||
|
||||
static DWORD WINAPI uncleanThread(LPVOID lpThreadParameter)
|
||||
{
|
||||
/* this thread post an APC that will never get executed */
|
||||
UserApcArg* userArg = (UserApcArg*)lpThreadParameter;
|
||||
if (!QueueUserAPC((PAPCFUNC)userApc, _GetCurrentThread(), (ULONG_PTR)lpThreadParameter))
|
||||
{
|
||||
userArg->error = TRUE;
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static DWORD WINAPI cleanThread(LPVOID lpThreadParameter)
|
||||
{
|
||||
Sleep(500);
|
||||
|
||||
SleepEx(500, TRUE);
|
||||
return 0;
|
||||
}
|
||||
|
||||
typedef struct
|
||||
{
|
||||
HANDLE timer1;
|
||||
DWORD timer1Calls;
|
||||
HANDLE timer2;
|
||||
DWORD timer2Calls;
|
||||
BOOL endTest;
|
||||
} UncleanCloseData;
|
||||
|
||||
static VOID CALLBACK Timer1APCProc(LPVOID lpArg, DWORD dwTimerLowValue, DWORD dwTimerHighValue)
|
||||
{
|
||||
UncleanCloseData* data = (UncleanCloseData*)lpArg;
|
||||
data->timer1Calls++;
|
||||
(void)CloseHandle(data->timer2);
|
||||
data->endTest = TRUE;
|
||||
}
|
||||
|
||||
static VOID CALLBACK Timer2APCProc(LPVOID lpArg, DWORD dwTimerLowValue, DWORD dwTimerHighValue)
|
||||
{
|
||||
UncleanCloseData* data = (UncleanCloseData*)lpArg;
|
||||
data->timer2Calls++;
|
||||
}
|
||||
|
||||
static DWORD /*WINAPI*/ closeHandleTest(LPVOID lpThreadParameter)
|
||||
{
|
||||
LARGE_INTEGER dueTime = WINPR_C_ARRAY_INIT;
|
||||
UncleanCloseData* data = (UncleanCloseData*)lpThreadParameter;
|
||||
data->endTest = FALSE;
|
||||
|
||||
dueTime.QuadPart = -500;
|
||||
if (!SetWaitableTimer(data->timer1, &dueTime, 0, Timer1APCProc, lpThreadParameter, FALSE))
|
||||
return 1;
|
||||
|
||||
dueTime.QuadPart = -900;
|
||||
if (!SetWaitableTimer(data->timer2, &dueTime, 0, Timer2APCProc, lpThreadParameter, FALSE))
|
||||
return 1;
|
||||
|
||||
while (!data->endTest)
|
||||
{
|
||||
SleepEx(100, TRUE);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int TestSynchAPC(int argc, char* argv[])
|
||||
{
|
||||
HANDLE thread = nullptr;
|
||||
UserApcArg userApcArg;
|
||||
|
||||
userApcArg.error = FALSE;
|
||||
userApcArg.called = FALSE;
|
||||
|
||||
WINPR_UNUSED(argc);
|
||||
WINPR_UNUSED(argv);
|
||||
|
||||
/* first post an APC and check it is executed during a SleepEx */
|
||||
if (!QueueUserAPC((PAPCFUNC)userApc, _GetCurrentThread(), (ULONG_PTR)&userApcArg))
|
||||
return 1;
|
||||
|
||||
if (SleepEx(100, FALSE) != 0)
|
||||
return 2;
|
||||
|
||||
if (SleepEx(100, TRUE) != WAIT_IO_COMPLETION)
|
||||
return 3;
|
||||
|
||||
if (!userApcArg.called)
|
||||
return 4;
|
||||
|
||||
userApcArg.called = FALSE;
|
||||
|
||||
/* test that the APC is cleaned up even when not called */
|
||||
thread = CreateThread(nullptr, 0, uncleanThread, &userApcArg, 0, nullptr);
|
||||
if (!thread)
|
||||
return 10;
|
||||
(void)WaitForSingleObject(thread, INFINITE);
|
||||
(void)CloseHandle(thread);
|
||||
|
||||
if (userApcArg.called || userApcArg.error)
|
||||
return 11;
|
||||
|
||||
/* test a remote APC queuing */
|
||||
thread = CreateThread(nullptr, 0, cleanThread, &userApcArg, 0, nullptr);
|
||||
if (!thread)
|
||||
return 20;
|
||||
|
||||
if (!QueueUserAPC((PAPCFUNC)userApc, thread, (ULONG_PTR)&userApcArg))
|
||||
return 21;
|
||||
|
||||
(void)WaitForSingleObject(thread, INFINITE);
|
||||
(void)CloseHandle(thread);
|
||||
|
||||
if (!userApcArg.called)
|
||||
return 22;
|
||||
|
||||
return 0;
|
||||
}
|
||||
270
third_party/FreeRDP/winpr/libwinpr/synch/test/TestSynchBarrier.c
vendored
Normal file
270
third_party/FreeRDP/winpr/libwinpr/synch/test/TestSynchBarrier.c
vendored
Normal file
@@ -0,0 +1,270 @@
|
||||
|
||||
#include <winpr/crt.h>
|
||||
#include <winpr/crypto.h>
|
||||
#include <winpr/synch.h>
|
||||
#include <winpr/thread.h>
|
||||
#include <winpr/interlocked.h>
|
||||
#include <winpr/sysinfo.h>
|
||||
|
||||
#include "../synch.h"
|
||||
|
||||
static SYNCHRONIZATION_BARRIER gBarrier;
|
||||
static HANDLE gStartEvent = nullptr;
|
||||
static LONG gErrorCount = 0;
|
||||
|
||||
#define MAX_SLEEP_MS 22
|
||||
|
||||
struct test_params
|
||||
{
|
||||
LONG threadCount;
|
||||
LONG trueCount;
|
||||
LONG falseCount;
|
||||
DWORD loops;
|
||||
DWORD flags;
|
||||
};
|
||||
|
||||
static UINT32 prand(UINT32 max)
|
||||
{
|
||||
UINT32 tmp = 0;
|
||||
if (max <= 1)
|
||||
return 1;
|
||||
if (winpr_RAND(&tmp, sizeof(tmp)) < 0)
|
||||
{
|
||||
// NOLINTNEXTLINE(concurrency-mt-unsafe)
|
||||
exit(-1);
|
||||
}
|
||||
return tmp % (max - 1) + 1;
|
||||
}
|
||||
|
||||
static DWORD WINAPI test_synch_barrier_thread(LPVOID lpParam)
|
||||
{
|
||||
BOOL status = FALSE;
|
||||
struct test_params* p = (struct test_params*)lpParam;
|
||||
|
||||
InterlockedIncrement(&p->threadCount);
|
||||
|
||||
// printf("Thread #%03u entered.\n", tnum);
|
||||
|
||||
/* wait for start event from main */
|
||||
if (WaitForSingleObject(gStartEvent, INFINITE) != WAIT_OBJECT_0)
|
||||
{
|
||||
InterlockedIncrement(&gErrorCount);
|
||||
goto out;
|
||||
}
|
||||
|
||||
// printf("Thread #%03u unblocked.\n", tnum);
|
||||
|
||||
for (DWORD i = 0; i < p->loops && gErrorCount == 0; i++)
|
||||
{
|
||||
/* simulate different execution times before the barrier */
|
||||
Sleep(1 + prand(MAX_SLEEP_MS));
|
||||
status = EnterSynchronizationBarrier(&gBarrier, p->flags);
|
||||
|
||||
// printf("Thread #%03u status: %s\n", tnum, status ? "TRUE" : "FALSE");
|
||||
if (status)
|
||||
InterlockedIncrement(&p->trueCount);
|
||||
else
|
||||
InterlockedIncrement(&p->falseCount);
|
||||
}
|
||||
|
||||
out:
|
||||
// printf("Thread #%03u leaving.\n", tnum);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static BOOL TestSynchBarrierWithFlags(DWORD dwFlags, DWORD dwThreads, DWORD dwLoops)
|
||||
{
|
||||
BOOL rc = FALSE;
|
||||
DWORD dwStatus = 0;
|
||||
|
||||
struct test_params p = {
|
||||
.threadCount = 0, .trueCount = 0, .falseCount = 0, .loops = dwLoops, .flags = dwFlags
|
||||
};
|
||||
DWORD expectedTrueCount = dwLoops;
|
||||
DWORD expectedFalseCount = dwLoops * (dwThreads - 1);
|
||||
|
||||
printf("%s: >> Testing with flags 0x%08" PRIx32 ". Using %" PRIu32
|
||||
" threads performing %" PRIu32 " loops\n",
|
||||
__func__, dwFlags, dwThreads, dwLoops);
|
||||
|
||||
HANDLE* threads = (HANDLE*)calloc(dwThreads, sizeof(HANDLE));
|
||||
if (!threads)
|
||||
{
|
||||
printf("%s: error allocatin thread array memory\n", __func__);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if (dwThreads > INT32_MAX)
|
||||
goto fail;
|
||||
|
||||
if (!InitializeSynchronizationBarrier(&gBarrier, (LONG)dwThreads, -1))
|
||||
{
|
||||
printf("%s: InitializeSynchronizationBarrier failed. GetLastError() = 0x%08x", __func__,
|
||||
GetLastError());
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (!(gStartEvent = CreateEvent(nullptr, TRUE, FALSE, nullptr)))
|
||||
{
|
||||
printf("%s: CreateEvent failed with error 0x%08x", __func__, GetLastError());
|
||||
goto fail;
|
||||
}
|
||||
|
||||
DWORD i = 0;
|
||||
for (; i < dwThreads; i++)
|
||||
{
|
||||
threads[i] = CreateThread(nullptr, 0, test_synch_barrier_thread, &p, 0, nullptr);
|
||||
if (!threads[i])
|
||||
{
|
||||
printf("%s: CreateThread failed for thread #%" PRIu32 " with error 0x%08x\n", __func__,
|
||||
i, GetLastError());
|
||||
InterlockedIncrement(&gErrorCount);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (i > 0)
|
||||
{
|
||||
if (!SetEvent(gStartEvent))
|
||||
{
|
||||
printf("%s: SetEvent(gStartEvent) failed with error = 0x%08x)\n", __func__,
|
||||
GetLastError());
|
||||
InterlockedIncrement(&gErrorCount);
|
||||
}
|
||||
|
||||
while (i--)
|
||||
{
|
||||
if (WAIT_OBJECT_0 != (dwStatus = WaitForSingleObject(threads[i], INFINITE)))
|
||||
{
|
||||
printf("%s: WaitForSingleObject(thread[%" PRIu32 "] unexpectedly returned %" PRIu32
|
||||
" (error = 0x%08x)\n",
|
||||
__func__, i, dwStatus, GetLastError());
|
||||
InterlockedIncrement(&gErrorCount);
|
||||
}
|
||||
|
||||
if (!CloseHandle(threads[i]))
|
||||
{
|
||||
printf("%s: CloseHandle(thread[%" PRIu32 "]) failed with error = 0x%08x)\n",
|
||||
__func__, i, GetLastError());
|
||||
InterlockedIncrement(&gErrorCount);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!CloseHandle(gStartEvent))
|
||||
{
|
||||
printf("%s: CloseHandle(gStartEvent) failed with error = 0x%08x)\n", __func__,
|
||||
GetLastError());
|
||||
InterlockedIncrement(&gErrorCount);
|
||||
}
|
||||
|
||||
if (p.threadCount != (INT64)dwThreads)
|
||||
InterlockedIncrement(&gErrorCount);
|
||||
|
||||
if (p.trueCount != (INT64)expectedTrueCount)
|
||||
InterlockedIncrement(&gErrorCount);
|
||||
|
||||
if (p.falseCount != (INT64)expectedFalseCount)
|
||||
InterlockedIncrement(&gErrorCount);
|
||||
|
||||
printf("%s: error count: %" PRId32 "\n", __func__, gErrorCount);
|
||||
printf("%s: thread count: %" PRId32 " (expected %" PRIu32 ")\n", __func__, p.threadCount,
|
||||
dwThreads);
|
||||
printf("%s: true count: %" PRId32 " (expected %" PRIu32 ")\n", __func__, p.trueCount,
|
||||
expectedTrueCount);
|
||||
printf("%s: false count: %" PRId32 " (expected %" PRIu32 ")\n", __func__, p.falseCount,
|
||||
expectedFalseCount);
|
||||
|
||||
rc = TRUE;
|
||||
fail:
|
||||
free((void*)threads);
|
||||
DeleteSynchronizationBarrier(&gBarrier);
|
||||
if (gErrorCount > 0)
|
||||
{
|
||||
printf("%s: Error test failed with %" PRId32 " reported errors\n", __func__, gErrorCount);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
int TestSynchBarrier(int argc, char* argv[])
|
||||
{
|
||||
SYSTEM_INFO sysinfo;
|
||||
DWORD dwMaxThreads = 0;
|
||||
DWORD dwMinThreads = 0;
|
||||
DWORD dwNumLoops = 10;
|
||||
|
||||
WINPR_UNUSED(argc);
|
||||
WINPR_UNUSED(argv);
|
||||
|
||||
GetNativeSystemInfo(&sysinfo);
|
||||
printf("%s: Number of processors: %" PRIu32 "\n", __func__, sysinfo.dwNumberOfProcessors);
|
||||
dwMinThreads = sysinfo.dwNumberOfProcessors;
|
||||
dwMaxThreads = sysinfo.dwNumberOfProcessors * 4;
|
||||
|
||||
if (dwMaxThreads > 32)
|
||||
dwMaxThreads = 32;
|
||||
|
||||
/* Test invalid parameters */
|
||||
if (InitializeSynchronizationBarrier(&gBarrier, 0, -1))
|
||||
{
|
||||
(void)fprintf(
|
||||
stderr,
|
||||
"%s: InitializeSynchronizationBarrier unecpectedly succeeded with lTotalThreads = 0\n",
|
||||
__func__);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (InitializeSynchronizationBarrier(&gBarrier, -1, -1))
|
||||
{
|
||||
(void)fprintf(
|
||||
stderr,
|
||||
"%s: InitializeSynchronizationBarrier unecpectedly succeeded with lTotalThreads = -1\n",
|
||||
__func__);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (InitializeSynchronizationBarrier(&gBarrier, 1, -2))
|
||||
{
|
||||
(void)fprintf(
|
||||
stderr,
|
||||
"%s: InitializeSynchronizationBarrier unecpectedly succeeded with lSpinCount = -2\n",
|
||||
__func__);
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Functional tests */
|
||||
|
||||
if (!TestSynchBarrierWithFlags(0, dwMaxThreads, dwNumLoops))
|
||||
{
|
||||
(void)fprintf(
|
||||
stderr,
|
||||
"%s: TestSynchBarrierWithFlags(0) unecpectedly succeeded with lTotalThreads = -1\n",
|
||||
__func__);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!TestSynchBarrierWithFlags(SYNCHRONIZATION_BARRIER_FLAGS_SPIN_ONLY, dwMinThreads,
|
||||
dwNumLoops))
|
||||
{
|
||||
(void)fprintf(stderr,
|
||||
"%s: TestSynchBarrierWithFlags(SYNCHRONIZATION_BARRIER_FLAGS_SPIN_ONLY) "
|
||||
"unecpectedly succeeded with lTotalThreads = -1\n",
|
||||
__func__);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!TestSynchBarrierWithFlags(SYNCHRONIZATION_BARRIER_FLAGS_BLOCK_ONLY, dwMaxThreads,
|
||||
dwNumLoops))
|
||||
{
|
||||
(void)fprintf(stderr,
|
||||
"%s: TestSynchBarrierWithFlags(SYNCHRONIZATION_BARRIER_FLAGS_BLOCK_ONLY) "
|
||||
"unecpectedly succeeded with lTotalThreads = -1\n",
|
||||
__func__);
|
||||
return -1;
|
||||
}
|
||||
|
||||
printf("%s: Test successfully completed\n", __func__);
|
||||
return 0;
|
||||
}
|
||||
382
third_party/FreeRDP/winpr/libwinpr/synch/test/TestSynchCritical.c
vendored
Normal file
382
third_party/FreeRDP/winpr/libwinpr/synch/test/TestSynchCritical.c
vendored
Normal file
@@ -0,0 +1,382 @@
|
||||
|
||||
#include <stdio.h>
|
||||
#include <winpr/crt.h>
|
||||
#include <winpr/crypto.h>
|
||||
#include <winpr/windows.h>
|
||||
#include <winpr/synch.h>
|
||||
#include <winpr/sysinfo.h>
|
||||
#include <winpr/thread.h>
|
||||
#include <winpr/interlocked.h>
|
||||
|
||||
#define TEST_SYNC_CRITICAL_TEST1_RUNTIME_MS 100
|
||||
#define TEST_SYNC_CRITICAL_TEST1_RUNS 4
|
||||
|
||||
static CRITICAL_SECTION critical;
|
||||
static LONG gTestValueVulnerable = 0;
|
||||
static LONG gTestValueSerialized = 0;
|
||||
|
||||
static BOOL TestSynchCritical_TriggerAndCheckRaceCondition(HANDLE OwningThread, LONG RecursionCount)
|
||||
{
|
||||
/* if called unprotected this will hopefully trigger a race condition ... */
|
||||
gTestValueVulnerable++;
|
||||
|
||||
if (critical.OwningThread != OwningThread)
|
||||
{
|
||||
printf("CriticalSection failure: OwningThread is invalid\n");
|
||||
return FALSE;
|
||||
}
|
||||
if (critical.RecursionCount != RecursionCount)
|
||||
{
|
||||
printf("CriticalSection failure: RecursionCount is invalid\n");
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/* ... which we try to detect using the serialized counter */
|
||||
if (gTestValueVulnerable != InterlockedIncrement(&gTestValueSerialized))
|
||||
{
|
||||
printf("CriticalSection failure: Data corruption detected\n");
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static UINT32 prand(UINT32 max)
|
||||
{
|
||||
UINT32 tmp = 0;
|
||||
if (max <= 1)
|
||||
return 1;
|
||||
if (winpr_RAND(&tmp, sizeof(tmp)) < 0)
|
||||
return 0;
|
||||
return tmp % (max - 1) + 1;
|
||||
}
|
||||
|
||||
/* this thread function shall increment the global dwTestValue until the PBOOL passed in arg is
|
||||
* FALSE */
|
||||
static DWORD WINAPI TestSynchCritical_Test1(LPVOID arg)
|
||||
{
|
||||
int rc = 0;
|
||||
HANDLE hThread = (HANDLE)(ULONG_PTR)GetCurrentThreadId();
|
||||
|
||||
PBOOL pbContinueRunning = (PBOOL)arg;
|
||||
|
||||
while (*pbContinueRunning)
|
||||
{
|
||||
EnterCriticalSection(&critical);
|
||||
|
||||
rc = 1;
|
||||
|
||||
if (!TestSynchCritical_TriggerAndCheckRaceCondition(hThread, rc))
|
||||
return 1;
|
||||
|
||||
/* add some random recursion level */
|
||||
UINT32 j = prand(5);
|
||||
for (UINT32 i = 0; i < j; i++)
|
||||
{
|
||||
if (!TestSynchCritical_TriggerAndCheckRaceCondition(hThread, rc++))
|
||||
return 2;
|
||||
EnterCriticalSection(&critical);
|
||||
}
|
||||
for (UINT32 i = 0; i < j; i++)
|
||||
{
|
||||
if (!TestSynchCritical_TriggerAndCheckRaceCondition(hThread, rc--))
|
||||
return 2;
|
||||
LeaveCriticalSection(&critical);
|
||||
}
|
||||
|
||||
if (!TestSynchCritical_TriggerAndCheckRaceCondition(hThread, rc))
|
||||
return 3;
|
||||
|
||||
LeaveCriticalSection(&critical);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* this thread function tries to call TryEnterCriticalSection while the main thread holds the lock
|
||||
*/
|
||||
static DWORD WINAPI TestSynchCritical_Test2(LPVOID arg)
|
||||
{
|
||||
WINPR_UNUSED(arg);
|
||||
if (TryEnterCriticalSection(&critical) == TRUE)
|
||||
{
|
||||
LeaveCriticalSection(&critical);
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static DWORD WINAPI TestSynchCritical_Main(LPVOID arg)
|
||||
{
|
||||
SYSTEM_INFO sysinfo;
|
||||
DWORD dwPreviousSpinCount = 0;
|
||||
DWORD dwSpinCount = 0;
|
||||
DWORD dwSpinCountExpected = 0;
|
||||
HANDLE hMainThread = nullptr;
|
||||
HANDLE* hThreads = nullptr;
|
||||
HANDLE hThread = nullptr;
|
||||
DWORD dwThreadCount = 0;
|
||||
DWORD dwThreadExitCode = 0;
|
||||
BOOL bTest1Running = 0;
|
||||
|
||||
PBOOL pbThreadTerminated = (PBOOL)arg;
|
||||
|
||||
GetNativeSystemInfo(&sysinfo);
|
||||
|
||||
hMainThread = (HANDLE)(ULONG_PTR)GetCurrentThreadId();
|
||||
|
||||
/**
|
||||
* Test SpinCount in SetCriticalSectionSpinCount, InitializeCriticalSectionEx and
|
||||
* InitializeCriticalSectionAndSpinCount SpinCount must be forced to be zero on on uniprocessor
|
||||
* systems and on systems where WINPR_CRITICAL_SECTION_DISABLE_SPINCOUNT is defined
|
||||
*/
|
||||
|
||||
dwSpinCount = 100;
|
||||
InitializeCriticalSectionEx(&critical, dwSpinCount, 0);
|
||||
while (--dwSpinCount)
|
||||
{
|
||||
dwPreviousSpinCount = SetCriticalSectionSpinCount(&critical, dwSpinCount);
|
||||
dwSpinCountExpected = 0;
|
||||
#if !defined(WINPR_CRITICAL_SECTION_DISABLE_SPINCOUNT)
|
||||
if (sysinfo.dwNumberOfProcessors > 1)
|
||||
dwSpinCountExpected = dwSpinCount + 1;
|
||||
#endif
|
||||
if (dwPreviousSpinCount != dwSpinCountExpected)
|
||||
{
|
||||
printf("CriticalSection failure: SetCriticalSectionSpinCount returned %" PRIu32
|
||||
" (expected: %" PRIu32 ")\n",
|
||||
dwPreviousSpinCount, dwSpinCountExpected);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
DeleteCriticalSection(&critical);
|
||||
|
||||
if (dwSpinCount % 2 == 0)
|
||||
{
|
||||
if (!InitializeCriticalSectionAndSpinCount(&critical, dwSpinCount))
|
||||
goto fail;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!InitializeCriticalSectionEx(&critical, dwSpinCount, 0))
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
DeleteCriticalSection(&critical);
|
||||
|
||||
/**
|
||||
* Test single-threaded recursive
|
||||
* TryEnterCriticalSection/EnterCriticalSection/LeaveCriticalSection
|
||||
*
|
||||
*/
|
||||
|
||||
InitializeCriticalSection(&critical);
|
||||
|
||||
int i = 0;
|
||||
for (; i < 10; i++)
|
||||
{
|
||||
if (critical.RecursionCount != i)
|
||||
{
|
||||
printf("CriticalSection failure: RecursionCount field is %" PRId32 " instead of %d.\n",
|
||||
critical.RecursionCount, i);
|
||||
goto fail;
|
||||
}
|
||||
if (i % 2 == 0)
|
||||
{
|
||||
EnterCriticalSection(&critical);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (TryEnterCriticalSection(&critical) == FALSE)
|
||||
{
|
||||
printf("CriticalSection failure: TryEnterCriticalSection failed where it should "
|
||||
"not.\n");
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
if (critical.OwningThread != hMainThread)
|
||||
{
|
||||
printf("CriticalSection failure: Could not verify section ownership (loop index=%d).\n",
|
||||
i);
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
while (--i >= 0)
|
||||
{
|
||||
LeaveCriticalSection(&critical);
|
||||
if (critical.RecursionCount != i)
|
||||
{
|
||||
printf("CriticalSection failure: RecursionCount field is %" PRId32 " instead of %d.\n",
|
||||
critical.RecursionCount, i);
|
||||
goto fail;
|
||||
}
|
||||
if (critical.OwningThread != (i ? hMainThread : nullptr))
|
||||
{
|
||||
printf("CriticalSection failure: Could not verify section ownership (loop index=%d).\n",
|
||||
i);
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
DeleteCriticalSection(&critical);
|
||||
|
||||
/**
|
||||
* Test using multiple threads modifying the same value
|
||||
*/
|
||||
|
||||
dwThreadCount = sysinfo.dwNumberOfProcessors > 1 ? sysinfo.dwNumberOfProcessors : 2;
|
||||
|
||||
hThreads = (HANDLE*)calloc(dwThreadCount, sizeof(HANDLE));
|
||||
if (!hThreads)
|
||||
{
|
||||
printf("Problem allocating memory\n");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
for (int j = 0; j < TEST_SYNC_CRITICAL_TEST1_RUNS; j++)
|
||||
{
|
||||
dwSpinCount = j * 100;
|
||||
if (!InitializeCriticalSectionAndSpinCount(&critical, dwSpinCount))
|
||||
goto fail;
|
||||
|
||||
gTestValueVulnerable = 0;
|
||||
gTestValueSerialized = 0;
|
||||
|
||||
/* the TestSynchCritical_Test1 threads shall run until bTest1Running is FALSE */
|
||||
bTest1Running = TRUE;
|
||||
for (int i = 0; i < (int)dwThreadCount; i++)
|
||||
{
|
||||
if (!(hThreads[i] = CreateThread(nullptr, 0, TestSynchCritical_Test1, &bTest1Running, 0,
|
||||
nullptr)))
|
||||
{
|
||||
printf("CriticalSection failure: Failed to create test_1 thread #%d\n", i);
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
|
||||
/* let it run for TEST_SYNC_CRITICAL_TEST1_RUNTIME_MS ... */
|
||||
Sleep(TEST_SYNC_CRITICAL_TEST1_RUNTIME_MS);
|
||||
bTest1Running = FALSE;
|
||||
|
||||
for (int i = 0; i < (int)dwThreadCount; i++)
|
||||
{
|
||||
if (WaitForSingleObject(hThreads[i], INFINITE) != WAIT_OBJECT_0)
|
||||
{
|
||||
printf("CriticalSection failure: Failed to wait for thread #%d\n", i);
|
||||
goto fail;
|
||||
}
|
||||
GetExitCodeThread(hThreads[i], &dwThreadExitCode);
|
||||
if (dwThreadExitCode != 0)
|
||||
{
|
||||
printf("CriticalSection failure: Thread #%d returned error code %" PRIu32 "\n", i,
|
||||
dwThreadExitCode);
|
||||
goto fail;
|
||||
}
|
||||
(void)CloseHandle(hThreads[i]);
|
||||
}
|
||||
|
||||
if (gTestValueVulnerable != gTestValueSerialized)
|
||||
{
|
||||
printf("CriticalSection failure: unexpected test value %" PRId32 " (expected %" PRId32
|
||||
")\n",
|
||||
gTestValueVulnerable, gTestValueSerialized);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
DeleteCriticalSection(&critical);
|
||||
}
|
||||
|
||||
free((void*)hThreads);
|
||||
|
||||
/**
|
||||
* TryEnterCriticalSection in thread must fail if we hold the lock in the main thread
|
||||
*/
|
||||
|
||||
InitializeCriticalSection(&critical);
|
||||
|
||||
if (TryEnterCriticalSection(&critical) == FALSE)
|
||||
{
|
||||
printf("CriticalSection failure: TryEnterCriticalSection unexpectedly failed.\n");
|
||||
goto fail;
|
||||
}
|
||||
/* This thread tries to call TryEnterCriticalSection which must fail */
|
||||
if (!(hThread = CreateThread(nullptr, 0, TestSynchCritical_Test2, nullptr, 0, nullptr)))
|
||||
{
|
||||
printf("CriticalSection failure: Failed to create test_2 thread\n");
|
||||
goto fail;
|
||||
}
|
||||
if (WaitForSingleObject(hThread, INFINITE) != WAIT_OBJECT_0)
|
||||
{
|
||||
printf("CriticalSection failure: Failed to wait for thread\n");
|
||||
goto fail;
|
||||
}
|
||||
GetExitCodeThread(hThread, &dwThreadExitCode);
|
||||
if (dwThreadExitCode != 0)
|
||||
{
|
||||
printf("CriticalSection failure: Thread returned error code %" PRIu32 "\n",
|
||||
dwThreadExitCode);
|
||||
goto fail;
|
||||
}
|
||||
(void)CloseHandle(hThread);
|
||||
|
||||
*pbThreadTerminated = TRUE; /* requ. for winpr issue, see below */
|
||||
return 0;
|
||||
|
||||
fail:
|
||||
*pbThreadTerminated = TRUE; /* requ. for winpr issue, see below */
|
||||
return 1;
|
||||
}
|
||||
|
||||
int TestSynchCritical(int argc, char* argv[])
|
||||
{
|
||||
BOOL bThreadTerminated = FALSE;
|
||||
HANDLE hThread = nullptr;
|
||||
DWORD dwThreadExitCode = 0;
|
||||
DWORD dwDeadLockDetectionTimeMs = 0;
|
||||
|
||||
WINPR_UNUSED(argc);
|
||||
WINPR_UNUSED(argv);
|
||||
|
||||
dwDeadLockDetectionTimeMs =
|
||||
2 * TEST_SYNC_CRITICAL_TEST1_RUNTIME_MS * TEST_SYNC_CRITICAL_TEST1_RUNS;
|
||||
|
||||
printf("Deadlock will be assumed after %" PRIu32 " ms.\n", dwDeadLockDetectionTimeMs);
|
||||
|
||||
if (!(hThread =
|
||||
CreateThread(nullptr, 0, TestSynchCritical_Main, &bThreadTerminated, 0, nullptr)))
|
||||
{
|
||||
printf("CriticalSection failure: Failed to create main thread\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* We have to be able to detect dead locks in this test.
|
||||
* At the time of writing winpr's WaitForSingleObject has not implemented timeout for thread
|
||||
* wait
|
||||
*
|
||||
* Workaround checking the value of bThreadTerminated which is passed in the thread arg
|
||||
*/
|
||||
|
||||
for (DWORD i = 0; i < dwDeadLockDetectionTimeMs; i += 10)
|
||||
{
|
||||
if (bThreadTerminated)
|
||||
break;
|
||||
|
||||
Sleep(10);
|
||||
}
|
||||
|
||||
if (!bThreadTerminated)
|
||||
{
|
||||
printf("CriticalSection failure: Possible dead lock detected\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
GetExitCodeThread(hThread, &dwThreadExitCode);
|
||||
(void)CloseHandle(hThread);
|
||||
|
||||
if (dwThreadExitCode != 0)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
94
third_party/FreeRDP/winpr/libwinpr/synch/test/TestSynchEvent.c
vendored
Normal file
94
third_party/FreeRDP/winpr/libwinpr/synch/test/TestSynchEvent.c
vendored
Normal file
@@ -0,0 +1,94 @@
|
||||
|
||||
#include <winpr/crt.h>
|
||||
#include <winpr/synch.h>
|
||||
|
||||
int TestSynchEvent(int argc, char* argv[])
|
||||
{
|
||||
HANDLE event = nullptr;
|
||||
WINPR_UNUSED(argc);
|
||||
WINPR_UNUSED(argv);
|
||||
if (ResetEvent(nullptr))
|
||||
{
|
||||
printf("ResetEvent(nullptr) unexpectedly succeeded\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (SetEvent(nullptr))
|
||||
{
|
||||
printf("SetEvent(nullptr) unexpectedly succeeded\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
event = CreateEvent(nullptr, TRUE, TRUE, nullptr);
|
||||
|
||||
if (!event)
|
||||
{
|
||||
printf("CreateEvent failure\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (WaitForSingleObject(event, INFINITE) != WAIT_OBJECT_0)
|
||||
{
|
||||
printf("WaitForSingleObject failure 1\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!ResetEvent(event))
|
||||
{
|
||||
printf("ResetEvent failure with signaled event object\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (WaitForSingleObject(event, 0) != WAIT_TIMEOUT)
|
||||
{
|
||||
printf("WaitForSingleObject failure 2\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!ResetEvent(event))
|
||||
{
|
||||
/* Note: ResetEvent must also succeed if event is currently nonsignaled */
|
||||
printf("ResetEvent failure with nonsignaled event object\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!SetEvent(event))
|
||||
{
|
||||
printf("SetEvent failure with nonsignaled event object\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (WaitForSingleObject(event, 0) != WAIT_OBJECT_0)
|
||||
{
|
||||
printf("WaitForSingleObject failure 3\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
for (int i = 0; i < 10000; i++)
|
||||
{
|
||||
if (!SetEvent(event))
|
||||
{
|
||||
printf("SetEvent failure with signaled event object (i = %d)\n", i);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
if (!ResetEvent(event))
|
||||
{
|
||||
printf("ResetEvent failure after multiple SetEvent calls\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Independent of the amount of the previous SetEvent calls, a single
|
||||
ResetEvent must be sufficient to get into nonsignaled state */
|
||||
|
||||
if (WaitForSingleObject(event, 0) != WAIT_TIMEOUT)
|
||||
{
|
||||
printf("WaitForSingleObject failure 4\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
(void)CloseHandle(event);
|
||||
|
||||
return 0;
|
||||
}
|
||||
167
third_party/FreeRDP/winpr/libwinpr/synch/test/TestSynchInit.c
vendored
Normal file
167
third_party/FreeRDP/winpr/libwinpr/synch/test/TestSynchInit.c
vendored
Normal file
@@ -0,0 +1,167 @@
|
||||
#include <stdio.h>
|
||||
#include <winpr/crt.h>
|
||||
#include <winpr/crypto.h>
|
||||
#include <winpr/synch.h>
|
||||
#include <winpr/thread.h>
|
||||
#include <winpr/interlocked.h>
|
||||
|
||||
#define TEST_NUM_THREADS 100
|
||||
#define TEST_NUM_FAILURES 10
|
||||
|
||||
static INIT_ONCE initOnceTest = INIT_ONCE_STATIC_INIT;
|
||||
|
||||
static HANDLE hStartEvent = nullptr;
|
||||
static LONG* pErrors = nullptr;
|
||||
static LONG* pTestThreadFunctionCalls = nullptr;
|
||||
static LONG* pTestOnceFunctionCalls = nullptr;
|
||||
static LONG* pInitOnceExecuteOnceCalls = nullptr;
|
||||
|
||||
static UINT32 prand(UINT32 max)
|
||||
{
|
||||
UINT32 tmp = 0;
|
||||
if (max <= 1)
|
||||
return 1;
|
||||
if (winpr_RAND(&tmp, sizeof(tmp)) < 0)
|
||||
return 0;
|
||||
return tmp % (max - 1) + 1;
|
||||
}
|
||||
|
||||
static BOOL CALLBACK TestOnceFunction(PINIT_ONCE once, PVOID param, PVOID* context)
|
||||
{
|
||||
LONG calls = InterlockedIncrement(pTestOnceFunctionCalls) - 1;
|
||||
|
||||
WINPR_UNUSED(once);
|
||||
WINPR_UNUSED(param);
|
||||
WINPR_UNUSED(context);
|
||||
|
||||
/* simulate execution time */
|
||||
Sleep(30 + prand(40));
|
||||
|
||||
if (calls < TEST_NUM_FAILURES)
|
||||
{
|
||||
/* simulated error */
|
||||
return FALSE;
|
||||
}
|
||||
if (calls == TEST_NUM_FAILURES)
|
||||
{
|
||||
return TRUE;
|
||||
}
|
||||
(void)fprintf(stderr, "%s: error: called again after success\n", __func__);
|
||||
InterlockedIncrement(pErrors);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static DWORD WINAPI TestThreadFunction(LPVOID lpParam)
|
||||
{
|
||||
LONG calls = 0;
|
||||
BOOL ok = 0;
|
||||
|
||||
WINPR_UNUSED(lpParam);
|
||||
|
||||
InterlockedIncrement(pTestThreadFunctionCalls);
|
||||
if (WaitForSingleObject(hStartEvent, INFINITE) != WAIT_OBJECT_0)
|
||||
{
|
||||
(void)fprintf(stderr, "%s: error: failed to wait for start event\n", __func__);
|
||||
InterlockedIncrement(pErrors);
|
||||
return 0;
|
||||
}
|
||||
|
||||
ok = InitOnceExecuteOnce(&initOnceTest, TestOnceFunction, nullptr, nullptr);
|
||||
calls = InterlockedIncrement(pInitOnceExecuteOnceCalls);
|
||||
if (!ok && calls > TEST_NUM_FAILURES)
|
||||
{
|
||||
(void)fprintf(stderr, "%s: InitOnceExecuteOnce failed unexpectedly\n", __func__);
|
||||
InterlockedIncrement(pErrors);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int TestSynchInit(int argc, char* argv[])
|
||||
{
|
||||
HANDLE hThreads[TEST_NUM_THREADS];
|
||||
DWORD dwCreatedThreads = 0;
|
||||
BOOL result = FALSE;
|
||||
|
||||
WINPR_UNUSED(argc);
|
||||
WINPR_UNUSED(argv);
|
||||
|
||||
pErrors = winpr_aligned_malloc(sizeof(LONG), sizeof(LONG));
|
||||
pTestThreadFunctionCalls = winpr_aligned_malloc(sizeof(LONG), sizeof(LONG));
|
||||
pTestOnceFunctionCalls = winpr_aligned_malloc(sizeof(LONG), sizeof(LONG));
|
||||
pInitOnceExecuteOnceCalls = winpr_aligned_malloc(sizeof(LONG), sizeof(LONG));
|
||||
|
||||
if (!pErrors || !pTestThreadFunctionCalls || !pTestOnceFunctionCalls ||
|
||||
!pInitOnceExecuteOnceCalls)
|
||||
{
|
||||
(void)fprintf(stderr, "error: _aligned_malloc failed\n");
|
||||
goto out;
|
||||
}
|
||||
|
||||
*pErrors = 0;
|
||||
*pTestThreadFunctionCalls = 0;
|
||||
*pTestOnceFunctionCalls = 0;
|
||||
*pInitOnceExecuteOnceCalls = 0;
|
||||
|
||||
if (!(hStartEvent = CreateEvent(nullptr, TRUE, FALSE, nullptr)))
|
||||
{
|
||||
(void)fprintf(stderr, "error creating start event\n");
|
||||
InterlockedIncrement(pErrors);
|
||||
goto out;
|
||||
}
|
||||
|
||||
for (DWORD i = 0; i < TEST_NUM_THREADS; i++)
|
||||
{
|
||||
if (!(hThreads[i] = CreateThread(nullptr, 0, TestThreadFunction, nullptr, 0, nullptr)))
|
||||
{
|
||||
(void)fprintf(stderr, "error creating thread #%" PRIu32 "\n", i);
|
||||
InterlockedIncrement(pErrors);
|
||||
goto out;
|
||||
}
|
||||
dwCreatedThreads++;
|
||||
}
|
||||
|
||||
Sleep(100);
|
||||
(void)SetEvent(hStartEvent);
|
||||
|
||||
for (DWORD i = 0; i < dwCreatedThreads; i++)
|
||||
{
|
||||
if (WaitForSingleObject(hThreads[i], INFINITE) != WAIT_OBJECT_0)
|
||||
{
|
||||
(void)fprintf(stderr, "error: error waiting for thread #%" PRIu32 "\n", i);
|
||||
InterlockedIncrement(pErrors);
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
if (*pErrors == 0 && *pTestThreadFunctionCalls == TEST_NUM_THREADS &&
|
||||
*pInitOnceExecuteOnceCalls == TEST_NUM_THREADS &&
|
||||
*pTestOnceFunctionCalls == TEST_NUM_FAILURES + 1)
|
||||
{
|
||||
result = TRUE;
|
||||
}
|
||||
|
||||
out:
|
||||
(void)fprintf(stderr, "Test result: %s\n", result ? "OK" : "ERROR");
|
||||
(void)fprintf(stderr, "Error count: %" PRId32 "\n", pErrors ? *pErrors : -1);
|
||||
(void)fprintf(stderr, "Threads created: %" PRIu32 "\n", dwCreatedThreads);
|
||||
(void)fprintf(stderr, "TestThreadFunctionCalls: %" PRId32 "\n",
|
||||
pTestThreadFunctionCalls ? *pTestThreadFunctionCalls : -1);
|
||||
(void)fprintf(stderr, "InitOnceExecuteOnceCalls: %" PRId32 "\n",
|
||||
pInitOnceExecuteOnceCalls ? *pInitOnceExecuteOnceCalls : -1);
|
||||
(void)fprintf(stderr, "TestOnceFunctionCalls: %" PRId32 "\n",
|
||||
pTestOnceFunctionCalls ? *pTestOnceFunctionCalls : -1);
|
||||
|
||||
winpr_aligned_free(pErrors);
|
||||
winpr_aligned_free(pTestThreadFunctionCalls);
|
||||
winpr_aligned_free(pTestOnceFunctionCalls);
|
||||
winpr_aligned_free(pInitOnceExecuteOnceCalls);
|
||||
|
||||
(void)CloseHandle(hStartEvent);
|
||||
|
||||
for (DWORD i = 0; i < dwCreatedThreads; i++)
|
||||
{
|
||||
(void)CloseHandle(hThreads[i]);
|
||||
}
|
||||
|
||||
return (result ? 0 : 1);
|
||||
}
|
||||
255
third_party/FreeRDP/winpr/libwinpr/synch/test/TestSynchMultipleThreads.c
vendored
Normal file
255
third_party/FreeRDP/winpr/libwinpr/synch/test/TestSynchMultipleThreads.c
vendored
Normal file
@@ -0,0 +1,255 @@
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
#include <winpr/crt.h>
|
||||
#include <winpr/crypto.h>
|
||||
#include <winpr/synch.h>
|
||||
#include <winpr/thread.h>
|
||||
|
||||
#define THREADS 8
|
||||
|
||||
static UINT32 prand(UINT32 max)
|
||||
{
|
||||
UINT32 tmp = 0;
|
||||
if (max <= 1)
|
||||
return 1;
|
||||
winpr_RAND(&tmp, sizeof(tmp));
|
||||
return tmp % (max - 1) + 1;
|
||||
}
|
||||
|
||||
static DWORD WINAPI test_thread(LPVOID arg)
|
||||
{
|
||||
UINT32 timeout = 50 + prand(100);
|
||||
WINPR_UNUSED(arg);
|
||||
Sleep(timeout);
|
||||
ExitThread(0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int start_threads(size_t count, HANDLE* threads)
|
||||
{
|
||||
for (size_t i = 0; i < count; i++)
|
||||
{
|
||||
threads[i] = CreateThread(nullptr, 0, test_thread, nullptr, CREATE_SUSPENDED, nullptr);
|
||||
|
||||
if (!threads[i])
|
||||
{
|
||||
(void)fprintf(stderr, "%s: CreateThread [%" PRIuz "] failure\n", __func__, i);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < count; i++)
|
||||
ResumeThread(threads[i]);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int close_threads(DWORD count, HANDLE* threads)
|
||||
{
|
||||
int rc = 0;
|
||||
|
||||
for (DWORD i = 0; i < count; i++)
|
||||
{
|
||||
if (!threads[i])
|
||||
continue;
|
||||
|
||||
if (!CloseHandle(threads[i]))
|
||||
{
|
||||
(void)fprintf(stderr, "%s: CloseHandle [%" PRIu32 "] failure\n", __func__, i);
|
||||
rc = -1;
|
||||
}
|
||||
threads[i] = nullptr;
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
static BOOL TestWaitForAll(void)
|
||||
{
|
||||
BOOL rc = FALSE;
|
||||
HANDLE threads[THREADS] = WINPR_C_ARRAY_INIT;
|
||||
/* WaitForAll, timeout */
|
||||
if (start_threads(ARRAYSIZE(threads), threads))
|
||||
{
|
||||
(void)fprintf(stderr, "%s: start_threads failed\n", __func__);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
const DWORD ret = WaitForMultipleObjects(ARRAYSIZE(threads), threads, TRUE, 10);
|
||||
if (ret != WAIT_TIMEOUT)
|
||||
{
|
||||
(void)fprintf(stderr, "%s: WaitForMultipleObjects bWaitAll, timeout 10 failed, ret=%d\n",
|
||||
__func__, ret);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (WaitForMultipleObjects(ARRAYSIZE(threads), threads, TRUE, INFINITE) != WAIT_OBJECT_0)
|
||||
{
|
||||
(void)fprintf(stderr, "%s: WaitForMultipleObjects bWaitAll, INFINITE failed\n", __func__);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
rc = TRUE;
|
||||
fail:
|
||||
if (close_threads(ARRAYSIZE(threads), threads))
|
||||
{
|
||||
(void)fprintf(stderr, "%s: close_threads failed\n", __func__);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
static BOOL TestWaitOne(void)
|
||||
{
|
||||
BOOL rc = FALSE;
|
||||
HANDLE threads[THREADS] = WINPR_C_ARRAY_INIT;
|
||||
/* WaitForAll, timeout */
|
||||
if (start_threads(ARRAYSIZE(threads), threads))
|
||||
{
|
||||
(void)fprintf(stderr, "%s: start_threads failed\n", __func__);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
const DWORD ret = WaitForMultipleObjects(ARRAYSIZE(threads), threads, FALSE, INFINITE);
|
||||
if (ret > (WAIT_OBJECT_0 + ARRAYSIZE(threads)))
|
||||
{
|
||||
(void)fprintf(stderr, "%s: WaitForMultipleObjects INFINITE failed\n", __func__);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (WaitForMultipleObjects(ARRAYSIZE(threads), threads, TRUE, INFINITE) != WAIT_OBJECT_0)
|
||||
{
|
||||
(void)fprintf(stderr, "%s: WaitForMultipleObjects bWaitAll, INFINITE failed\n", __func__);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
rc = TRUE;
|
||||
fail:
|
||||
if (close_threads(ARRAYSIZE(threads), threads))
|
||||
{
|
||||
(void)fprintf(stderr, "%s: close_threads failed\n", __func__);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
static BOOL TestWaitOneTimeout(void)
|
||||
{
|
||||
BOOL rc = FALSE;
|
||||
HANDLE threads[THREADS] = WINPR_C_ARRAY_INIT;
|
||||
/* WaitForAll, timeout */
|
||||
if (start_threads(ARRAYSIZE(threads), threads))
|
||||
{
|
||||
(void)fprintf(stderr, "%s: start_threads failed\n", __func__);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
const DWORD ret = WaitForMultipleObjects(ARRAYSIZE(threads), threads, FALSE, 1);
|
||||
if (ret != WAIT_TIMEOUT)
|
||||
{
|
||||
(void)fprintf(stderr, "%s: WaitForMultipleObjects timeout 50 failed, ret=%d\n", __func__,
|
||||
ret);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (WaitForMultipleObjects(ARRAYSIZE(threads), threads, TRUE, INFINITE) != WAIT_OBJECT_0)
|
||||
{
|
||||
(void)fprintf(stderr, "%s: WaitForMultipleObjects bWaitAll, INFINITE failed\n", __func__);
|
||||
goto fail;
|
||||
}
|
||||
rc = TRUE;
|
||||
fail:
|
||||
if (close_threads(ARRAYSIZE(threads), threads))
|
||||
{
|
||||
(void)fprintf(stderr, "%s: close_threads failed\n", __func__);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
static BOOL TestWaitOneTimeoutMultijoin(void)
|
||||
{
|
||||
BOOL rc = FALSE;
|
||||
HANDLE threads[THREADS] = WINPR_C_ARRAY_INIT;
|
||||
/* WaitForAll, timeout */
|
||||
if (start_threads(ARRAYSIZE(threads), threads))
|
||||
{
|
||||
(void)fprintf(stderr, "%s: start_threads failed\n", __func__);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < ARRAYSIZE(threads); i++)
|
||||
{
|
||||
const DWORD ret = WaitForMultipleObjects(ARRAYSIZE(threads), threads, FALSE, 0);
|
||||
if (ret != WAIT_TIMEOUT)
|
||||
{
|
||||
(void)fprintf(stderr, "%s: WaitForMultipleObjects timeout 0 failed, ret=%d\n", __func__,
|
||||
ret);
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
|
||||
if (WaitForMultipleObjects(ARRAYSIZE(threads), threads, TRUE, INFINITE) != WAIT_OBJECT_0)
|
||||
{
|
||||
(void)fprintf(stderr, "%s: WaitForMultipleObjects bWaitAll, INFINITE failed\n", __func__);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
rc = TRUE;
|
||||
fail:
|
||||
if (close_threads(ARRAYSIZE(threads), threads))
|
||||
{
|
||||
(void)fprintf(stderr, "%s: close_threads failed\n", __func__);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
static BOOL TestDetach(void)
|
||||
{
|
||||
BOOL rc = FALSE;
|
||||
HANDLE threads[THREADS] = WINPR_C_ARRAY_INIT;
|
||||
/* WaitForAll, timeout */
|
||||
if (start_threads(ARRAYSIZE(threads), threads))
|
||||
{
|
||||
(void)fprintf(stderr, "%s: start_threads failed\n", __func__);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
rc = TRUE;
|
||||
fail:
|
||||
if (close_threads(ARRAYSIZE(threads), threads))
|
||||
{
|
||||
(void)fprintf(stderr, "%s: close_threads failed\n", __func__);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
int TestSynchMultipleThreads(int argc, char* argv[])
|
||||
{
|
||||
WINPR_UNUSED(argc);
|
||||
WINPR_UNUSED(argv);
|
||||
|
||||
if (!TestWaitForAll())
|
||||
return -1;
|
||||
|
||||
if (!TestWaitOne())
|
||||
return -2;
|
||||
|
||||
if (!TestWaitOneTimeout())
|
||||
return -3;
|
||||
|
||||
if (!TestWaitOneTimeoutMultijoin())
|
||||
return -4;
|
||||
|
||||
if (!TestDetach())
|
||||
return -5;
|
||||
|
||||
return 0;
|
||||
}
|
||||
258
third_party/FreeRDP/winpr/libwinpr/synch/test/TestSynchMutex.c
vendored
Normal file
258
third_party/FreeRDP/winpr/libwinpr/synch/test/TestSynchMutex.c
vendored
Normal file
@@ -0,0 +1,258 @@
|
||||
|
||||
#include <winpr/crt.h>
|
||||
#include <winpr/synch.h>
|
||||
#include <winpr/thread.h>
|
||||
|
||||
static BOOL test_mutex_basic(void)
|
||||
{
|
||||
HANDLE mutex = nullptr;
|
||||
DWORD rc = 0;
|
||||
|
||||
if (!(mutex = CreateMutex(nullptr, FALSE, nullptr)))
|
||||
{
|
||||
printf("%s: CreateMutex failed\n", __func__);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
rc = WaitForSingleObject(mutex, INFINITE);
|
||||
|
||||
if (rc != WAIT_OBJECT_0)
|
||||
{
|
||||
printf("%s: WaitForSingleObject on mutex failed with %" PRIu32 "\n", __func__, rc);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if (!ReleaseMutex(mutex))
|
||||
{
|
||||
printf("%s: ReleaseMutex failed\n", __func__);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if (ReleaseMutex(mutex))
|
||||
{
|
||||
printf("%s: ReleaseMutex unexpectedly succeeded on released mutex\n", __func__);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if (!CloseHandle(mutex))
|
||||
{
|
||||
printf("%s: CloseHandle on mutex failed\n", __func__);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static BOOL test_mutex_recursive(void)
|
||||
{
|
||||
HANDLE mutex = nullptr;
|
||||
DWORD rc = 0;
|
||||
DWORD cnt = 50;
|
||||
|
||||
if (!(mutex = CreateMutex(nullptr, TRUE, nullptr)))
|
||||
{
|
||||
printf("%s: CreateMutex failed\n", __func__);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
for (UINT32 i = 0; i < cnt; i++)
|
||||
{
|
||||
rc = WaitForSingleObject(mutex, INFINITE);
|
||||
|
||||
if (rc != WAIT_OBJECT_0)
|
||||
{
|
||||
printf("%s: WaitForSingleObject #%" PRIu32 " on mutex failed with %" PRIu32 "\n",
|
||||
__func__, i, rc);
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
for (UINT32 i = 0; i < cnt; i++)
|
||||
{
|
||||
if (!ReleaseMutex(mutex))
|
||||
{
|
||||
printf("%s: ReleaseMutex #%" PRIu32 " failed\n", __func__, i);
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
if (!ReleaseMutex(mutex))
|
||||
{
|
||||
/* Note: The mutex was initially owned ! */
|
||||
printf("%s: Final ReleaseMutex failed\n", __func__);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if (ReleaseMutex(mutex))
|
||||
{
|
||||
printf("%s: ReleaseMutex unexpectedly succeeded on released mutex\n", __func__);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if (!CloseHandle(mutex))
|
||||
{
|
||||
printf("%s: CloseHandle on mutex failed\n", __func__);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static HANDLE thread1_mutex1 = nullptr;
|
||||
static HANDLE thread1_mutex2 = nullptr;
|
||||
static BOOL thread1_failed = TRUE;
|
||||
|
||||
static DWORD WINAPI test_mutex_thread1(LPVOID lpParam)
|
||||
{
|
||||
HANDLE hStartEvent = (HANDLE)lpParam;
|
||||
DWORD rc = 0;
|
||||
|
||||
if (WaitForSingleObject(hStartEvent, INFINITE) != WAIT_OBJECT_0)
|
||||
{
|
||||
(void)fprintf(stderr, "%s: failed to wait for start event\n", __func__);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* at this point:
|
||||
* thread1_mutex1 is expected to be locked
|
||||
* thread1_mutex2 is expected to be unlocked
|
||||
* defined task:
|
||||
* try to lock thread1_mutex1 (expected to fail)
|
||||
* lock and unlock thread1_mutex2 (expected to work)
|
||||
*/
|
||||
rc = WaitForSingleObject(thread1_mutex1, 10);
|
||||
|
||||
if (rc != WAIT_TIMEOUT)
|
||||
{
|
||||
(void)fprintf(stderr,
|
||||
"%s: WaitForSingleObject on thread1_mutex1 unexpectedly returned %" PRIu32
|
||||
" instead of WAIT_TIMEOUT (%u)\n",
|
||||
__func__, rc, WAIT_TIMEOUT);
|
||||
return 0;
|
||||
}
|
||||
|
||||
rc = WaitForSingleObject(thread1_mutex2, 10);
|
||||
|
||||
if (rc != WAIT_OBJECT_0)
|
||||
{
|
||||
(void)fprintf(stderr,
|
||||
"%s: WaitForSingleObject on thread1_mutex2 unexpectedly returned %" PRIu32
|
||||
" instead of WAIT_OBJECT_0\n",
|
||||
__func__, rc);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!ReleaseMutex(thread1_mutex2))
|
||||
{
|
||||
(void)fprintf(stderr, "%s: ReleaseMutex failed on thread1_mutex2\n", __func__);
|
||||
return 0;
|
||||
}
|
||||
|
||||
thread1_failed = FALSE;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static BOOL test_mutex_threading(void)
|
||||
{
|
||||
HANDLE hThread = nullptr;
|
||||
HANDLE hStartEvent = nullptr;
|
||||
|
||||
if (!(thread1_mutex1 = CreateMutex(nullptr, TRUE, nullptr)))
|
||||
{
|
||||
printf("%s: CreateMutex thread1_mutex1 failed\n", __func__);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (!(thread1_mutex2 = CreateMutex(nullptr, FALSE, nullptr)))
|
||||
{
|
||||
printf("%s: CreateMutex thread1_mutex2 failed\n", __func__);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (!(hStartEvent = CreateEvent(nullptr, TRUE, FALSE, nullptr)))
|
||||
{
|
||||
(void)fprintf(stderr, "%s: error creating start event\n", __func__);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
thread1_failed = TRUE;
|
||||
|
||||
if (!(hThread = CreateThread(nullptr, 0, test_mutex_thread1, (LPVOID)hStartEvent, 0, nullptr)))
|
||||
{
|
||||
(void)fprintf(stderr, "%s: error creating test_mutex_thread_1\n", __func__);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
Sleep(100);
|
||||
|
||||
if (!thread1_failed)
|
||||
{
|
||||
(void)fprintf(stderr, "%s: thread1 premature success\n", __func__);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
(void)SetEvent(hStartEvent);
|
||||
|
||||
if (WaitForSingleObject(hThread, 2000) != WAIT_OBJECT_0)
|
||||
{
|
||||
(void)fprintf(stderr, "%s: thread1 premature success\n", __func__);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (thread1_failed)
|
||||
{
|
||||
(void)fprintf(stderr, "%s: thread1 has not reported success\n", __func__);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
/**
|
||||
* - thread1 must not have succeeded to lock thread1_mutex1
|
||||
* - thread1 must have locked and unlocked thread1_mutex2
|
||||
*/
|
||||
|
||||
if (!ReleaseMutex(thread1_mutex1))
|
||||
{
|
||||
printf("%s: ReleaseMutex unexpectedly failed on thread1_mutex1\n", __func__);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (ReleaseMutex(thread1_mutex2))
|
||||
{
|
||||
printf("%s: ReleaseMutex unexpectedly succeeded on thread1_mutex2\n", __func__);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
(void)CloseHandle(hThread);
|
||||
(void)CloseHandle(hStartEvent);
|
||||
(void)CloseHandle(thread1_mutex1);
|
||||
(void)CloseHandle(thread1_mutex2);
|
||||
return TRUE;
|
||||
fail:
|
||||
(void)ReleaseMutex(thread1_mutex1);
|
||||
(void)ReleaseMutex(thread1_mutex2);
|
||||
(void)CloseHandle(thread1_mutex1);
|
||||
(void)CloseHandle(thread1_mutex2);
|
||||
(void)CloseHandle(hStartEvent);
|
||||
(void)CloseHandle(hThread);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
int TestSynchMutex(int argc, char* argv[])
|
||||
{
|
||||
int rc = 0;
|
||||
WINPR_UNUSED(argc);
|
||||
WINPR_UNUSED(argv);
|
||||
|
||||
if (!test_mutex_basic())
|
||||
rc += 1;
|
||||
|
||||
if (!test_mutex_recursive())
|
||||
rc += 2;
|
||||
|
||||
if (!test_mutex_threading())
|
||||
rc += 4;
|
||||
|
||||
printf("TestSynchMutex result %d\n", rc);
|
||||
return rc;
|
||||
}
|
||||
21
third_party/FreeRDP/winpr/libwinpr/synch/test/TestSynchSemaphore.c
vendored
Normal file
21
third_party/FreeRDP/winpr/libwinpr/synch/test/TestSynchSemaphore.c
vendored
Normal file
@@ -0,0 +1,21 @@
|
||||
|
||||
#include <winpr/crt.h>
|
||||
#include <winpr/synch.h>
|
||||
|
||||
int TestSynchSemaphore(int argc, char* argv[])
|
||||
{
|
||||
HANDLE semaphore = nullptr;
|
||||
WINPR_UNUSED(argc);
|
||||
WINPR_UNUSED(argv);
|
||||
semaphore = CreateSemaphore(nullptr, 0, 1, nullptr);
|
||||
|
||||
if (!semaphore)
|
||||
{
|
||||
printf("CreateSemaphore failure\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
(void)CloseHandle(semaphore);
|
||||
|
||||
return 0;
|
||||
}
|
||||
131
third_party/FreeRDP/winpr/libwinpr/synch/test/TestSynchThread.c
vendored
Normal file
131
third_party/FreeRDP/winpr/libwinpr/synch/test/TestSynchThread.c
vendored
Normal file
@@ -0,0 +1,131 @@
|
||||
|
||||
#include <winpr/crt.h>
|
||||
#include <winpr/synch.h>
|
||||
#include <winpr/thread.h>
|
||||
|
||||
static DWORD WINAPI test_thread(LPVOID arg)
|
||||
{
|
||||
WINPR_UNUSED(arg);
|
||||
Sleep(100);
|
||||
ExitThread(0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int TestSynchThread(int argc, char* argv[])
|
||||
{
|
||||
DWORD rc = 0;
|
||||
HANDLE thread = nullptr;
|
||||
|
||||
WINPR_UNUSED(argc);
|
||||
WINPR_UNUSED(argv);
|
||||
|
||||
thread = CreateThread(nullptr, 0, test_thread, nullptr, 0, nullptr);
|
||||
|
||||
if (!thread)
|
||||
{
|
||||
printf("CreateThread failure\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* TryJoin should now fail. */
|
||||
rc = WaitForSingleObject(thread, 0);
|
||||
|
||||
if (WAIT_TIMEOUT != rc)
|
||||
{
|
||||
printf("Timed WaitForSingleObject on running thread failed with %" PRIu32 "\n", rc);
|
||||
return -3;
|
||||
}
|
||||
|
||||
/* Join the thread */
|
||||
rc = WaitForSingleObject(thread, INFINITE);
|
||||
|
||||
if (WAIT_OBJECT_0 != rc)
|
||||
{
|
||||
printf("WaitForSingleObject on thread failed with %" PRIu32 "\n", rc);
|
||||
return -2;
|
||||
}
|
||||
|
||||
/* TimedJoin should now succeed. */
|
||||
rc = WaitForSingleObject(thread, 0);
|
||||
|
||||
if (WAIT_OBJECT_0 != rc)
|
||||
{
|
||||
printf("Timed WaitForSingleObject on dead thread failed with %" PRIu32 "\n", rc);
|
||||
return -5;
|
||||
}
|
||||
|
||||
/* check that WaitForSingleObject works multiple times on a terminated thread */
|
||||
for (int i = 0; i < 4; i++)
|
||||
{
|
||||
rc = WaitForSingleObject(thread, 0);
|
||||
if (WAIT_OBJECT_0 != rc)
|
||||
{
|
||||
printf("Timed WaitForSingleObject on dead thread failed with %" PRIu32 "\n", rc);
|
||||
return -6;
|
||||
}
|
||||
}
|
||||
|
||||
if (!CloseHandle(thread))
|
||||
{
|
||||
printf("CloseHandle failed!");
|
||||
return -1;
|
||||
}
|
||||
|
||||
thread = CreateThread(nullptr, 0, test_thread, nullptr, 0, nullptr);
|
||||
|
||||
if (!thread)
|
||||
{
|
||||
printf("CreateThread failure\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* TryJoin should now fail. */
|
||||
rc = WaitForSingleObject(thread, 10);
|
||||
|
||||
if (WAIT_TIMEOUT != rc)
|
||||
{
|
||||
printf("Timed WaitForSingleObject on running thread failed with %" PRIu32 "\n", rc);
|
||||
return -3;
|
||||
}
|
||||
|
||||
/* Join the thread */
|
||||
rc = WaitForSingleObject(thread, INFINITE);
|
||||
|
||||
if (WAIT_OBJECT_0 != rc)
|
||||
{
|
||||
printf("WaitForSingleObject on thread failed with %" PRIu32 "\n", rc);
|
||||
return -2;
|
||||
}
|
||||
|
||||
/* TimedJoin should now succeed. */
|
||||
rc = WaitForSingleObject(thread, 0);
|
||||
|
||||
if (WAIT_OBJECT_0 != rc)
|
||||
{
|
||||
printf("Timed WaitForSingleObject on dead thread failed with %" PRIu32 "\n", rc);
|
||||
return -5;
|
||||
}
|
||||
|
||||
if (!CloseHandle(thread))
|
||||
{
|
||||
printf("CloseHandle failed!");
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Thread detach test */
|
||||
thread = CreateThread(nullptr, 0, test_thread, nullptr, 0, nullptr);
|
||||
|
||||
if (!thread)
|
||||
{
|
||||
printf("CreateThread failure\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!CloseHandle(thread))
|
||||
{
|
||||
printf("CloseHandle failed!");
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
120
third_party/FreeRDP/winpr/libwinpr/synch/test/TestSynchTimerQueue.c
vendored
Normal file
120
third_party/FreeRDP/winpr/libwinpr/synch/test/TestSynchTimerQueue.c
vendored
Normal file
@@ -0,0 +1,120 @@
|
||||
|
||||
#include <winpr/crt.h>
|
||||
#include <winpr/sysinfo.h>
|
||||
#include <winpr/file.h>
|
||||
#include <winpr/synch.h>
|
||||
|
||||
#define FIRE_COUNT 5
|
||||
#define TIMER_COUNT 5
|
||||
|
||||
struct apc_data
|
||||
{
|
||||
DWORD TimerId;
|
||||
DWORD FireCount;
|
||||
DWORD DueTime;
|
||||
DWORD Period;
|
||||
UINT32 StartTime;
|
||||
DWORD MaxFireCount;
|
||||
HANDLE CompletionEvent;
|
||||
};
|
||||
typedef struct apc_data APC_DATA;
|
||||
|
||||
static VOID CALLBACK TimerRoutine(PVOID lpParam, BOOLEAN TimerOrWaitFired)
|
||||
{
|
||||
WINPR_UNUSED(TimerOrWaitFired);
|
||||
|
||||
if (!lpParam)
|
||||
return;
|
||||
|
||||
APC_DATA* apcData = (APC_DATA*)lpParam;
|
||||
const UINT32 CurrentTime = GetTickCount();
|
||||
const INT64 TimerTime = 1ll * CurrentTime - apcData->StartTime;
|
||||
const INT64 expectedTime =
|
||||
1ll * apcData->DueTime + (1ll * apcData->Period * apcData->FireCount);
|
||||
|
||||
apcData->FireCount++;
|
||||
|
||||
printf("TimerRoutine: TimerId: %" PRIu32 " FireCount: %" PRIu32 " ActualTime: %" PRId64
|
||||
" ExpectedTime: %" PRId64 " Discrepancy: %" PRId64 "\n",
|
||||
apcData->TimerId, apcData->FireCount, TimerTime, expectedTime, TimerTime - expectedTime);
|
||||
|
||||
Sleep(11);
|
||||
|
||||
if (apcData->FireCount == apcData->MaxFireCount)
|
||||
{
|
||||
(void)SetEvent(apcData->CompletionEvent);
|
||||
}
|
||||
}
|
||||
|
||||
int TestSynchTimerQueue(int argc, char* argv[])
|
||||
{
|
||||
HANDLE hTimers[TIMER_COUNT] = WINPR_C_ARRAY_INIT;
|
||||
APC_DATA apcData[TIMER_COUNT] = WINPR_C_ARRAY_INIT;
|
||||
|
||||
WINPR_UNUSED(argc);
|
||||
WINPR_UNUSED(argv);
|
||||
|
||||
HANDLE hTimerQueue = CreateTimerQueue();
|
||||
|
||||
if (!hTimerQueue)
|
||||
{
|
||||
printf("CreateTimerQueue failed (%" PRIu32 ")\n", GetLastError());
|
||||
return -1;
|
||||
}
|
||||
|
||||
for (DWORD index = 0; index < TIMER_COUNT; index++)
|
||||
{
|
||||
apcData[index].TimerId = index;
|
||||
apcData[index].StartTime = GetTickCount();
|
||||
apcData[index].DueTime = (index * 10) + 50;
|
||||
apcData[index].Period = 100;
|
||||
apcData[index].FireCount = 0;
|
||||
apcData[index].MaxFireCount = FIRE_COUNT;
|
||||
|
||||
if (!(apcData[index].CompletionEvent = CreateEvent(nullptr, TRUE, FALSE, nullptr)))
|
||||
{
|
||||
printf("Failed to create apcData[%" PRIu32 "] event (%" PRIu32 ")\n", index,
|
||||
GetLastError());
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!CreateTimerQueueTimer(&hTimers[index], hTimerQueue, TimerRoutine, &apcData[index],
|
||||
apcData[index].DueTime, apcData[index].Period, 0))
|
||||
{
|
||||
printf("CreateTimerQueueTimer failed (%" PRIu32 ")\n", GetLastError());
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
for (DWORD index = 0; index < TIMER_COUNT; index++)
|
||||
{
|
||||
if (WaitForSingleObject(apcData[index].CompletionEvent, 2000) != WAIT_OBJECT_0)
|
||||
{
|
||||
printf("Failed to wait for timer queue timer #%" PRIu32 " (%" PRIu32 ")\n", index,
|
||||
GetLastError());
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
for (DWORD index = 0; index < TIMER_COUNT; index++)
|
||||
{
|
||||
/**
|
||||
* Note: If the CompletionEvent parameter is INVALID_HANDLE_VALUE, the function waits
|
||||
* for any running timer callback functions to complete before returning.
|
||||
*/
|
||||
if (!DeleteTimerQueueTimer(hTimerQueue, hTimers[index], INVALID_HANDLE_VALUE))
|
||||
{
|
||||
printf("DeleteTimerQueueTimer failed (%" PRIu32 ")\n", GetLastError());
|
||||
return -1;
|
||||
}
|
||||
(void)CloseHandle(apcData[index].CompletionEvent);
|
||||
}
|
||||
|
||||
if (!DeleteTimerQueue(hTimerQueue))
|
||||
{
|
||||
printf("DeleteTimerQueue failed (%" PRIu32 ")\n", GetLastError());
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
83
third_party/FreeRDP/winpr/libwinpr/synch/test/TestSynchWaitableTimer.c
vendored
Normal file
83
third_party/FreeRDP/winpr/libwinpr/synch/test/TestSynchWaitableTimer.c
vendored
Normal file
@@ -0,0 +1,83 @@
|
||||
|
||||
#include <winpr/crt.h>
|
||||
#include <winpr/synch.h>
|
||||
|
||||
int TestSynchWaitableTimer(int argc, char* argv[])
|
||||
{
|
||||
DWORD status = 0;
|
||||
HANDLE timer = nullptr;
|
||||
LONG period = 0;
|
||||
LARGE_INTEGER due = WINPR_C_ARRAY_INIT;
|
||||
int result = -1;
|
||||
WINPR_UNUSED(argc);
|
||||
WINPR_UNUSED(argv);
|
||||
timer = CreateWaitableTimer(nullptr, FALSE, nullptr);
|
||||
|
||||
if (!timer)
|
||||
{
|
||||
printf("CreateWaitableTimer failure\n");
|
||||
goto out;
|
||||
}
|
||||
|
||||
due.QuadPart = -1500000LL; /* 0.15 seconds */
|
||||
|
||||
if (!SetWaitableTimer(timer, &due, 0, nullptr, nullptr, 0))
|
||||
{
|
||||
printf("SetWaitableTimer failure\n");
|
||||
goto out;
|
||||
}
|
||||
|
||||
status = WaitForSingleObject(timer, INFINITE);
|
||||
|
||||
if (status != WAIT_OBJECT_0)
|
||||
{
|
||||
printf("WaitForSingleObject(timer, INFINITE) failure\n");
|
||||
goto out;
|
||||
}
|
||||
|
||||
printf("Timer Signaled\n");
|
||||
status = WaitForSingleObject(timer, 200);
|
||||
|
||||
if (status != WAIT_TIMEOUT)
|
||||
{
|
||||
printf("WaitForSingleObject(timer, 200) failure: Actual: 0x%08" PRIX32
|
||||
", Expected: 0x%08X\n",
|
||||
status, WAIT_TIMEOUT);
|
||||
goto out;
|
||||
}
|
||||
|
||||
due.QuadPart = 0;
|
||||
period = 120; /* 0.12 seconds */
|
||||
|
||||
if (!SetWaitableTimer(timer, &due, period, nullptr, nullptr, 0))
|
||||
{
|
||||
printf("SetWaitableTimer failure\n");
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (WaitForSingleObject(timer, INFINITE) != WAIT_OBJECT_0)
|
||||
{
|
||||
printf("WaitForSingleObject(timer, INFINITE) failure\n");
|
||||
goto out;
|
||||
}
|
||||
|
||||
printf("Timer Signaled\n");
|
||||
|
||||
if (!SetWaitableTimer(timer, &due, period, nullptr, nullptr, 0))
|
||||
{
|
||||
printf("SetWaitableTimer failure\n");
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (WaitForMultipleObjects(1, &timer, FALSE, INFINITE) != WAIT_OBJECT_0)
|
||||
{
|
||||
printf("WaitForMultipleObjects(timer, INFINITE) failure\n");
|
||||
goto out;
|
||||
}
|
||||
|
||||
printf("Timer Signaled\n");
|
||||
result = 0;
|
||||
out:
|
||||
(void)CloseHandle(timer);
|
||||
return result;
|
||||
}
|
||||
92
third_party/FreeRDP/winpr/libwinpr/synch/test/TestSynchWaitableTimerAPC.c
vendored
Normal file
92
third_party/FreeRDP/winpr/libwinpr/synch/test/TestSynchWaitableTimerAPC.c
vendored
Normal file
@@ -0,0 +1,92 @@
|
||||
|
||||
#include <winpr/crt.h>
|
||||
#include <winpr/synch.h>
|
||||
#include <winpr/sysinfo.h>
|
||||
|
||||
static int g_Count = 0;
|
||||
static HANDLE g_Event = nullptr;
|
||||
|
||||
struct apc_data
|
||||
{
|
||||
UINT32 StartTime;
|
||||
};
|
||||
typedef struct apc_data APC_DATA;
|
||||
|
||||
static VOID CALLBACK TimerAPCProc(LPVOID lpArg, DWORD dwTimerLowValue, DWORD dwTimerHighValue)
|
||||
{
|
||||
APC_DATA* apcData = nullptr;
|
||||
UINT32 CurrentTime = GetTickCount();
|
||||
WINPR_UNUSED(dwTimerLowValue);
|
||||
WINPR_UNUSED(dwTimerHighValue);
|
||||
|
||||
if (!lpArg)
|
||||
return;
|
||||
|
||||
apcData = (APC_DATA*)lpArg;
|
||||
printf("TimerAPCProc: time: %" PRIu32 "\n", CurrentTime - apcData->StartTime);
|
||||
g_Count++;
|
||||
|
||||
if (g_Count >= 5)
|
||||
{
|
||||
(void)SetEvent(g_Event);
|
||||
}
|
||||
}
|
||||
|
||||
int TestSynchWaitableTimerAPC(int argc, char* argv[])
|
||||
{
|
||||
int status = -1;
|
||||
DWORD rc = 0;
|
||||
HANDLE hTimer = nullptr;
|
||||
BOOL bSuccess = 0;
|
||||
LARGE_INTEGER due = WINPR_C_ARRAY_INIT;
|
||||
APC_DATA apcData = WINPR_C_ARRAY_INIT;
|
||||
WINPR_UNUSED(argc);
|
||||
WINPR_UNUSED(argv);
|
||||
g_Event = CreateEvent(nullptr, TRUE, FALSE, nullptr);
|
||||
|
||||
if (!g_Event)
|
||||
{
|
||||
printf("Failed to create event\n");
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
hTimer = CreateWaitableTimer(nullptr, FALSE, nullptr);
|
||||
if (!hTimer)
|
||||
goto cleanup;
|
||||
|
||||
due.QuadPart = -1000 * 100LL; /* 0.1 seconds */
|
||||
apcData.StartTime = GetTickCount();
|
||||
bSuccess = SetWaitableTimer(hTimer, &due, 10, TimerAPCProc, &apcData, FALSE);
|
||||
|
||||
if (!bSuccess)
|
||||
goto cleanup;
|
||||
|
||||
/* nothing shall happen after 0.12 second, because thread is not in alertable state */
|
||||
rc = WaitForSingleObject(g_Event, 120);
|
||||
if (rc != WAIT_TIMEOUT)
|
||||
goto cleanup;
|
||||
|
||||
for (;;)
|
||||
{
|
||||
rc = WaitForSingleObjectEx(g_Event, INFINITE, TRUE);
|
||||
if (rc == WAIT_OBJECT_0)
|
||||
break;
|
||||
|
||||
if (rc == WAIT_IO_COMPLETION)
|
||||
continue;
|
||||
|
||||
printf("Failed to wait for completion event (%" PRIu32 ")\n", GetLastError());
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
status = 0;
|
||||
cleanup:
|
||||
|
||||
if (hTimer)
|
||||
(void)CloseHandle(hTimer);
|
||||
|
||||
if (g_Event)
|
||||
(void)CloseHandle(g_Event);
|
||||
|
||||
return status;
|
||||
}
|
||||
Reference in New Issue
Block a user