/* * Copyright (c) 2017 The WebM project authors. All Rights Reserved. * * Use of this source code is governed by a BSD-style license * that can be found in the LICENSE file in the root of the source * tree. An additional intellectual property rights grant can be found * in the file PATENTS. All contributing project authors may * be found in the AUTHORS file in the root of the source tree. */ #ifndef VPX_VPX_UTIL_VPX_ATOMICS_H_ #define VPX_VPX_UTIL_VPX_ATOMICS_H_ #include "./vpx_config.h" #ifdef __cplusplus extern "C" { #endif // __cplusplus #if CONFIG_OS_SUPPORT && CONFIG_MULTITHREAD // Look for built-in atomic support. We cannot use or // since neither is guaranteed to exist on both C and C++ platforms, and we need // to back the atomic type with the same type (g++ needs to be able to use // gcc-built code). g++ 6 doesn't support _Atomic as a keyword and can't use the // stdatomic.h header. Even if both and existed it's not // guaranteed that atomic_int is the same type as std::atomic_int. // See https://gcc.gnu.org/bugzilla/show_bug.cgi?id=60932#c13. #if !defined(__has_builtin) #define __has_builtin(x) 0 // Compatibility with non-clang compilers. #endif // !defined(__has_builtin) #if (__has_builtin(__atomic_load_n)) || \ (defined(__GNUC__) && \ (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 7))) // For GCC >= 4.7 and Clang versions that support __atomic builtins, use those. #define VPX_USE_ATOMIC_BUILTINS #else // Use platform-specific asm barriers. #if defined(_MSC_VER) // TODO(pbos): This assumes that newer versions of MSVC are building with the // default /volatile:ms (or older, where this is always true. Consider adding // support for using instead of stdatomic.h when building C++11 under // MSVC. It's unclear what to do for plain C under /volatile:iso (inline asm?), // there're no explicit Interlocked* functions for only storing or loading // (presumably because volatile has historically implied that on MSVC). // // For earlier versions of MSVC or the default /volatile:ms volatile int are // acquire/release and require no barrier. #define vpx_atomic_memory_barrier() \ do { \ } while (0) #else #if VPX_ARCH_X86 || VPX_ARCH_X86_64 // Use a compiler barrier on x86, no runtime penalty. #define vpx_atomic_memory_barrier() __asm__ __volatile__("" ::: "memory") #elif VPX_ARCH_ARM #define vpx_atomic_memory_barrier() __asm__ __volatile__("dmb ish" ::: "memory") #elif VPX_ARCH_MIPS #define vpx_atomic_memory_barrier() __asm__ __volatile__("sync" ::: "memory") #else #error Unsupported architecture! #endif // VPX_ARCH_X86 || VPX_ARCH_X86_64 #endif // defined(_MSC_VER) #endif // atomic builtin availability check // These are wrapped in a struct so that they are not easily accessed directly // on any platform (to discourage programmer errors by setting values directly). // This primitive MUST be initialized using vpx_atomic_init or VPX_ATOMIC_INIT // (NOT memset) and accessed through vpx_atomic_ functions. typedef struct vpx_atomic_int { volatile int value; } vpx_atomic_int; #define VPX_ATOMIC_INIT(num) \ { num } // Initialization of an atomic int, not thread safe. static INLINE void vpx_atomic_init(vpx_atomic_int *atomic, int value) { atomic->value = value; } static INLINE void vpx_atomic_store_release(vpx_atomic_int *atomic, int value) { #if defined(VPX_USE_ATOMIC_BUILTINS) __atomic_store_n(&atomic->value, value, __ATOMIC_RELEASE); #else vpx_atomic_memory_barrier(); atomic->value = value; #endif // defined(VPX_USE_ATOMIC_BUILTINS) } static INLINE int vpx_atomic_load_acquire(const vpx_atomic_int *atomic) { #if defined(VPX_USE_ATOMIC_BUILTINS) return __atomic_load_n(&atomic->value, __ATOMIC_ACQUIRE); #else int v = atomic->value; vpx_atomic_memory_barrier(); return v; #endif // defined(VPX_USE_ATOMIC_BUILTINS) } #undef VPX_USE_ATOMIC_BUILTINS #undef vpx_atomic_memory_barrier #endif /* CONFIG_OS_SUPPORT && CONFIG_MULTITHREAD */ #ifdef __cplusplus } // extern "C" #endif // __cplusplus #endif // VPX_VPX_UTIL_VPX_ATOMICS_H_