michael@0: /* michael@0: * Copyright (c) 2011 The WebM project authors. All Rights Reserved. michael@0: * michael@0: * Use of this source code is governed by a BSD-style license michael@0: * that can be found in the LICENSE file in the root of the source michael@0: * tree. An additional intellectual property rights grant can be found michael@0: * in the file PATENTS. All contributing project authors may michael@0: * be found in the AUTHORS file in the root of the source tree. michael@0: */ michael@0: #ifndef VPX_ONCE_H michael@0: #define VPX_ONCE_H michael@0: michael@0: #include "vpx_config.h" michael@0: michael@0: #if CONFIG_MULTITHREAD && defined(_WIN32) michael@0: #include michael@0: #include michael@0: static void once(void (*func)(void)) michael@0: { michael@0: static CRITICAL_SECTION *lock; michael@0: static LONG waiters; michael@0: static int done; michael@0: void *lock_ptr = &lock; michael@0: michael@0: /* If the initialization is complete, return early. This isn't just an michael@0: * optimization, it prevents races on the destruction of the global michael@0: * lock. michael@0: */ michael@0: if(done) michael@0: return; michael@0: michael@0: InterlockedIncrement(&waiters); michael@0: michael@0: /* Get a lock. We create one and try to make it the one-true-lock, michael@0: * throwing it away if we lost the race. michael@0: */ michael@0: michael@0: { michael@0: /* Scope to protect access to new_lock */ michael@0: CRITICAL_SECTION *new_lock = malloc(sizeof(CRITICAL_SECTION)); michael@0: InitializeCriticalSection(new_lock); michael@0: if (InterlockedCompareExchangePointer(lock_ptr, new_lock, NULL) != NULL) michael@0: { michael@0: DeleteCriticalSection(new_lock); michael@0: free(new_lock); michael@0: } michael@0: } michael@0: michael@0: /* At this point, we have a lock that can be synchronized on. We don't michael@0: * care which thread actually performed the allocation. michael@0: */ michael@0: michael@0: EnterCriticalSection(lock); michael@0: michael@0: if (!done) michael@0: { michael@0: func(); michael@0: done = 1; michael@0: } michael@0: michael@0: LeaveCriticalSection(lock); michael@0: michael@0: /* Last one out should free resources. The destructed objects are michael@0: * protected by checking if(done) above. michael@0: */ michael@0: if(!InterlockedDecrement(&waiters)) michael@0: { michael@0: DeleteCriticalSection(lock); michael@0: free(lock); michael@0: lock = NULL; michael@0: } michael@0: } michael@0: michael@0: michael@0: #elif CONFIG_MULTITHREAD && HAVE_PTHREAD_H michael@0: #include michael@0: static void once(void (*func)(void)) michael@0: { michael@0: static pthread_once_t lock = PTHREAD_ONCE_INIT; michael@0: pthread_once(&lock, func); michael@0: } michael@0: michael@0: michael@0: #else michael@0: /* No-op version that performs no synchronization. vp8_rtcd() is idempotent, michael@0: * so as long as your platform provides atomic loads/stores of pointers michael@0: * no synchronization is strictly necessary. michael@0: */ michael@0: michael@0: static void once(void (*func)(void)) michael@0: { michael@0: static int done; michael@0: michael@0: if(!done) michael@0: { michael@0: func(); michael@0: done = 1; michael@0: } michael@0: } michael@0: #endif michael@0: michael@0: #endif