#define PTHREAD_ONCE_INIT {0, 0, 0, 0} enum ptw32_once_state { PTW32_ONCE_INIT = 0x0, PTW32_ONCE_STARTED = 0x1, PTW32_ONCE_DONE = 0x2 }; struct pthread_once_t_ { int state; int reserved; int numSemaphoreUsers; HANDLE semaphore; }; static void PTW32_CDECL ptw32_once_on_init_cancel (void * arg) { pthread_once_t * once_control = (pthread_once_t *) arg; (void) PTW32_INTERLOCKED_EXCHANGE((LPLONG)&once_control->state, (LONG)PTW32_ONCE_INIT); if (InterlockedExchangeAdd((LPLONG)&once_control->semaphore, 0L)) /* MBR fence */ { ReleaseSemaphore(once_control->semaphore, 1, NULL); } } static int ptw32_once (pthread_once_t * once_control, void (*init_routine) (void)) { int state; HANDLE sema; while ((state = PTW32_INTERLOCKED_COMPARE_EXCHANGE( (PTW32_INTERLOCKED_LPLONG)&once_control->state, (PTW32_INTERLOCKED_LONG)PTW32_ONCE_STARTED, (PTW32_INTERLOCKED_LONG)PTW32_ONCE_INIT)) != PTW32_ONCE_DONE) { if (PTW32_ONCE_INIT == state) { #ifdef _MSC_VER #pragma inline_depth(0) #endif pthread_cleanup_push(ptw32_once_on_init_cancel, (void *) once_control); (*init_routine)(); pthread_cleanup_pop(0); #ifdef _MSC_VER #pragma inline_depth() #endif (void) PTW32_INTERLOCKED_EXCHANGE((LPLONG)&once_control->state, (LONG)PTW32_ONCE_DONE); /* * we didn't create the semaphore. * it is only there if there is someone waiting. */ if (InterlockedExchangeAdd((LPLONG)&once_control->semaphore, 0L)) /* MBR fence */ { ReleaseSemaphore(once_control->semaphore, once_control->numSemaphoreUsers, NULL); } } else { InterlockedIncrement((LPLONG)&once_control->numSemaphoreUsers); if (!InterlockedExchangeAdd((LPLONG)&once_control->semaphore, 0L)) /* MBR fence */ { sema = CreateSemaphore(NULL, 0, INT_MAX, NULL); if (PTW32_INTERLOCKED_COMPARE_EXCHANGE( (PTW32_INTERLOCKED_LPLONG)&once_control->semaphore, (PTW32_INTERLOCKED_LONG)sema, (PTW32_INTERLOCKED_LONG)0)) { CloseHandle(sema); } } /* * Check 'state' again in case the initting thread has finished or cancelled and left before seeing that there was a semaphore. */ if (InterlockedExchangeAdd((LPLONG)&once_control->state, 0L) == PTW32_ONCE_STARTED) { WaitForSingleObject(once_control->semaphore, INFINITE); } InterlockedDecrement((LPLONG)&once_control->numSemaphoreUsers); } } if (0 == InterlockedExchangeAdd((LPLONG)&once_control->numSemaphoreUsers, 0)) /* MBR */ { if ((sema = (HANDLE) PTW32_INTERLOCKED_EXCHANGE( (LPLONG)&once_control->semaphore, (LONG)0))) { CloseHandle(sema); } } return 0; } int pthread_once (pthread_once_t * once_control, void (*init_routine) (void)) { int result; if (once_control == NULL || init_routine == NULL) { result = EINVAL; } else { if (InterlockedExchangeAdd((LPLONG)&once_control->state, 0L) != PTW32_ONCE_DONE) /* MBR */ { result = ptw32_once(once_control, init_routine); } else { result = 0; } } return (result); } /* pthread_once */