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 <__threading_support> 14 15 #include <stdint.h> 16 17 /* 18 This implementation must be careful to not call code external to this file 19 which will turn around and try to call __cxa_guard_acquire reentrantly. 20 For this reason, the headers of this file are as restricted as possible. 21 Previous implementations of this code for __APPLE__ have used 22 std::__libcpp_mutex_lock and the abort_message utility without problem. This 23 implementation also uses std::__libcpp_condvar_wait which has tested 24 to not be a problem. 25 */ 26 27 namespace __cxxabiv1 28 { 29 30 namespace 31 { 32 33 #ifdef __arm__ 34 // A 32-bit, 4-byte-aligned static data value. The least significant 2 bits must 35 // be statically initialized to 0. 36 typedef uint32_t guard_type; 37 38 inline void set_initialized(guard_type* guard_object) { 39 *guard_object |= 1; 40 } 41 #else 42 typedef uint64_t guard_type; 43 44 void set_initialized(guard_type* guard_object) { 45 char* initialized = (char*)guard_object; 46 *initialized = 1; 47 } 48 #endif 49 50 #if defined(_LIBCXXABI_HAS_NO_THREADS) || (defined(__APPLE__) && !defined(__arm__)) 51 #ifdef __arm__ 52 53 // Test the lowest bit. 54 inline bool is_initialized(guard_type* guard_object) { 55 return (*guard_object) & 1; 56 } 57 58 #else 59 60 bool is_initialized(guard_type* guard_object) { 61 char* initialized = (char*)guard_object; 62 return *initialized; 63 } 64 65 #endif 66 #endif 67 68 #ifndef _LIBCXXABI_HAS_NO_THREADS 69 std::__libcpp_mutex_t guard_mut = _LIBCPP_MUTEX_INITIALIZER; 70 std::__libcpp_condvar_t guard_cv = _LIBCPP_CONDVAR_INITIALIZER; 71 #endif 72 73 #if defined(__APPLE__) && !defined(__arm__) 74 75 typedef uint32_t lock_type; 76 77 #if __LITTLE_ENDIAN__ 78 79 inline 80 lock_type 81 get_lock(uint64_t x) 82 { 83 return static_cast<lock_type>(x >> 32); 84 } 85 86 inline 87 void 88 set_lock(uint64_t& x, lock_type y) 89 { 90 x = static_cast<uint64_t>(y) << 32; 91 } 92 93 #else // __LITTLE_ENDIAN__ 94 95 inline 96 lock_type 97 get_lock(uint64_t x) 98 { 99 return static_cast<lock_type>(x); 100 } 101 102 inline 103 void 104 set_lock(uint64_t& x, lock_type y) 105 { 106 x = y; 107 } 108 109 #endif // __LITTLE_ENDIAN__ 110 111 #else // !__APPLE__ || __arm__ 112 113 typedef bool lock_type; 114 115 #if !defined(__arm__) 116 static_assert(std::is_same<guard_type, uint64_t>::value, ""); 117 118 inline lock_type 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 void 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 #else // defined(__arm__) 139 static_assert(std::is_same<guard_type, uint32_t>::value, ""); 140 141 inline lock_type 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 void 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 // !defined(__arm__) 163 164 #endif // __APPLE__ && !__arm__ 165 166 } // unnamed namespace 167 168 extern "C" 169 { 170 171 #ifndef _LIBCXXABI_HAS_NO_THREADS 172 _LIBCXXABI_FUNC_VIS int __cxa_guard_acquire(guard_type *guard_object) { 173 char* initialized = (char*)guard_object; 174 if (std::__libcpp_mutex_lock(&guard_mut)) 175 abort_message("__cxa_guard_acquire failed to acquire mutex"); 176 int result = *initialized == 0; 177 if (result) 178 { 179 #if defined(__APPLE__) && !defined(__arm__) 180 // This is a special-case pthread dependency for Mac. We can't pull this 181 // out into libcxx's threading API (__threading_support) because not all 182 // supported Mac environments provide this function (in pthread.h). To 183 // make it possible to build/use libcxx in those environments, we have to 184 // keep this pthread dependency local to libcxxabi. If there is some 185 // convenient way to detect precisely when pthread_mach_thread_np is 186 // available in a given Mac environment, it might still be possible to 187 // bury this dependency in __threading_support. 188 #ifdef _LIBCPP_HAS_THREAD_API_PTHREAD 189 const lock_type id = pthread_mach_thread_np(std::__libcpp_thread_get_current_id()); 190 #else 191 #error "How do I pthread_mach_thread_np()?" 192 #endif 193 lock_type lock = get_lock(*guard_object); 194 if (lock) 195 { 196 // if this thread set lock for this same guard_object, abort 197 if (lock == id) 198 abort_message("__cxa_guard_acquire detected deadlock"); 199 do 200 { 201 if (std::__libcpp_condvar_wait(&guard_cv, &guard_mut)) 202 abort_message("__cxa_guard_acquire condition variable wait failed"); 203 lock = get_lock(*guard_object); 204 } while (lock); 205 result = !is_initialized(guard_object); 206 if (result) 207 set_lock(*guard_object, id); 208 } 209 else 210 set_lock(*guard_object, id); 211 #else // !__APPLE__ || __arm__ 212 while (get_lock(*guard_object)) 213 if (std::__libcpp_condvar_wait(&guard_cv, &guard_mut)) 214 abort_message("__cxa_guard_acquire condition variable wait failed"); 215 result = *initialized == 0; 216 if (result) 217 set_lock(*guard_object, true); 218 #endif // !__APPLE__ || __arm__ 219 } 220 if (std::__libcpp_mutex_unlock(&guard_mut)) 221 abort_message("__cxa_guard_acquire failed to release mutex"); 222 return result; 223 } 224 225 _LIBCXXABI_FUNC_VIS void __cxa_guard_release(guard_type *guard_object) { 226 if (std::__libcpp_mutex_lock(&guard_mut)) 227 abort_message("__cxa_guard_release failed to acquire mutex"); 228 *guard_object = 0; 229 set_initialized(guard_object); 230 if (std::__libcpp_mutex_unlock(&guard_mut)) 231 abort_message("__cxa_guard_release failed to release mutex"); 232 if (std::__libcpp_condvar_broadcast(&guard_cv)) 233 abort_message("__cxa_guard_release failed to broadcast condition variable"); 234 } 235 236 _LIBCXXABI_FUNC_VIS void __cxa_guard_abort(guard_type *guard_object) { 237 if (std::__libcpp_mutex_lock(&guard_mut)) 238 abort_message("__cxa_guard_abort failed to acquire mutex"); 239 *guard_object = 0; 240 if (std::__libcpp_mutex_unlock(&guard_mut)) 241 abort_message("__cxa_guard_abort failed to release mutex"); 242 if (std::__libcpp_condvar_broadcast(&guard_cv)) 243 abort_message("__cxa_guard_abort failed to broadcast condition variable"); 244 } 245 246 #else // _LIBCXXABI_HAS_NO_THREADS 247 248 _LIBCXXABI_FUNC_VIS int __cxa_guard_acquire(guard_type *guard_object) { 249 return !is_initialized(guard_object); 250 } 251 252 _LIBCXXABI_FUNC_VIS void __cxa_guard_release(guard_type *guard_object) { 253 *guard_object = 0; 254 set_initialized(guard_object); 255 } 256 257 _LIBCXXABI_FUNC_VIS void __cxa_guard_abort(guard_type *guard_object) { 258 *guard_object = 0; 259 } 260 261 #endif // !_LIBCXXABI_HAS_NO_THREADS 262 263 } // extern "C" 264 265 } // __cxxabiv1 266