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_SINGLE_THREADED 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 28namespace __cxxabiv1 29{ 30 31namespace 32{ 33 34#if __arm__ 35 36// A 32-bit, 4-byte-aligned static data value. The least significant 2 bits must 37// be statically initialized to 0. 38typedef uint32_t guard_type; 39 40// Test the lowest bit. 41inline bool is_initialized(guard_type* guard_object) { 42 return (*guard_object) & 1; 43} 44 45inline void set_initialized(guard_type* guard_object) { 46 *guard_object |= 1; 47} 48 49#else 50 51typedef uint64_t guard_type; 52 53bool is_initialized(guard_type* guard_object) { 54 char* initialized = (char*)guard_object; 55 return *initialized; 56} 57 58void set_initialized(guard_type* guard_object) { 59 char* initialized = (char*)guard_object; 60 *initialized = 1; 61} 62 63#endif 64 65#if !LIBCXXABI_SINGLE_THREADED 66pthread_mutex_t guard_mut = PTHREAD_MUTEX_INITIALIZER; 67pthread_cond_t guard_cv = PTHREAD_COND_INITIALIZER; 68#endif 69 70#if defined(__APPLE__) && !defined(__arm__) 71 72typedef uint32_t lock_type; 73 74#if __LITTLE_ENDIAN__ 75 76inline 77lock_type 78get_lock(uint64_t x) 79{ 80 return static_cast<lock_type>(x >> 32); 81} 82 83inline 84void 85set_lock(uint64_t& x, lock_type y) 86{ 87 x = static_cast<uint64_t>(y) << 32; 88} 89 90#else // __LITTLE_ENDIAN__ 91 92inline 93lock_type 94get_lock(uint64_t x) 95{ 96 return static_cast<lock_type>(x); 97} 98 99inline 100void 101set_lock(uint64_t& x, lock_type y) 102{ 103 x = y; 104} 105 106#endif // __LITTLE_ENDIAN__ 107 108#else // !__APPLE__ || __arm__ 109 110typedef bool lock_type; 111 112inline 113lock_type 114get_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 124inline 125void 126set_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 137inline 138lock_type 139get_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 149inline 150void 151set_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 166extern "C" 167{ 168 169#if LIBCXXABI_SINGLE_THREADED 170int __cxa_guard_acquire(guard_type* guard_object) 171{ 172 return !is_initialized(guard_object); 173} 174 175void __cxa_guard_release(guard_type* guard_object) 176{ 177 *guard_object = 0; 178 set_initialized(guard_object); 179} 180 181void __cxa_guard_abort(guard_type* guard_object) 182{ 183 *guard_object = 0; 184} 185 186#else // !LIBCXXABI_SINGLE_THREADED 187 188int __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 230void __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 242void __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_SINGLE_THREADED 254 255} // extern "C" 256 257} // __cxxabiv1 258