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 "abort_message.h"
     11 #include "config.h"
     12 
     13 #if !LIBCXXABI_SINGLE_THREADED
     14 #  include <pthread.h>
     15 #endif
     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     pthread_mutex_lock and the abort_message utility without problem.  This
     24     implementation also uses pthread_cond_wait which has tested to not be a
     25     problem.
     26 */
     27 
     28 namespace __cxxabiv1
     29 {
     30 
     31 namespace
     32 {
     33 
     34 #if __arm__
     35 
     36 // A 32-bit, 4-byte-aligned static data value. The least significant 2 bits must
     37 // be statically initialized to 0.
     38 typedef uint32_t guard_type;
     39 
     40 // Test the lowest bit.
     41 inline bool is_initialized(guard_type* guard_object) {
     42     return (*guard_object) & 1;
     43 }
     44 
     45 inline void set_initialized(guard_type* guard_object) {
     46     *guard_object |= 1;
     47 }
     48 
     49 #else
     50 
     51 typedef uint64_t guard_type;
     52 
     53 bool is_initialized(guard_type* guard_object) {
     54     char* initialized = (char*)guard_object;
     55     return *initialized;
     56 }
     57 
     58 void set_initialized(guard_type* guard_object) {
     59     char* initialized = (char*)guard_object;
     60     *initialized = 1;
     61 }
     62 
     63 #endif
     64 
     65 #if !LIBCXXABI_SINGLE_THREADED
     66 pthread_mutex_t guard_mut = PTHREAD_MUTEX_INITIALIZER;
     67 pthread_cond_t  guard_cv  = PTHREAD_COND_INITIALIZER;
     68 #endif
     69 
     70 #if defined(__APPLE__) && !defined(__arm__)
     71 
     72 typedef uint32_t lock_type;
     73 
     74 #if __LITTLE_ENDIAN__
     75 
     76 inline
     77 lock_type
     78 get_lock(uint64_t x)
     79 {
     80     return static_cast<lock_type>(x >> 32);
     81 }
     82 
     83 inline
     84 void
     85 set_lock(uint64_t& x, lock_type y)
     86 {
     87     x = static_cast<uint64_t>(y) << 32;
     88 }
     89 
     90 #else  // __LITTLE_ENDIAN__
     91 
     92 inline
     93 lock_type
     94 get_lock(uint64_t x)
     95 {
     96     return static_cast<lock_type>(x);
     97 }
     98 
     99 inline
    100 void
    101 set_lock(uint64_t& x, lock_type y)
    102 {
    103     x = y;
    104 }
    105 
    106 #endif  // __LITTLE_ENDIAN__
    107 
    108 #else  // !__APPLE__ || __arm__
    109 
    110 typedef bool lock_type;
    111 
    112 inline
    113 lock_type
    114 get_lock(uint64_t x)
    115 {
    116     union
    117     {
    118         uint64_t guard;
    119         uint8_t lock[2];
    120     } f = {x};
    121     return f.lock[1] != 0;
    122 }
    123 
    124 inline
    125 void
    126 set_lock(uint64_t& x, lock_type y)
    127 {
    128     union
    129     {
    130         uint64_t guard;
    131         uint8_t lock[2];
    132     } f = {0};
    133     f.lock[1] = y;
    134     x = f.guard;
    135 }
    136 
    137 inline
    138 lock_type
    139 get_lock(uint32_t x)
    140 {
    141     union
    142     {
    143         uint32_t guard;
    144         uint8_t lock[2];
    145     } f = {x};
    146     return f.lock[1] != 0;
    147 }
    148 
    149 inline
    150 void
    151 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  // __APPLE__
    163 
    164 }  // unnamed namespace
    165 
    166 extern "C"
    167 {
    168 
    169 #if LIBCXXABI_SINGLE_THREADED
    170 int __cxa_guard_acquire(guard_type* guard_object)
    171 {
    172     return !is_initialized(guard_object);
    173 }
    174 
    175 void __cxa_guard_release(guard_type* guard_object)
    176 {
    177     *guard_object = 0;
    178     set_initialized(guard_object);
    179 }
    180 
    181 void __cxa_guard_abort(guard_type* guard_object)
    182 {
    183     *guard_object = 0;
    184 }
    185 
    186 #else // !LIBCXXABI_SINGLE_THREADED
    187 
    188 int __cxa_guard_acquire(guard_type* guard_object)
    189 {
    190     char* initialized = (char*)guard_object;
    191     if (pthread_mutex_lock(&guard_mut))
    192         abort_message("__cxa_guard_acquire failed to acquire mutex");
    193     int result = *initialized == 0;
    194     if (result)
    195     {
    196 #if defined(__APPLE__) && !defined(__arm__)
    197         const lock_type id = pthread_mach_thread_np(pthread_self());
    198         lock_type lock = get_lock(*guard_object);
    199         if (lock)
    200         {
    201             // if this thread set lock for this same guard_object, abort
    202             if (lock == id)
    203                 abort_message("__cxa_guard_acquire detected deadlock");
    204             do
    205             {
    206                 if (pthread_cond_wait(&guard_cv, &guard_mut))
    207                     abort_message("__cxa_guard_acquire condition variable wait failed");
    208                 lock = get_lock(*guard_object);
    209             } while (lock);
    210             result = !is_initialized(guard_object);
    211             if (result)
    212                 set_lock(*guard_object, id);
    213         }
    214         else
    215             set_lock(*guard_object, id);
    216 #else  // !__APPLE__ || __arm__
    217         while (get_lock(*guard_object))
    218             if (pthread_cond_wait(&guard_cv, &guard_mut))
    219                 abort_message("__cxa_guard_acquire condition variable wait failed");
    220         result = *initialized == 0;
    221         if (result)
    222             set_lock(*guard_object, true);
    223 #endif  // !__APPLE__ || __arm__
    224     }
    225     if (pthread_mutex_unlock(&guard_mut))
    226         abort_message("__cxa_guard_acquire failed to release mutex");
    227     return result;
    228 }
    229 
    230 void __cxa_guard_release(guard_type* guard_object)
    231 {
    232     if (pthread_mutex_lock(&guard_mut))
    233         abort_message("__cxa_guard_release failed to acquire mutex");
    234     *guard_object = 0;
    235     set_initialized(guard_object);
    236     if (pthread_mutex_unlock(&guard_mut))
    237         abort_message("__cxa_guard_release failed to release mutex");
    238     if (pthread_cond_broadcast(&guard_cv))
    239         abort_message("__cxa_guard_release failed to broadcast condition variable");
    240 }
    241 
    242 void __cxa_guard_abort(guard_type* guard_object)
    243 {
    244     if (pthread_mutex_lock(&guard_mut))
    245         abort_message("__cxa_guard_abort failed to acquire mutex");
    246     *guard_object = 0;
    247     if (pthread_mutex_unlock(&guard_mut))
    248         abort_message("__cxa_guard_abort failed to release mutex");
    249     if (pthread_cond_broadcast(&guard_cv))
    250         abort_message("__cxa_guard_abort failed to broadcast condition variable");
    251 }
    252 
    253 #endif // !LIBCXXABI_SINGLE_THREADED
    254 
    255 }  // extern "C"
    256 
    257 }  // __cxxabiv1
    258