winpr/sync: Added InitOnceExecuteOnce plus CTest
This commit is contained in:
parent
fb4a64bc4a
commit
fae8f6fbf2
@ -154,6 +154,8 @@ WINPR_API LONG InterlockedExchangeAdd(LONG volatile *Addend, LONG Value);
|
||||
|
||||
WINPR_API LONG InterlockedCompareExchange(LONG volatile *Destination, LONG Exchange, LONG Comperand);
|
||||
|
||||
WINPR_API PVOID InterlockedCompareExchangePointer(PVOID volatile *Destination, PVOID Exchange, PVOID Comperand);
|
||||
|
||||
#endif /* _WIN32 */
|
||||
|
||||
#if (!defined(_WIN32) || (defined(_WIN32) && (_WIN32_WINNT < 0x0502)))
|
||||
|
@ -3,6 +3,8 @@
|
||||
* Synchronization Functions
|
||||
*
|
||||
* Copyright 2012 Marc-Andre Moreau <marcandre.moreau@gmail.com>
|
||||
* Copyright 2014 Thincast Technologies GmbH <norbert.federa@thincast.com>
|
||||
* Copyright 2014 Norbert Federa <norbert.federa@thincast.com>
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@ -102,22 +104,6 @@ WINPR_API BOOL ResetEvent(HANDLE hEvent);
|
||||
#define OpenEvent OpenEventA
|
||||
#endif
|
||||
|
||||
/* One-Time Initialization */
|
||||
|
||||
typedef union _RTL_RUN_ONCE
|
||||
{
|
||||
PVOID Ptr;
|
||||
} RTL_RUN_ONCE, *PRTL_RUN_ONCE;
|
||||
|
||||
typedef PRTL_RUN_ONCE PINIT_ONCE;
|
||||
typedef PRTL_RUN_ONCE LPINIT_ONCE;
|
||||
typedef BOOL CALLBACK (*PINIT_ONCE_FN) (PINIT_ONCE InitOnce, PVOID Parameter, PVOID* Context);
|
||||
|
||||
WINPR_API BOOL InitOnceBeginInitialize(LPINIT_ONCE lpInitOnce, DWORD dwFlags, PBOOL fPending, LPVOID* lpContext);
|
||||
WINPR_API BOOL InitOnceComplete(LPINIT_ONCE lpInitOnce, DWORD dwFlags, LPVOID lpContext);
|
||||
WINPR_API BOOL InitOnceExecuteOnce(PINIT_ONCE InitOnce, PINIT_ONCE_FN InitFn, PVOID Parameter, LPVOID* Context);
|
||||
WINPR_API VOID InitOnceInitialize(PINIT_ONCE InitOnce);
|
||||
|
||||
/* Slim Reader/Writer (SRW) Lock */
|
||||
|
||||
typedef PVOID RTL_SRWLOCK;
|
||||
@ -303,6 +289,30 @@ WINPR_API BOOL InitializeCriticalSectionEx(LPCRITICAL_SECTION lpCriticalSection,
|
||||
|
||||
#endif
|
||||
|
||||
#if (!defined(_WIN32)) || (defined(_WIN32) && (_WIN32_WINNT < 0x0600))
|
||||
|
||||
/* One-Time Initialization */
|
||||
|
||||
typedef struct _RTL_RUN_ONCE
|
||||
{
|
||||
PVOID Ptr;
|
||||
} RTL_RUN_ONCE, *PRTL_RUN_ONCE;
|
||||
|
||||
#define RTL_RUN_ONCE_INIT { 0 }
|
||||
#define INIT_ONCE_STATIC_INIT RTL_RUN_ONCE_INIT
|
||||
|
||||
typedef RTL_RUN_ONCE INIT_ONCE;
|
||||
typedef PRTL_RUN_ONCE PINIT_ONCE;
|
||||
typedef PRTL_RUN_ONCE LPINIT_ONCE;
|
||||
typedef BOOL CALLBACK (*PINIT_ONCE_FN) (PINIT_ONCE InitOnce, PVOID Parameter, PVOID* Context);
|
||||
|
||||
WINPR_API BOOL InitOnceBeginInitialize(LPINIT_ONCE lpInitOnce, DWORD dwFlags, PBOOL fPending, LPVOID* lpContext);
|
||||
WINPR_API BOOL InitOnceComplete(LPINIT_ONCE lpInitOnce, DWORD dwFlags, LPVOID lpContext);
|
||||
WINPR_API BOOL InitOnceExecuteOnce(PINIT_ONCE InitOnce, PINIT_ONCE_FN InitFn, PVOID Parameter, LPVOID* Context);
|
||||
WINPR_API VOID InitOnceInitialize(PINIT_ONCE InitOnce);
|
||||
|
||||
#endif
|
||||
|
||||
/* Extended API */
|
||||
|
||||
WINPR_API VOID USleep(DWORD dwMicroseconds);
|
||||
|
@ -234,6 +234,15 @@ LONG InterlockedCompareExchange(LONG volatile *Destination, LONG Exchange, LONG
|
||||
#endif
|
||||
}
|
||||
|
||||
PVOID InterlockedCompareExchangePointer(PVOID volatile *Destination, PVOID Exchange, PVOID Comperand)
|
||||
{
|
||||
#ifdef __GNUC__
|
||||
return __sync_val_compare_and_swap(Destination, Comperand, Exchange);
|
||||
#else
|
||||
return 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
#endif /* _WIN32 */
|
||||
|
||||
#if defined(_WIN32) && !defined(WINPR_INTERLOCKED_COMPARE_EXCHANGE64)
|
||||
@ -249,7 +258,7 @@ int static_mutex_lock(volatile HANDLE* static_mutex)
|
||||
if (*static_mutex == NULL)
|
||||
{
|
||||
HANDLE handle = CreateMutex(NULL, FALSE, NULL);
|
||||
|
||||
|
||||
if (InterlockedCompareExchangePointer((PVOID*) static_mutex, (PVOID) handle, NULL) != NULL)
|
||||
CloseHandle(handle);
|
||||
}
|
||||
|
@ -3,6 +3,8 @@
|
||||
* Synchronization Functions
|
||||
*
|
||||
* Copyright 2012 Marc-Andre Moreau <marcandre.moreau@gmail.com>
|
||||
* Copyright 2014 Thincast Technologies GmbH <norbert.federa@thincast.com>
|
||||
* Copyright 2014 Norbert Federa <norbert.federa@thincast.com>
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@ -22,27 +24,68 @@
|
||||
#endif
|
||||
|
||||
#include <winpr/synch.h>
|
||||
#include <winpr/interlocked.h>
|
||||
|
||||
#ifndef _WIN32
|
||||
#if (!defined(_WIN32)) || (defined(_WIN32) && (_WIN32_WINNT < 0x0600))
|
||||
|
||||
BOOL InitOnceBeginInitialize(LPINIT_ONCE lpInitOnce, DWORD dwFlags, PBOOL fPending, LPVOID* lpContext)
|
||||
{
|
||||
return TRUE;
|
||||
fprintf(stderr, "%s: not implemented\n", __FUNCTION__);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
BOOL InitOnceComplete(LPINIT_ONCE lpInitOnce, DWORD dwFlags, LPVOID lpContext)
|
||||
{
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
BOOL InitOnceExecuteOnce(PINIT_ONCE InitOnce, PINIT_ONCE_FN InitFn, PVOID Parameter, LPVOID* Context)
|
||||
{
|
||||
return TRUE;
|
||||
fprintf(stderr, "%s: not implemented\n", __FUNCTION__);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
VOID InitOnceInitialize(PINIT_ONCE InitOnce)
|
||||
{
|
||||
fprintf(stderr, "%s: not implemented\n", __FUNCTION__);
|
||||
}
|
||||
|
||||
BOOL InitOnceExecuteOnce(PINIT_ONCE InitOnce, PINIT_ONCE_FN InitFn, PVOID Parameter, LPVOID* Context)
|
||||
{
|
||||
for (;;)
|
||||
{
|
||||
switch((ULONG_PTR)InitOnce->Ptr & 3)
|
||||
{
|
||||
case 2:
|
||||
/* already completed successfully */
|
||||
return TRUE;
|
||||
|
||||
case 0:
|
||||
/* first time */
|
||||
if (InterlockedCompareExchangePointer(&InitOnce->Ptr, (PVOID)1, (PVOID)0) != (PVOID)0)
|
||||
{
|
||||
/* some other thread was faster */
|
||||
break;
|
||||
}
|
||||
|
||||
/* it's our job to call the init function */
|
||||
if (InitFn(InitOnce, Parameter, Context))
|
||||
{
|
||||
/* success */
|
||||
InitOnce->Ptr = (PVOID)2;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/* the init function returned an error, reset the status */
|
||||
InitOnce->Ptr = (PVOID)0;
|
||||
return FALSE;
|
||||
|
||||
case 1:
|
||||
/* in progress */
|
||||
break;
|
||||
|
||||
default:
|
||||
fprintf(stderr, "%s: internal error\n", __FUNCTION__);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
Sleep(5);
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
@ -5,6 +5,7 @@ set(MODULE_PREFIX "TEST_SYNCH")
|
||||
set(${MODULE_PREFIX}_DRIVER ${MODULE_NAME}.c)
|
||||
|
||||
set(${MODULE_PREFIX}_TESTS
|
||||
TestSynchInit.c
|
||||
TestSynchEvent.c
|
||||
TestSynchMutex.c
|
||||
TestSynchCritical.c
|
||||
|
146
winpr/libwinpr/synch/test/TestSynchInit.c
Normal file
146
winpr/libwinpr/synch/test/TestSynchInit.c
Normal file
@ -0,0 +1,146 @@
|
||||
#include <stdio.h>
|
||||
#include <winpr/crt.h>
|
||||
#include <winpr/synch.h>
|
||||
#include <winpr/thread.h>
|
||||
#include <winpr/interlocked.h>
|
||||
|
||||
#define TEST_NUM_THREADS 100
|
||||
#define TEST_NUM_FAILURES 10
|
||||
|
||||
INIT_ONCE initOnceTest = INIT_ONCE_STATIC_INIT;
|
||||
|
||||
HANDLE hStartEvent = NULL;
|
||||
LONG *pErrors = NULL;
|
||||
LONG *pTestThreadFunctionCalls = NULL;
|
||||
LONG *pTestOnceFunctionCalls = NULL;
|
||||
LONG *pInitOnceExecuteOnceCalls = NULL;
|
||||
|
||||
|
||||
BOOL CALLBACK TestOnceFunction(PINIT_ONCE once, PVOID param, PVOID *context)
|
||||
{
|
||||
LONG calls = InterlockedIncrement(pTestOnceFunctionCalls) - 1;
|
||||
|
||||
/* simulate execution time */
|
||||
Sleep(100 + rand() % 400);
|
||||
|
||||
if (calls < TEST_NUM_FAILURES)
|
||||
{
|
||||
/* simulated error */
|
||||
return FALSE;
|
||||
}
|
||||
if (calls == TEST_NUM_FAILURES)
|
||||
{
|
||||
return TRUE;
|
||||
}
|
||||
fprintf(stderr, "%s: error: called again after success\n", __FUNCTION__);
|
||||
InterlockedIncrement(pErrors);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
DWORD WINAPI TestThreadFunction(LPVOID lpParam)
|
||||
{
|
||||
LONG calls;
|
||||
BOOL ok;
|
||||
InterlockedIncrement(pTestThreadFunctionCalls);
|
||||
if (WaitForSingleObject(hStartEvent, INFINITE) != WAIT_OBJECT_0)
|
||||
{
|
||||
fprintf(stderr, "%s: error: failed to wait for start event\n", __FUNCTION__);
|
||||
InterlockedIncrement(pErrors);
|
||||
return 0;
|
||||
}
|
||||
|
||||
ok = InitOnceExecuteOnce(&initOnceTest, TestOnceFunction, NULL, NULL);
|
||||
calls = InterlockedIncrement(pInitOnceExecuteOnceCalls);
|
||||
if (!ok && calls > TEST_NUM_FAILURES)
|
||||
{
|
||||
fprintf(stderr, "%s: InitOnceExecuteOnce failed unexpectedly\n", __FUNCTION__);
|
||||
InterlockedIncrement(pErrors);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int TestSynchInit(int argc, char* argv[])
|
||||
{
|
||||
HANDLE hThreads[TEST_NUM_THREADS];
|
||||
DWORD dwCreatedThreads = 0;
|
||||
DWORD i;
|
||||
BOOL result = FALSE;
|
||||
|
||||
pErrors = _aligned_malloc(sizeof(LONG), sizeof(LONG));
|
||||
pTestThreadFunctionCalls = _aligned_malloc(sizeof(LONG), sizeof(LONG));
|
||||
pTestOnceFunctionCalls = _aligned_malloc(sizeof(LONG), sizeof(LONG));
|
||||
pInitOnceExecuteOnceCalls = _aligned_malloc(sizeof(LONG), sizeof(LONG));
|
||||
|
||||
if (!pErrors || !pTestThreadFunctionCalls || !pTestOnceFunctionCalls || !pInitOnceExecuteOnceCalls)
|
||||
{
|
||||
fprintf(stderr, "error: _aligned_malloc failed\n");
|
||||
goto out;
|
||||
}
|
||||
|
||||
*pErrors = 0;
|
||||
*pTestThreadFunctionCalls = 0;
|
||||
*pTestOnceFunctionCalls = 0;
|
||||
*pInitOnceExecuteOnceCalls = 0;
|
||||
|
||||
if (!(hStartEvent = CreateEvent(NULL, TRUE, FALSE, NULL)))
|
||||
{
|
||||
fprintf(stderr, "error creating start event\n");
|
||||
InterlockedIncrement(pErrors);
|
||||
goto out;
|
||||
}
|
||||
|
||||
for (i = 0; i < TEST_NUM_THREADS; i++)
|
||||
{
|
||||
if (!(hThreads[i] = CreateThread(NULL, 0, TestThreadFunction, NULL, 0, NULL)))
|
||||
{
|
||||
fprintf(stderr, "error creating thread #%d\n", i);
|
||||
InterlockedIncrement(pErrors);
|
||||
goto out;
|
||||
}
|
||||
dwCreatedThreads++;
|
||||
}
|
||||
|
||||
Sleep(100);
|
||||
SetEvent(hStartEvent);
|
||||
|
||||
for (i = 0; i < dwCreatedThreads; i++)
|
||||
{
|
||||
if (WaitForSingleObject(hThreads[i], INFINITE) != WAIT_OBJECT_0)
|
||||
{
|
||||
fprintf(stderr, "error: error waiting for thread #%d\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:
|
||||
fprintf(stderr, "Test result: %s\n", result ? "OK" : "ERROR");
|
||||
fprintf(stderr, "Error count: %d\n", pErrors ? *pErrors : -1);
|
||||
fprintf(stderr, "Threads created: %u\n", dwCreatedThreads);
|
||||
fprintf(stderr, "TestThreadFunctionCalls: %d\n", pTestThreadFunctionCalls ? *pTestThreadFunctionCalls : -1);
|
||||
fprintf(stderr, "InitOnceExecuteOnceCalls: %d\n", pInitOnceExecuteOnceCalls ? *pInitOnceExecuteOnceCalls : -1);
|
||||
fprintf(stderr, "TestOnceFunctionCalls: %d\n", pTestOnceFunctionCalls ? *pTestOnceFunctionCalls : -1);
|
||||
|
||||
_aligned_free(pErrors);
|
||||
_aligned_free(pTestThreadFunctionCalls);
|
||||
_aligned_free(pTestOnceFunctionCalls);
|
||||
_aligned_free(pInitOnceExecuteOnceCalls);
|
||||
|
||||
CloseHandle(hStartEvent);
|
||||
|
||||
|
||||
for (i = 0; i < dwCreatedThreads; i++)
|
||||
{
|
||||
CloseHandle(hThreads[i]);
|
||||
}
|
||||
|
||||
return (result ? 0 : 1);
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user