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 && defined(__OS2__)
77 #define INCL_DOS
78 #include <os2.h>
once(void (* func)(void))79 static 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>
once(void (* func)(void))105 static 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. *_rtcd() is idempotent,
114  * so as long as your platform provides atomic loads/stores of pointers
115  * no synchronization is strictly necessary.
116  */
117 
once(void (* func)(void))118 static 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