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 const lock_type id = std::__libcpp_thread_get_port(); 183 lock_type lock = get_lock(*guard_object); 184 if (lock) 185 { 186 // if this thread set lock for this same guard_object, abort 187 if (lock == id) 188 abort_message("__cxa_guard_acquire detected deadlock"); 189 do 190 { 191 if (std::__libcpp_condvar_wait(&guard_cv, &guard_mut)) 192 abort_message("__cxa_guard_acquire condition variable wait failed"); 193 lock = get_lock(*guard_object); 194 } while (lock); 195 result = !is_initialized(guard_object); 196 if (result) 197 set_lock(*guard_object, id); 198 } 199 else 200 set_lock(*guard_object, id); 201 #else // !__APPLE__ || __arm__ 202 while (get_lock(*guard_object)) 203 if (std::__libcpp_condvar_wait(&guard_cv, &guard_mut)) 204 abort_message("__cxa_guard_acquire condition variable wait failed"); 205 result = *initialized == 0; 206 if (result) 207 set_lock(*guard_object, true); 208 #endif // !__APPLE__ || __arm__ 209 } 210 if (std::__libcpp_mutex_unlock(&guard_mut)) 211 abort_message("__cxa_guard_acquire failed to release mutex"); 212 return result; 213 } 214 215 _LIBCXXABI_FUNC_VIS void __cxa_guard_release(guard_type *guard_object) { 216 if (std::__libcpp_mutex_lock(&guard_mut)) 217 abort_message("__cxa_guard_release failed to acquire mutex"); 218 *guard_object = 0; 219 set_initialized(guard_object); 220 if (std::__libcpp_mutex_unlock(&guard_mut)) 221 abort_message("__cxa_guard_release failed to release mutex"); 222 if (std::__libcpp_condvar_broadcast(&guard_cv)) 223 abort_message("__cxa_guard_release failed to broadcast condition variable"); 224 } 225 226 _LIBCXXABI_FUNC_VIS void __cxa_guard_abort(guard_type *guard_object) { 227 if (std::__libcpp_mutex_lock(&guard_mut)) 228 abort_message("__cxa_guard_abort failed to acquire mutex"); 229 *guard_object = 0; 230 if (std::__libcpp_mutex_unlock(&guard_mut)) 231 abort_message("__cxa_guard_abort failed to release mutex"); 232 if (std::__libcpp_condvar_broadcast(&guard_cv)) 233 abort_message("__cxa_guard_abort failed to broadcast condition variable"); 234 } 235 236 #else // _LIBCXXABI_HAS_NO_THREADS 237 238 _LIBCXXABI_FUNC_VIS int __cxa_guard_acquire(guard_type *guard_object) { 239 return !is_initialized(guard_object); 240 } 241 242 _LIBCXXABI_FUNC_VIS void __cxa_guard_release(guard_type *guard_object) { 243 *guard_object = 0; 244 set_initialized(guard_object); 245 } 246 247 _LIBCXXABI_FUNC_VIS void __cxa_guard_abort(guard_type *guard_object) { 248 *guard_object = 0; 249 } 250 251 #endif // !_LIBCXXABI_HAS_NO_THREADS 252 253 } // extern "C" 254 255 } // __cxxabiv1 256