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>
once(void (* func)(void))19 static 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>
once(void (* func)(void))78 static 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 
once(void (* func)(void))91 static 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