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