X-Git-Url: https://pd.if.org/git/?a=blobdiff_plain;f=platform%2Fwin32%2Ffunctions%2Fthreads%2Fcall_once.c;fp=platform%2Fwin32%2Ffunctions%2Fthreads%2Fcall_once.c;h=675e00029ecc436f0b4550c3e9ede014c95ab219;hb=639bad513ab9399ed9a8c588a476a10dbe6c9478;hp=0000000000000000000000000000000000000000;hpb=008908f7a61acf9df1248c005378a3c4b4393547;p=pdclib diff --git a/platform/win32/functions/threads/call_once.c b/platform/win32/functions/threads/call_once.c new file mode 100644 index 0000000..675e000 --- /dev/null +++ b/platform/win32/functions/threads/call_once.c @@ -0,0 +1,79 @@ +#ifndef REGTEST +#include +#include + +static volatile HANDLE onceHandle = NULL; + +static void initOnceHandle( _PDCLIB_once_flag *flag ) +{ + HANDLE tOnceHandle = CreateEvent(NULL, TRUE, TRUE, NULL); + HANDLE oldVal + = InterlockedCompareExchangePointer(&flag->_Handle, tOnceHandle, NULL); + if(oldVal != NULL) + CloseHandle(tOnceHandle); +} + +void _PDCLIB_call_once(_PDCLIB_once_flag *flag, void (*func)(void)) +{ + if(!flag->_Handle) initOnceHandle(flag); + + long oldVal = InterlockedCompareExchange(&flag->_State, 1, -1); + for(;;) { + if(oldVal == 0) { + // Initialized + return; + } else if(oldVal == -1) { + // We are doing the initialization + func(); + if(InterlockedDecrement(&flag->_State) == 0) + CloseHandle(flag->_Handle); + SetEvent(flag->_Handle); + return; + } else { + // Somebody else is initializing - we are waiting + long newOldVal = InterlockedCompareExchange(&flag->_State, oldVal, + oldVal+1); + if(newOldVal == oldVal) { + // We incremented the "waiters" counter + if(WaitForSingleObject(flag->_Handle, INFINITE) != WAIT_OBJECT_0) + abort(); + if(InterlockedDecrement(&flag->_State) == 0) + CloseHandle(flag->_Handle); + return; + } else { + oldVal = newOldVal; + continue; + } + } + } +} +#endif + +#ifdef TEST +#include <_PDCLIB_test.h> + +#ifndef REGTEST +static int count = 0; +static once_flag once = ONCE_FLAG_INIT; + +static void do_once(void) +{ + count++; +} +#endif + +int main( void ) +{ +#ifndef REGTEST + TESTCASE(count == 0); + call_once(&once, do_once); + TESTCASE(count == 1); + call_once(&once, do_once); + TESTCASE(count == 1); + do_once(); + TESTCASE(count == 2); +#endif + return TEST_RESULTS; +} + +#endif