OLD | NEW |
(Empty) | |
| 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 |
| 12 #include <pthread.h> |
| 13 #include <stdint.h> |
| 14 |
| 15 /* |
| 16 This implementation must be careful to not call code external to this file |
| 17 which will turn around and try to call __cxa_guard_acquire reentrantly. |
| 18 For this reason, the headers of this file are as restricted as possible. |
| 19 Previous implementations of this code for __APPLE__ have used |
| 20 pthread_mutex_lock and the abort_message utility without problem. This |
| 21 implementation also uses pthread_cond_wait which has tested to not be a |
| 22 problem. |
| 23 */ |
| 24 |
| 25 namespace __cxxabiv1 |
| 26 { |
| 27 |
| 28 namespace |
| 29 { |
| 30 |
| 31 #if __arm__ |
| 32 |
| 33 // A 32-bit, 4-byte-aligned static data value. The least significant 2 bits must |
| 34 // be statically initialized to 0. |
| 35 typedef uint32_t guard_type; |
| 36 |
| 37 // Test the lowest bit. |
| 38 inline bool is_initialized(guard_type* guard_object) { |
| 39 return (*guard_object) & 1; |
| 40 } |
| 41 |
| 42 inline void set_initialized(guard_type* guard_object) { |
| 43 *guard_object |= 1; |
| 44 } |
| 45 |
| 46 #else |
| 47 |
| 48 typedef uint64_t guard_type; |
| 49 |
| 50 bool is_initialized(guard_type* guard_object) { |
| 51 char* initialized = (char*)guard_object; |
| 52 return *initialized; |
| 53 } |
| 54 |
| 55 void set_initialized(guard_type* guard_object) { |
| 56 char* initialized = (char*)guard_object; |
| 57 *initialized = 1; |
| 58 } |
| 59 |
| 60 #endif |
| 61 |
| 62 pthread_mutex_t guard_mut = PTHREAD_MUTEX_INITIALIZER; |
| 63 pthread_cond_t guard_cv = PTHREAD_COND_INITIALIZER; |
| 64 |
| 65 #if defined(__APPLE__) && !defined(__arm__) |
| 66 |
| 67 typedef uint32_t lock_type; |
| 68 |
| 69 #if __LITTLE_ENDIAN__ |
| 70 |
| 71 inline |
| 72 lock_type |
| 73 get_lock(uint64_t x) |
| 74 { |
| 75 return static_cast<lock_type>(x >> 32); |
| 76 } |
| 77 |
| 78 inline |
| 79 void |
| 80 set_lock(uint64_t& x, lock_type y) |
| 81 { |
| 82 x = static_cast<uint64_t>(y) << 32; |
| 83 } |
| 84 |
| 85 #else // __LITTLE_ENDIAN__ |
| 86 |
| 87 inline |
| 88 lock_type |
| 89 get_lock(uint64_t x) |
| 90 { |
| 91 return static_cast<lock_type>(x); |
| 92 } |
| 93 |
| 94 inline |
| 95 void |
| 96 set_lock(uint64_t& x, lock_type y) |
| 97 { |
| 98 x = y; |
| 99 } |
| 100 |
| 101 #endif // __LITTLE_ENDIAN__ |
| 102 |
| 103 #else // !__APPLE__ || __arm__ |
| 104 |
| 105 typedef bool lock_type; |
| 106 |
| 107 inline |
| 108 lock_type |
| 109 get_lock(uint64_t x) |
| 110 { |
| 111 union |
| 112 { |
| 113 uint64_t guard; |
| 114 uint8_t lock[2]; |
| 115 } f = {x}; |
| 116 return f.lock[1] != 0; |
| 117 } |
| 118 |
| 119 inline |
| 120 void |
| 121 set_lock(uint64_t& x, lock_type y) |
| 122 { |
| 123 union |
| 124 { |
| 125 uint64_t guard; |
| 126 uint8_t lock[2]; |
| 127 } f = {0}; |
| 128 f.lock[1] = y; |
| 129 x = f.guard; |
| 130 } |
| 131 |
| 132 inline |
| 133 lock_type |
| 134 get_lock(uint32_t x) |
| 135 { |
| 136 union |
| 137 { |
| 138 uint32_t guard; |
| 139 uint8_t lock[2]; |
| 140 } f = {x}; |
| 141 return f.lock[1] != 0; |
| 142 } |
| 143 |
| 144 inline |
| 145 void |
| 146 set_lock(uint32_t& x, lock_type y) |
| 147 { |
| 148 union |
| 149 { |
| 150 uint32_t guard; |
| 151 uint8_t lock[2]; |
| 152 } f = {0}; |
| 153 f.lock[1] = y; |
| 154 x = f.guard; |
| 155 } |
| 156 |
| 157 #endif // __APPLE__ |
| 158 |
| 159 } // unnamed namespace |
| 160 |
| 161 extern "C" |
| 162 { |
| 163 |
| 164 int __cxa_guard_acquire(guard_type* guard_object) |
| 165 { |
| 166 char* initialized = (char*)guard_object; |
| 167 if (pthread_mutex_lock(&guard_mut)) |
| 168 abort_message("__cxa_guard_acquire failed to acquire mutex"); |
| 169 int result = *initialized == 0; |
| 170 if (result) |
| 171 { |
| 172 #if defined(__APPLE__) && !defined(__arm__) |
| 173 const lock_type id = pthread_mach_thread_np(pthread_self()); |
| 174 lock_type lock = get_lock(*guard_object); |
| 175 if (lock) |
| 176 { |
| 177 // if this thread set lock for this same guard_object, abort |
| 178 if (lock == id) |
| 179 abort_message("__cxa_guard_acquire detected deadlock"); |
| 180 do |
| 181 { |
| 182 if (pthread_cond_wait(&guard_cv, &guard_mut)) |
| 183 abort_message("__cxa_guard_acquire condition variable wait f
ailed"); |
| 184 lock = get_lock(*guard_object); |
| 185 } while (lock); |
| 186 result = !is_initialized(guard_object); |
| 187 if (result) |
| 188 set_lock(*guard_object, id); |
| 189 } |
| 190 else |
| 191 set_lock(*guard_object, id); |
| 192 #else // !__APPLE__ || __arm__ |
| 193 while (get_lock(*guard_object)) |
| 194 if (pthread_cond_wait(&guard_cv, &guard_mut)) |
| 195 abort_message("__cxa_guard_acquire condition variable wait faile
d"); |
| 196 result = *initialized == 0; |
| 197 if (result) |
| 198 set_lock(*guard_object, true); |
| 199 #endif // !__APPLE__ || __arm__ |
| 200 } |
| 201 if (pthread_mutex_unlock(&guard_mut)) |
| 202 abort_message("__cxa_guard_acquire failed to release mutex"); |
| 203 return result; |
| 204 } |
| 205 |
| 206 void __cxa_guard_release(guard_type* guard_object) |
| 207 { |
| 208 if (pthread_mutex_lock(&guard_mut)) |
| 209 abort_message("__cxa_guard_release failed to acquire mutex"); |
| 210 *guard_object = 0; |
| 211 set_initialized(guard_object); |
| 212 if (pthread_mutex_unlock(&guard_mut)) |
| 213 abort_message("__cxa_guard_release failed to release mutex"); |
| 214 if (pthread_cond_broadcast(&guard_cv)) |
| 215 abort_message("__cxa_guard_release failed to broadcast condition variabl
e"); |
| 216 } |
| 217 |
| 218 void __cxa_guard_abort(guard_type* guard_object) |
| 219 { |
| 220 if (pthread_mutex_lock(&guard_mut)) |
| 221 abort_message("__cxa_guard_abort failed to acquire mutex"); |
| 222 *guard_object = 0; |
| 223 if (pthread_mutex_unlock(&guard_mut)) |
| 224 abort_message("__cxa_guard_abort failed to release mutex"); |
| 225 if (pthread_cond_broadcast(&guard_cv)) |
| 226 abort_message("__cxa_guard_abort failed to broadcast condition variable"
); |
| 227 } |
| 228 |
| 229 } // extern "C" |
| 230 |
| 231 } // __cxxabiv1 |
OLD | NEW |