/src/libvpx/vpx_ports/vpx_once.h
Line | Count | Source |
1 | | /* |
2 | | * Copyright (c) 2015 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_VPX_PORTS_VPX_ONCE_H_ |
12 | | #define VPX_VPX_PORTS_VPX_ONCE_H_ |
13 | | |
14 | | #include "vpx_config.h" |
15 | | |
16 | | /* Implement a function wrapper to guarantee initialization |
17 | | * thread-safety for library singletons. |
18 | | * |
19 | | * NOTE: These functions use static locks, and can only be |
20 | | * used with one common argument per compilation unit. So |
21 | | * |
22 | | * file1.c: |
23 | | * vpx_once(foo); |
24 | | * ... |
25 | | * vpx_once(foo); |
26 | | * |
27 | | * file2.c: |
28 | | * vpx_once(bar); |
29 | | * |
30 | | * will ensure foo() and bar() are each called only once, but in |
31 | | * |
32 | | * file1.c: |
33 | | * vpx_once(foo); |
34 | | * vpx_once(bar): |
35 | | * |
36 | | * bar() will never be called because the lock is used up |
37 | | * by the call to foo(). |
38 | | */ |
39 | | |
40 | | #if CONFIG_MULTITHREAD && defined(_WIN32) |
41 | | #include <windows.h> |
42 | | #include <stdlib.h> |
43 | | /* Declare a per-compilation-unit state variable to track the progress |
44 | | * of calling func() only once. This must be at global scope because |
45 | | * local initializers are not thread-safe in MSVC prior to Visual |
46 | | * Studio 2015. |
47 | | * |
48 | | * As a static, once_state will be zero-initialized as program start. |
49 | | */ |
50 | | static LONG once_state; |
51 | | static void once(void (*func)(void)) { |
52 | | /* Try to advance once_state from its initial value of 0 to 1. |
53 | | * Only one thread can succeed in doing so. |
54 | | */ |
55 | | if (InterlockedCompareExchange(&once_state, 1, 0) == 0) { |
56 | | /* We're the winning thread, having set once_state to 1. |
57 | | * Call our function. */ |
58 | | func(); |
59 | | /* Now advance once_state to 2, unblocking any other threads. */ |
60 | | InterlockedIncrement(&once_state); |
61 | | return; |
62 | | } |
63 | | |
64 | | /* We weren't the winning thread, but we want to block on |
65 | | * the state variable so we don't return before func() |
66 | | * has finished executing elsewhere. |
67 | | * |
68 | | * Try to advance once_state from 2 to 2, which is only possible |
69 | | * after the winning thead advances it from 1 to 2. |
70 | | */ |
71 | | while (InterlockedCompareExchange(&once_state, 2, 2) != 2) { |
72 | | /* State isn't yet 2. Try again. |
73 | | * |
74 | | * We are used for singleton initialization functions, |
75 | | * which should complete quickly. Contention will likewise |
76 | | * be rare, so it's worthwhile to use a simple but cpu- |
77 | | * intensive busy-wait instead of successive backoff, |
78 | | * waiting on a kernel object, or another heavier-weight scheme. |
79 | | * |
80 | | * We can at least yield our timeslice. |
81 | | */ |
82 | | Sleep(0); |
83 | | } |
84 | | |
85 | | /* We've seen once_state advance to 2, so we know func() |
86 | | * has been called. And we've left once_state as we found it, |
87 | | * so other threads will have the same experience. |
88 | | * |
89 | | * It's safe to return now. |
90 | | */ |
91 | | return; |
92 | | } |
93 | | |
94 | | #elif CONFIG_MULTITHREAD && HAVE_PTHREAD_H |
95 | | #include <pthread.h> |
96 | 23.1k | static void once(void (*func)(void)) { |
97 | 23.1k | static pthread_once_t lock = PTHREAD_ONCE_INIT; |
98 | 23.1k | pthread_once(&lock, func); |
99 | 23.1k | } Line | Count | Source | 96 | 5.03k | static void once(void (*func)(void)) { | 97 | 5.03k | static pthread_once_t lock = PTHREAD_ONCE_INIT; | 98 | 5.03k | pthread_once(&lock, func); | 99 | 5.03k | } |
Line | Count | Source | 96 | 3.06k | static void once(void (*func)(void)) { | 97 | 3.06k | static pthread_once_t lock = PTHREAD_ONCE_INIT; | 98 | 3.06k | pthread_once(&lock, func); | 99 | 3.06k | } |
Line | Count | Source | 96 | 5.03k | static void once(void (*func)(void)) { | 97 | 5.03k | static pthread_once_t lock = PTHREAD_ONCE_INIT; | 98 | 5.03k | pthread_once(&lock, func); | 99 | 5.03k | } |
Line | Count | Source | 96 | 5.03k | static void once(void (*func)(void)) { | 97 | 5.03k | static pthread_once_t lock = PTHREAD_ONCE_INIT; | 98 | 5.03k | pthread_once(&lock, func); | 99 | 5.03k | } |
Line | Count | Source | 96 | 5.03k | static void once(void (*func)(void)) { | 97 | 5.03k | static pthread_once_t lock = PTHREAD_ONCE_INIT; | 98 | 5.03k | pthread_once(&lock, func); | 99 | 5.03k | } |
Line | Count | Source | 96 | 1 | static void once(void (*func)(void)) { | 97 | 1 | static pthread_once_t lock = PTHREAD_ONCE_INIT; | 98 | 1 | pthread_once(&lock, func); | 99 | 1 | } |
Line | Count | Source | 96 | 1 | static void once(void (*func)(void)) { | 97 | 1 | static pthread_once_t lock = PTHREAD_ONCE_INIT; | 98 | 1 | pthread_once(&lock, func); | 99 | 1 | } |
Line | Count | Source | 96 | 1 | static void once(void (*func)(void)) { | 97 | 1 | static pthread_once_t lock = PTHREAD_ONCE_INIT; | 98 | 1 | pthread_once(&lock, func); | 99 | 1 | } |
|
100 | | |
101 | | #else |
102 | | /* No-op version that performs no synchronization. *_rtcd() is idempotent, |
103 | | * so as long as your platform provides atomic loads/stores of pointers |
104 | | * no synchronization is strictly necessary. |
105 | | */ |
106 | | |
107 | | static void once(void (*func)(void)) { |
108 | | static volatile int done; |
109 | | |
110 | | if (!done) { |
111 | | func(); |
112 | | done = 1; |
113 | | } |
114 | | } |
115 | | #endif |
116 | | |
117 | | #endif // VPX_VPX_PORTS_VPX_ONCE_H_ |