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_HAS_NO_THREADS 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 #ifdef __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_HAS_NO_THREADS 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_HAS_NO_THREADS 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_HAS_NO_THREADS 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_HAS_NO_THREADS 254 255 } // extern "C" 256 257 } // __cxxabiv1 258