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