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