blob: 4d77aac5a1bf0a6907d054300abcc447fbdd9107 [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
James Zerne1cbb132018-08-22 14:10:36 -070012#ifndef AOM_AOM_PORTS_AOM_ONCE_H_
13#define AOM_AOM_PORTS_AOM_ONCE_H_
Ehsan Akhgari45bac0c2013-12-09 12:05:38 -050014
Tom Finegan60e653d2018-05-22 11:34:58 -070015#include "config/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 *
Wan-Teh Chang5396c552018-06-29 10:33:50 -070020 * NOTE: This function uses static locks, and can only be
Ralph Giles26355732015-11-13 12:56:34 -080021 * 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 *
Wan-Teh Chang5396c552018-06-29 10:33:50 -070028 * file2.c:
29 * 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 *
Wan-Teh Chang5396c552018-06-29 10:33:50 -070049 * As a static, aom_once_state will be zero-initialized as program start.
Ralph Giles26355732015-11-13 12:56:34 -080050 */
Wan-Teh Chang5396c552018-06-29 10:33:50 -070051static LONG aom_once_state;
52static void aom_once(void (*func)(void)) {
53 /* Try to advance aom_once_state from its initial value of 0 to 1.
clang-format05ce8502016-08-10 18:23:43 -070054 * Only one thread can succeed in doing so.
55 */
Wan-Teh Chang5396c552018-06-29 10:33:50 -070056 if (InterlockedCompareExchange(&aom_once_state, 1, 0) == 0) {
57 /* We're the winning thread, having set aom_once_state to 1.
clang-format05ce8502016-08-10 18:23:43 -070058 * Call our function. */
59 func();
Wan-Teh Chang5396c552018-06-29 10:33:50 -070060 /* Now advance aom_once_state to 2, unblocking any other threads. */
61 InterlockedIncrement(&aom_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 *
Wan-Teh Chang5396c552018-06-29 10:33:50 -070069 * Try to advance aom_once_state from 2 to 2, which is only possible
clang-format05ce8502016-08-10 18:23:43 -070070 * after the winning thead advances it from 1 to 2.
71 */
Wan-Teh Chang5396c552018-06-29 10:33:50 -070072 while (InterlockedCompareExchange(&aom_once_state, 2, 2) != 2) {
clang-format05ce8502016-08-10 18:23:43 -070073 /* 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
Wan-Teh Chang5396c552018-06-29 10:33:50 -070086 /* We've seen aom_once_state advance to 2, so we know func()
87 * has been called. And we've left aom_once_state as we found it,
clang-format05ce8502016-08-10 18:23:43 -070088 * 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>
Wan-Teh Chang5396c552018-06-29 10:33:50 -070098static void aom_once(void (*func)(void)) {
clang-format05ce8502016-08-10 18:23:43 -070099 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>
Wan-Teh Chang5396c552018-06-29 10:33:50 -0700120static void aom_once(void (*func)(void)) {
clang-format05ce8502016-08-10 18:23:43 -0700121 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
Wan-Teh Chang5396c552018-06-29 10:33:50 -0700126/* Default version that performs no synchronization. */
John Koleszara9c75972012-11-08 17:09:30 -0800127
Wan-Teh Chang5396c552018-06-29 10:33:50 -0700128static void aom_once(void (*func)(void)) {
clang-format05ce8502016-08-10 18:23:43 -0700129 static int done;
John Koleszara9c75972012-11-08 17:09:30 -0800130
clang-format05ce8502016-08-10 18:23:43 -0700131 if (!done) {
132 func();
133 done = 1;
134 }
John Koleszara9c75972012-11-08 17:09:30 -0800135}
136#endif
Ehsan Akhgari45bac0c2013-12-09 12:05:38 -0500137
James Zerne1cbb132018-08-22 14:10:36 -0700138#endif // AOM_AOM_PORTS_AOM_ONCE_H_