1/*
2 *  Copyright (c) 2011 The WebM project authors. All Rights Reserved.
3 *
4 *  Use of this source code is governed by a BSD-style license
5 *  that can be found in the LICENSE file in the root of the source
6 *  tree. An additional intellectual property rights grant can be found
7 *  in the file PATENTS.  All contributing project authors may
8 *  be found in the AUTHORS file in the root of the source tree.
9 */
10
11#ifndef VPX_PORTS_VPX_ONCE_H_
12#define VPX_PORTS_VPX_ONCE_H_
13
14#include "vpx_config.h"
15
16#if CONFIG_MULTITHREAD && defined(_WIN32)
17#include <windows.h>
18#include <stdlib.h>
19static void once(void (*func)(void))
20{
21    static CRITICAL_SECTION *lock;
22    static LONG waiters;
23    static int done;
24    void *lock_ptr = &lock;
25
26    /* If the initialization is complete, return early. This isn't just an
27     * optimization, it prevents races on the destruction of the global
28     * lock.
29     */
30    if(done)
31        return;
32
33    InterlockedIncrement(&waiters);
34
35    /* Get a lock. We create one and try to make it the one-true-lock,
36     * throwing it away if we lost the race.
37     */
38
39    {
40        /* Scope to protect access to new_lock */
41        CRITICAL_SECTION *new_lock = malloc(sizeof(CRITICAL_SECTION));
42        InitializeCriticalSection(new_lock);
43        if (InterlockedCompareExchangePointer(lock_ptr, new_lock, NULL) != NULL)
44        {
45            DeleteCriticalSection(new_lock);
46            free(new_lock);
47        }
48    }
49
50    /* At this point, we have a lock that can be synchronized on. We don't
51     * care which thread actually performed the allocation.
52     */
53
54    EnterCriticalSection(lock);
55
56    if (!done)
57    {
58        func();
59        done = 1;
60    }
61
62    LeaveCriticalSection(lock);
63
64    /* Last one out should free resources. The destructed objects are
65     * protected by checking if(done) above.
66     */
67    if(!InterlockedDecrement(&waiters))
68    {
69        DeleteCriticalSection(lock);
70        free(lock);
71        lock = NULL;
72    }
73}
74
75
76#elif CONFIG_MULTITHREAD && HAVE_PTHREAD_H
77#include <pthread.h>
78static void once(void (*func)(void))
79{
80    static pthread_once_t lock = PTHREAD_ONCE_INIT;
81    pthread_once(&lock, func);
82}
83
84
85#else
86/* No-op version that performs no synchronization. vp8_rtcd() is idempotent,
87 * so as long as your platform provides atomic loads/stores of pointers
88 * no synchronization is strictly necessary.
89 */
90
91static void once(void (*func)(void))
92{
93    static int done;
94
95    if(!done)
96    {
97        func();
98        done = 1;
99    }
100}
101#endif
102
103#endif  // VPX_PORTS_VPX_ONCE_H_
104