2 * Copyright (c) 2016, Alliance for Open Media. All rights reserved
4 * This source code is subject to the terms of the BSD 2 Clause License and
5 * the Alliance for Open Media Patent License 1.0. If the BSD 2 Clause License
6 * was not distributed with this source code in the LICENSE file, you can
7 * obtain it at www.aomedia.org/license/software. If the Alliance for Open
8 * Media Patent License 1.0 was not distributed with this source code in the
9 * PATENTS file, you can obtain it at www.aomedia.org/license/patent.
12 #ifndef AOM_PORTS_AOM_ONCE_H_
13 #define AOM_PORTS_AOM_ONCE_H_
15 #include "aom_config.h"
17 /* Implement a function wrapper to guarantee initialization
18 * thread-safety for library singletons.
20 * NOTE: These functions use static locks, and can only be
21 * used with one common argument per compilation unit. So
31 * will ensure foo() and bar() are each called only once, but in
37 * bar() will never be called because the lock is used up
38 * by the call to foo().
41 #if CONFIG_MULTITHREAD && defined(_WIN32)
44 /* Declare a per-compilation-unit state variable to track the progress
45 * of calling func() only once. This must be at global scope because
46 * local initializers are not thread-safe in MSVC prior to Visual
49 * As a static, once_state will be zero-initialized as program start.
51 static LONG once_state
;
52 static void once(void (*func
)(void)) {
53 /* Try to advance once_state from its initial value of 0 to 1.
54 * Only one thread can succeed in doing so.
56 if (InterlockedCompareExchange(&once_state
, 1, 0) == 0) {
57 /* We're the winning thread, having set once_state to 1.
58 * Call our function. */
60 /* Now advance once_state to 2, unblocking any other threads. */
61 InterlockedIncrement(&once_state
);
65 /* We weren't the winning thread, but we want to block on
66 * the state variable so we don't return before func()
67 * has finished executing elsewhere.
69 * Try to advance once_state from 2 to 2, which is only possible
70 * after the winning thead advances it from 1 to 2.
72 while (InterlockedCompareExchange(&once_state
, 2, 2) != 2) {
73 /* State isn't yet 2. Try again.
75 * We are used for singleton initialization functions,
76 * which should complete quickly. Contention will likewise
77 * be rare, so it's worthwhile to use a simple but cpu-
78 * intensive busy-wait instead of successive backoff,
79 * waiting on a kernel object, or another heavier-weight scheme.
81 * We can at least yield our timeslice.
86 /* We've seen once_state advance to 2, so we know func()
87 * has been called. And we've left once_state as we found it,
88 * so other threads will have the same experience.
90 * It's safe to return now.
95 #elif CONFIG_MULTITHREAD && defined(__OS2__)
98 static void once(void (*func
)(void)) {
101 /* If the initialization is complete, return early. */
104 /* Causes all other threads in the process to block themselves
105 * and give up their time slice.
114 /* Restores normal thread dispatching for the current process. */
118 #elif CONFIG_MULTITHREAD && HAVE_PTHREAD_H
120 static void once(void (*func
)(void)) {
121 static pthread_once_t lock
= PTHREAD_ONCE_INIT
;
122 pthread_once(&lock
, func
);
126 /* No-op version that performs no synchronization. *_rtcd() is idempotent,
127 * so as long as your platform provides atomic loads/stores of pointers
128 * no synchronization is strictly necessary.
131 static void once(void (*func
)(void)) {
141 #endif // AOM_PORTS_AOM_ONCE_H_