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