Home | History | Annotate | Download | only in src
      1 //===---------------------------- cxa_guard.cpp ---------------------------===//
      2 //
      3 //                     The LLVM Compiler Infrastructure
      4 //
      5 // This file is dual licensed under the MIT and the University of Illinois Open
      6 // Source Licenses. See LICENSE.TXT for details.
      7 //
      8 //===----------------------------------------------------------------------===//
      9 
     10 #include "__cxxabi_config.h"
     11 
     12 #include "abort_message.h"
     13 #include <__threading_support>
     14 
     15 #include <stdint.h>
     16 
     17 /*
     18     This implementation must be careful to not call code external to this file
     19     which will turn around and try to call __cxa_guard_acquire reentrantly.
     20     For this reason, the headers of this file are as restricted as possible.
     21     Previous implementations of this code for __APPLE__ have used
     22     std::__libcpp_mutex_lock and the abort_message utility without problem. This
     23     implementation also uses std::__libcpp_condvar_wait which has tested
     24     to not be a problem.
     25 */
     26 
     27 namespace __cxxabiv1
     28 {
     29 
     30 namespace
     31 {
     32 
     33 #ifdef __arm__
     34 // A 32-bit, 4-byte-aligned static data value. The least significant 2 bits must
     35 // be statically initialized to 0.
     36 typedef uint32_t guard_type;
     37 
     38 inline void set_initialized(guard_type* guard_object) {
     39     *guard_object |= 1;
     40 }
     41 #else
     42 typedef uint64_t guard_type;
     43 
     44 void set_initialized(guard_type* guard_object) {
     45     char* initialized = (char*)guard_object;
     46     *initialized = 1;
     47 }
     48 #endif
     49 
     50 #if defined(_LIBCXXABI_HAS_NO_THREADS) || (defined(__APPLE__) && !defined(__arm__))
     51 #ifdef __arm__
     52 
     53 // Test the lowest bit.
     54 inline bool is_initialized(guard_type* guard_object) {
     55     return (*guard_object) & 1;
     56 }
     57 
     58 #else
     59 
     60 bool is_initialized(guard_type* guard_object) {
     61     char* initialized = (char*)guard_object;
     62     return *initialized;
     63 }
     64 
     65 #endif
     66 #endif
     67 
     68 #ifndef _LIBCXXABI_HAS_NO_THREADS
     69 std::__libcpp_mutex_t guard_mut = _LIBCPP_MUTEX_INITIALIZER;
     70 std::__libcpp_condvar_t guard_cv = _LIBCPP_CONDVAR_INITIALIZER;
     71 #endif
     72 
     73 #if defined(__APPLE__) && !defined(__arm__)
     74 
     75 typedef uint32_t lock_type;
     76 
     77 #if __LITTLE_ENDIAN__
     78 
     79 inline
     80 lock_type
     81 get_lock(uint64_t x)
     82 {
     83     return static_cast<lock_type>(x >> 32);
     84 }
     85 
     86 inline
     87 void
     88 set_lock(uint64_t& x, lock_type y)
     89 {
     90     x = static_cast<uint64_t>(y) << 32;
     91 }
     92 
     93 #else  // __LITTLE_ENDIAN__
     94 
     95 inline
     96 lock_type
     97 get_lock(uint64_t x)
     98 {
     99     return static_cast<lock_type>(x);
    100 }
    101 
    102 inline
    103 void
    104 set_lock(uint64_t& x, lock_type y)
    105 {
    106     x = y;
    107 }
    108 
    109 #endif  // __LITTLE_ENDIAN__
    110 
    111 #else  // !__APPLE__ || __arm__
    112 
    113 typedef bool lock_type;
    114 
    115 #if !defined(__arm__)
    116 static_assert(std::is_same<guard_type, uint64_t>::value, "");
    117 
    118 inline lock_type get_lock(uint64_t x)
    119 {
    120     union
    121     {
    122         uint64_t guard;
    123         uint8_t lock[2];
    124     } f = {x};
    125     return f.lock[1] != 0;
    126 }
    127 
    128 inline void set_lock(uint64_t& x, lock_type y)
    129 {
    130     union
    131     {
    132         uint64_t guard;
    133         uint8_t lock[2];
    134     } f = {0};
    135     f.lock[1] = y;
    136     x = f.guard;
    137 }
    138 #else // defined(__arm__)
    139 static_assert(std::is_same<guard_type, uint32_t>::value, "");
    140 
    141 inline lock_type get_lock(uint32_t x)
    142 {
    143     union
    144     {
    145         uint32_t guard;
    146         uint8_t lock[2];
    147     } f = {x};
    148     return f.lock[1] != 0;
    149 }
    150 
    151 inline void set_lock(uint32_t& x, lock_type y)
    152 {
    153     union
    154     {
    155         uint32_t guard;
    156         uint8_t lock[2];
    157     } f = {0};
    158     f.lock[1] = y;
    159     x = f.guard;
    160 }
    161 
    162 #endif // !defined(__arm__)
    163 
    164 #endif  // __APPLE__ && !__arm__
    165 
    166 }  // unnamed namespace
    167 
    168 extern "C"
    169 {
    170 
    171 #ifndef _LIBCXXABI_HAS_NO_THREADS
    172 _LIBCXXABI_FUNC_VIS int __cxa_guard_acquire(guard_type *guard_object) {
    173     char* initialized = (char*)guard_object;
    174     if (std::__libcpp_mutex_lock(&guard_mut))
    175         abort_message("__cxa_guard_acquire failed to acquire mutex");
    176     int result = *initialized == 0;
    177     if (result)
    178     {
    179 #if defined(__APPLE__) && !defined(__arm__)
    180         // This is a special-case pthread dependency for Mac. We can't pull this
    181         // out into libcxx's threading API (__threading_support) because not all
    182         // supported Mac environments provide this function (in pthread.h). To
    183         // make it possible to build/use libcxx in those environments, we have to
    184         // keep this pthread dependency local to libcxxabi. If there is some
    185         // convenient way to detect precisely when pthread_mach_thread_np is
    186         // available in a given Mac environment, it might still be possible to
    187         // bury this dependency in __threading_support.
    188         #ifdef _LIBCPP_HAS_THREAD_API_PTHREAD
    189            const lock_type id = pthread_mach_thread_np(std::__libcpp_thread_get_current_id());
    190         #else
    191            #error "How do I pthread_mach_thread_np()?"
    192         #endif
    193         lock_type lock = get_lock(*guard_object);
    194         if (lock)
    195         {
    196             // if this thread set lock for this same guard_object, abort
    197             if (lock == id)
    198                 abort_message("__cxa_guard_acquire detected deadlock");
    199             do
    200             {
    201                 if (std::__libcpp_condvar_wait(&guard_cv, &guard_mut))
    202                     abort_message("__cxa_guard_acquire condition variable wait failed");
    203                 lock = get_lock(*guard_object);
    204             } while (lock);
    205             result = !is_initialized(guard_object);
    206             if (result)
    207                 set_lock(*guard_object, id);
    208         }
    209         else
    210             set_lock(*guard_object, id);
    211 #else  // !__APPLE__ || __arm__
    212         while (get_lock(*guard_object))
    213             if (std::__libcpp_condvar_wait(&guard_cv, &guard_mut))
    214                 abort_message("__cxa_guard_acquire condition variable wait failed");
    215         result = *initialized == 0;
    216         if (result)
    217             set_lock(*guard_object, true);
    218 #endif  // !__APPLE__ || __arm__
    219     }
    220     if (std::__libcpp_mutex_unlock(&guard_mut))
    221         abort_message("__cxa_guard_acquire failed to release mutex");
    222     return result;
    223 }
    224 
    225 _LIBCXXABI_FUNC_VIS void __cxa_guard_release(guard_type *guard_object) {
    226     if (std::__libcpp_mutex_lock(&guard_mut))
    227         abort_message("__cxa_guard_release failed to acquire mutex");
    228     *guard_object = 0;
    229     set_initialized(guard_object);
    230     if (std::__libcpp_mutex_unlock(&guard_mut))
    231         abort_message("__cxa_guard_release failed to release mutex");
    232     if (std::__libcpp_condvar_broadcast(&guard_cv))
    233         abort_message("__cxa_guard_release failed to broadcast condition variable");
    234 }
    235 
    236 _LIBCXXABI_FUNC_VIS void __cxa_guard_abort(guard_type *guard_object) {
    237     if (std::__libcpp_mutex_lock(&guard_mut))
    238         abort_message("__cxa_guard_abort failed to acquire mutex");
    239     *guard_object = 0;
    240     if (std::__libcpp_mutex_unlock(&guard_mut))
    241         abort_message("__cxa_guard_abort failed to release mutex");
    242     if (std::__libcpp_condvar_broadcast(&guard_cv))
    243         abort_message("__cxa_guard_abort failed to broadcast condition variable");
    244 }
    245 
    246 #else // _LIBCXXABI_HAS_NO_THREADS
    247 
    248 _LIBCXXABI_FUNC_VIS int __cxa_guard_acquire(guard_type *guard_object) {
    249     return !is_initialized(guard_object);
    250 }
    251 
    252 _LIBCXXABI_FUNC_VIS void __cxa_guard_release(guard_type *guard_object) {
    253     *guard_object = 0;
    254     set_initialized(guard_object);
    255 }
    256 
    257 _LIBCXXABI_FUNC_VIS void __cxa_guard_abort(guard_type *guard_object) {
    258     *guard_object = 0;
    259 }
    260 
    261 #endif // !_LIBCXXABI_HAS_NO_THREADS
    262 
    263 }  // extern "C"
    264 
    265 }  // __cxxabiv1
    266