1 /*
2 * Copyright (c) 2017 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_UTIL_VPX_ATOMICS_H_
12 #define VPX_VPX_UTIL_VPX_ATOMICS_H_
13
14 #include "./vpx_config.h"
15
16 #ifdef __cplusplus
17 extern "C" {
18 #endif // __cplusplus
19
20 #if CONFIG_OS_SUPPORT && CONFIG_MULTITHREAD
21
22 // Look for built-in atomic support. We cannot use <stdatomic.h> or <atomic>
23 // since neither is guaranteed to exist on both C and C++ platforms, and we need
24 // to back the atomic type with the same type (g++ needs to be able to use
25 // gcc-built code). g++ 6 doesn't support _Atomic as a keyword and can't use the
26 // stdatomic.h header. Even if both <stdatomic.h> and <atomic> existed it's not
27 // guaranteed that atomic_int is the same type as std::atomic_int.
28 // See https://gcc.gnu.org/bugzilla/show_bug.cgi?id=60932#c13.
29 #if !defined(__has_builtin)
30 #define __has_builtin(x) 0 // Compatibility with non-clang compilers.
31 #endif // !defined(__has_builtin)
32
33 #if (__has_builtin(__atomic_load_n)) || \
34 (defined(__GNUC__) && \
35 (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 7)))
36 // For GCC >= 4.7 and Clang versions that support __atomic builtins, use those.
37 #define VPX_USE_ATOMIC_BUILTINS
38 #else
39 // Use platform-specific asm barriers.
40 #if defined(_MSC_VER)
41 // TODO(pbos): This assumes that newer versions of MSVC are building with the
42 // default /volatile:ms (or older, where this is always true. Consider adding
43 // support for using <atomic> instead of stdatomic.h when building C++11 under
44 // MSVC. It's unclear what to do for plain C under /volatile:iso (inline asm?),
45 // there're no explicit Interlocked* functions for only storing or loading
46 // (presumably because volatile has historically implied that on MSVC).
47 //
48 // For earlier versions of MSVC or the default /volatile:ms volatile int are
49 // acquire/release and require no barrier.
50 #define vpx_atomic_memory_barrier() \
51 do { \
52 } while (0)
53 #else
54 #if ARCH_X86 || ARCH_X86_64
55 // Use a compiler barrier on x86, no runtime penalty.
56 #define vpx_atomic_memory_barrier() __asm__ __volatile__("" ::: "memory")
57 #elif ARCH_ARM
58 #define vpx_atomic_memory_barrier() __asm__ __volatile__("dmb ish" ::: "memory")
59 #elif ARCH_MIPS
60 #define vpx_atomic_memory_barrier() __asm__ __volatile__("sync" ::: "memory")
61 #else
62 #error Unsupported architecture!
63 #endif // ARCH_X86 || ARCH_X86_64
64 #endif // defined(_MSC_VER)
65 #endif // atomic builtin availability check
66
67 // These are wrapped in a struct so that they are not easily accessed directly
68 // on any platform (to discourage programmer errors by setting values directly).
69 // This primitive MUST be initialized using vpx_atomic_init or VPX_ATOMIC_INIT
70 // (NOT memset) and accessed through vpx_atomic_ functions.
71 typedef struct vpx_atomic_int {
72 volatile int value;
73 } vpx_atomic_int;
74
75 #define VPX_ATOMIC_INIT(num) \
76 { num }
77
78 // Initialization of an atomic int, not thread safe.
vpx_atomic_init(vpx_atomic_int * atomic,int value)79 static INLINE void vpx_atomic_init(vpx_atomic_int *atomic, int value) {
80 atomic->value = value;
81 }
82
vpx_atomic_store_release(vpx_atomic_int * atomic,int value)83 static INLINE void vpx_atomic_store_release(vpx_atomic_int *atomic, int value) {
84 #if defined(VPX_USE_ATOMIC_BUILTINS)
85 __atomic_store_n(&atomic->value, value, __ATOMIC_RELEASE);
86 #else
87 vpx_atomic_memory_barrier();
88 atomic->value = value;
89 #endif // defined(VPX_USE_ATOMIC_BUILTINS)
90 }
91
vpx_atomic_load_acquire(const vpx_atomic_int * atomic)92 static INLINE int vpx_atomic_load_acquire(const vpx_atomic_int *atomic) {
93 #if defined(VPX_USE_ATOMIC_BUILTINS)
94 return __atomic_load_n(&atomic->value, __ATOMIC_ACQUIRE);
95 #else
96 int v = atomic->value;
97 vpx_atomic_memory_barrier();
98 return v;
99 #endif // defined(VPX_USE_ATOMIC_BUILTINS)
100 }
101
102 #undef VPX_USE_ATOMIC_BUILTINS
103 #undef vpx_atomic_memory_barrier
104
105 #endif /* CONFIG_OS_SUPPORT && CONFIG_MULTITHREAD */
106
107 #ifdef __cplusplus
108 } // extern "C"
109 #endif // __cplusplus
110
111 #endif // VPX_VPX_UTIL_VPX_ATOMICS_H_
112