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