blob: 3cfd2fd95824f950023e486540e5f2552662cf72 [file] [log] [blame]
John Koleszara9c75972012-11-08 17:09:30 -08001/*
Yaowu Xu9c01aa12016-09-01 14:32:49 -07002 * Copyright (c) 2016, Alliance for Open Media. All rights reserved
John Koleszara9c75972012-11-08 17:09:30 -08003 *
Yaowu Xu9c01aa12016-09-01 14:32:49 -07004 * 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.
John Koleszara9c75972012-11-08 17:09:30 -080010 */
Ehsan Akhgari45bac0c2013-12-09 12:05:38 -050011
Yaowu Xuf883b422016-08-30 14:01:10 -070012#ifndef AOM_PORTS_AOM_ONCE_H_
13#define AOM_PORTS_AOM_ONCE_H_
Ehsan Akhgari45bac0c2013-12-09 12:05:38 -050014
Yaowu Xuf883b422016-08-30 14:01:10 -070015#include "aom_config.h"
John Koleszara9c75972012-11-08 17:09:30 -080016
Ralph Giles26355732015-11-13 12:56:34 -080017/* Implement a function wrapper to guarantee initialization
18 * thread-safety for library singletons.
19 *
20 * NOTE: These functions use static locks, and can only be
21 * used with one common argument per compilation unit. So
22 *
23 * file1.c:
Yaowu Xuf883b422016-08-30 14:01:10 -070024 * aom_once(foo);
Ralph Giles26355732015-11-13 12:56:34 -080025 * ...
Yaowu Xuf883b422016-08-30 14:01:10 -070026 * aom_once(foo);
Ralph Giles26355732015-11-13 12:56:34 -080027 *
28 * file2.c:
Yaowu Xuf883b422016-08-30 14:01:10 -070029 * aom_once(bar);
Ralph Giles26355732015-11-13 12:56:34 -080030 *
31 * will ensure foo() and bar() are each called only once, but in
32 *
33 * file1.c:
Yaowu Xuf883b422016-08-30 14:01:10 -070034 * aom_once(foo);
35 * aom_once(bar):
Ralph Giles26355732015-11-13 12:56:34 -080036 *
37 * bar() will never be called because the lock is used up
38 * by the call to foo().
39 */
40
John Koleszara9c75972012-11-08 17:09:30 -080041#if CONFIG_MULTITHREAD && defined(_WIN32)
42#include <windows.h>
43#include <stdlib.h>
Ralph Giles26355732015-11-13 12:56:34 -080044/* 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
47 * Studio 2015.
48 *
49 * As a static, once_state will be zero-initialized as program start.
50 */
51static LONG once_state;
clang-format05ce8502016-08-10 18:23:43 -070052static 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.
55 */
56 if (InterlockedCompareExchange(&once_state, 1, 0) == 0) {
57 /* We're the winning thread, having set once_state to 1.
58 * Call our function. */
59 func();
60 /* Now advance once_state to 2, unblocking any other threads. */
61 InterlockedIncrement(&once_state);
Ralph Giles26355732015-11-13 12:56:34 -080062 return;
clang-format05ce8502016-08-10 18:23:43 -070063 }
John Koleszara9c75972012-11-08 17:09:30 -080064
clang-format05ce8502016-08-10 18:23:43 -070065 /* 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.
68 *
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.
71 */
72 while (InterlockedCompareExchange(&once_state, 2, 2) != 2) {
73 /* State isn't yet 2. Try again.
74 *
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.
80 *
81 * We can at least yield our timeslice.
82 */
83 Sleep(0);
84 }
85
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.
89 *
90 * It's safe to return now.
91 */
92 return;
93}
John Koleszara9c75972012-11-08 17:09:30 -080094
KO Myung-Hunf9f996b2014-07-26 14:53:59 +090095#elif CONFIG_MULTITHREAD && defined(__OS2__)
96#define INCL_DOS
97#include <os2.h>
clang-format05ce8502016-08-10 18:23:43 -070098static void once(void (*func)(void)) {
99 static int done;
KO Myung-Hunf9f996b2014-07-26 14:53:59 +0900100
clang-format05ce8502016-08-10 18:23:43 -0700101 /* If the initialization is complete, return early. */
102 if (done) return;
KO Myung-Hunf9f996b2014-07-26 14:53:59 +0900103
clang-format05ce8502016-08-10 18:23:43 -0700104 /* Causes all other threads in the process to block themselves
105 * and give up their time slice.
106 */
107 DosEnterCritSec();
KO Myung-Hunf9f996b2014-07-26 14:53:59 +0900108
clang-format05ce8502016-08-10 18:23:43 -0700109 if (!done) {
110 func();
111 done = 1;
112 }
KO Myung-Hunf9f996b2014-07-26 14:53:59 +0900113
clang-format05ce8502016-08-10 18:23:43 -0700114 /* Restores normal thread dispatching for the current process. */
115 DosExitCritSec();
KO Myung-Hunf9f996b2014-07-26 14:53:59 +0900116}
117
John Koleszara9c75972012-11-08 17:09:30 -0800118#elif CONFIG_MULTITHREAD && HAVE_PTHREAD_H
119#include <pthread.h>
clang-format05ce8502016-08-10 18:23:43 -0700120static void once(void (*func)(void)) {
121 static pthread_once_t lock = PTHREAD_ONCE_INIT;
122 pthread_once(&lock, func);
John Koleszara9c75972012-11-08 17:09:30 -0800123}
124
John Koleszara9c75972012-11-08 17:09:30 -0800125#else
Johann14ef4ae2015-04-15 09:27:00 -0400126/* No-op version that performs no synchronization. *_rtcd() is idempotent,
John Koleszara9c75972012-11-08 17:09:30 -0800127 * so as long as your platform provides atomic loads/stores of pointers
128 * no synchronization is strictly necessary.
129 */
130
clang-format05ce8502016-08-10 18:23:43 -0700131static void once(void (*func)(void)) {
132 static int done;
John Koleszara9c75972012-11-08 17:09:30 -0800133
clang-format05ce8502016-08-10 18:23:43 -0700134 if (!done) {
135 func();
136 done = 1;
137 }
John Koleszara9c75972012-11-08 17:09:30 -0800138}
139#endif
Ehsan Akhgari45bac0c2013-12-09 12:05:38 -0500140
Yaowu Xuf883b422016-08-30 14:01:10 -0700141#endif // AOM_PORTS_AOM_ONCE_H_