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 && defined(__OS2__)
77#define INCL_DOS
78#include <os2.h>
79static void once(void (*func)(void))
80{
81    static int done;
82
83    /* If the initialization is complete, return early. */
84    if(done)
85        return;
86
87    /* Causes all other threads in the process to block themselves
88     * and give up their time slice.
89     */
90    DosEnterCritSec();
91
92    if (!done)
93    {
94        func();
95        done = 1;
96    }
97
98    /* Restores normal thread dispatching for the current process. */
99    DosExitCritSec();
100}
101
102
103#elif CONFIG_MULTITHREAD && HAVE_PTHREAD_H
104#include <pthread.h>
105static void once(void (*func)(void))
106{
107    static pthread_once_t lock = PTHREAD_ONCE_INIT;
108    pthread_once(&lock, func);
109}
110
111
112#else
113/* No-op version that performs no synchronization. vp8_rtcd() is idempotent,
114 * so as long as your platform provides atomic loads/stores of pointers
115 * no synchronization is strictly necessary.
116 */
117
118static void once(void (*func)(void))
119{
120    static int done;
121
122    if(!done)
123    {
124        func();
125        done = 1;
126    }
127}
128#endif
129
130#endif  // VPX_PORTS_VPX_ONCE_H_
131