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