Home | History | Annotate | Download | only in vpx_util
      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.
     79 static INLINE void vpx_atomic_init(vpx_atomic_int *atomic, int value) {
     80   atomic->value = value;
     81 }
     82 
     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 
     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