From fae8f6fbf2dabef83091f82df0134c26182bdd1b Mon Sep 17 00:00:00 2001 From: Norbert Federa Date: Thu, 24 Jul 2014 21:07:44 +0200 Subject: [PATCH] winpr/sync: Added InitOnceExecuteOnce plus CTest --- winpr/include/winpr/interlocked.h | 2 + winpr/include/winpr/synch.h | 42 +++++---- winpr/libwinpr/interlocked/interlocked.c | 11 ++- winpr/libwinpr/synch/init.c | 55 +++++++++-- winpr/libwinpr/synch/test/CMakeLists.txt | 1 + winpr/libwinpr/synch/test/TestSynchInit.c | 146 ++++++++++++++++++++++++++++++ 6 files changed, 234 insertions(+), 23 deletions(-) create mode 100644 winpr/libwinpr/synch/test/TestSynchInit.c diff --git a/winpr/include/winpr/interlocked.h b/winpr/include/winpr/interlocked.h index 8e27f83..83c4625 100644 --- a/winpr/include/winpr/interlocked.h +++ b/winpr/include/winpr/interlocked.h @@ -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))) diff --git a/winpr/include/winpr/synch.h b/winpr/include/winpr/synch.h index 75213a8..fce97e9 100644 --- a/winpr/include/winpr/synch.h +++ b/winpr/include/winpr/synch.h @@ -3,6 +3,8 @@ * Synchronization Functions * * Copyright 2012 Marc-Andre Moreau + * Copyright 2014 Thincast Technologies GmbH + * Copyright 2014 Norbert Federa * * 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); diff --git a/winpr/libwinpr/interlocked/interlocked.c b/winpr/libwinpr/interlocked/interlocked.c index 13b597f..0b8c0d4 100644 --- a/winpr/libwinpr/interlocked/interlocked.c +++ b/winpr/libwinpr/interlocked/interlocked.c @@ -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); } diff --git a/winpr/libwinpr/synch/init.c b/winpr/libwinpr/synch/init.c index 88d32c2..89c6427 100644 --- a/winpr/libwinpr/synch/init.c +++ b/winpr/libwinpr/synch/init.c @@ -3,6 +3,8 @@ * Synchronization Functions * * Copyright 2012 Marc-Andre Moreau + * Copyright 2014 Thincast Technologies GmbH + * Copyright 2014 Norbert Federa * * 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 +#include -#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; + fprintf(stderr, "%s: not implemented\n", __FUNCTION__); + return FALSE; } -BOOL InitOnceExecuteOnce(PINIT_ONCE InitOnce, PINIT_ONCE_FN InitFn, PVOID Parameter, LPVOID* Context) +VOID InitOnceInitialize(PINIT_ONCE InitOnce) { - return TRUE; + fprintf(stderr, "%s: not implemented\n", __FUNCTION__); } -VOID InitOnceInitialize(PINIT_ONCE InitOnce) +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 diff --git a/winpr/libwinpr/synch/test/CMakeLists.txt b/winpr/libwinpr/synch/test/CMakeLists.txt index 34d7d3a..0309fb9 100644 --- a/winpr/libwinpr/synch/test/CMakeLists.txt +++ b/winpr/libwinpr/synch/test/CMakeLists.txt @@ -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 diff --git a/winpr/libwinpr/synch/test/TestSynchInit.c b/winpr/libwinpr/synch/test/TestSynchInit.c new file mode 100644 index 0000000..efcd9c3 --- /dev/null +++ b/winpr/libwinpr/synch/test/TestSynchInit.c @@ -0,0 +1,146 @@ +#include +#include +#include +#include +#include + +#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); +} -- 2.7.4