• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
set_initialized(guard_type * guard_object)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.
is_initialized(guard_type * guard_object)55 inline bool is_initialized(guard_type* guard_object) {
56     return (*guard_object) & 1;
57 }
58 
59 #else
60 
is_initialized(guard_type * guard_object)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
get_lock(uint64_t x)82 get_lock(uint64_t x)
83 {
84     return static_cast<lock_type>(x >> 32);
85 }
86 
87 inline
88 void
set_lock(uint64_t & x,lock_type y)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
get_lock(uint64_t x)98 get_lock(uint64_t x)
99 {
100     return static_cast<lock_type>(x);
101 }
102 
103 inline
104 void
set_lock(uint64_t & x,lock_type y)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
get_lock(uint64_t x)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
set_lock(uint64_t & x,lock_type y)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
get_lock(uint32_t x)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
set_lock(uint32_t & x,lock_type y)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
__cxa_guard_acquire(guard_type * guard_object)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         // This is a special-case pthread dependency for Mac. We can't pull this
183         // out into libcxx's threading API (__threading_support) because not all
184         // supported Mac environments provide this function (in pthread.h). To
185         // make it possible to build/use libcxx in those environments, we have to
186         // keep this pthread dependency local to libcxxabi. If there is some
187         // convenient way to detect precisely when pthread_mach_thread_np is
188         // available in a given Mac environment, it might still be possible to
189         // bury this dependency in __threading_support.
190         #ifdef _LIBCPP_HAS_THREAD_API_PTHREAD
191            const lock_type id = pthread_mach_thread_np(std::__libcpp_thread_get_current_id());
192         #else
193            #error "How do I pthread_mach_thread_np()?"
194         #endif
195         lock_type lock = get_lock(*guard_object);
196         if (lock)
197         {
198             // if this thread set lock for this same guard_object, abort
199             if (lock == id)
200                 abort_message("__cxa_guard_acquire detected deadlock");
201             do
202             {
203                 if (std::__libcpp_condvar_wait(&guard_cv, &guard_mut))
204                     abort_message("__cxa_guard_acquire condition variable wait failed");
205                 lock = get_lock(*guard_object);
206             } while (lock);
207             result = !is_initialized(guard_object);
208             if (result)
209                 set_lock(*guard_object, id);
210         }
211         else
212             set_lock(*guard_object, id);
213 #else  // !__APPLE__ || __arm__
214         while (get_lock(*guard_object))
215             if (std::__libcpp_condvar_wait(&guard_cv, &guard_mut))
216                 abort_message("__cxa_guard_acquire condition variable wait failed");
217         result = *initialized == 0;
218         if (result)
219             set_lock(*guard_object, true);
220 #endif  // !__APPLE__ || __arm__
221     }
222     if (std::__libcpp_mutex_unlock(&guard_mut))
223         abort_message("__cxa_guard_acquire failed to release mutex");
224     return result;
225 }
226 
__cxa_guard_release(guard_type * guard_object)227 _LIBCXXABI_FUNC_VIS void __cxa_guard_release(guard_type *guard_object) {
228     if (std::__libcpp_mutex_lock(&guard_mut))
229         abort_message("__cxa_guard_release failed to acquire mutex");
230     *guard_object = 0;
231     set_initialized(guard_object);
232     if (std::__libcpp_mutex_unlock(&guard_mut))
233         abort_message("__cxa_guard_release failed to release mutex");
234     if (std::__libcpp_condvar_broadcast(&guard_cv))
235         abort_message("__cxa_guard_release failed to broadcast condition variable");
236 }
237 
__cxa_guard_abort(guard_type * guard_object)238 _LIBCXXABI_FUNC_VIS void __cxa_guard_abort(guard_type *guard_object) {
239     if (std::__libcpp_mutex_lock(&guard_mut))
240         abort_message("__cxa_guard_abort failed to acquire mutex");
241     *guard_object = 0;
242     if (std::__libcpp_mutex_unlock(&guard_mut))
243         abort_message("__cxa_guard_abort failed to release mutex");
244     if (std::__libcpp_condvar_broadcast(&guard_cv))
245         abort_message("__cxa_guard_abort failed to broadcast condition variable");
246 }
247 
248 #else // _LIBCXXABI_HAS_NO_THREADS
249 
250 _LIBCXXABI_FUNC_VIS int __cxa_guard_acquire(guard_type *guard_object) {
251     return !is_initialized(guard_object);
252 }
253 
254 _LIBCXXABI_FUNC_VIS void __cxa_guard_release(guard_type *guard_object) {
255     *guard_object = 0;
256     set_initialized(guard_object);
257 }
258 
259 _LIBCXXABI_FUNC_VIS void __cxa_guard_abort(guard_type *guard_object) {
260     *guard_object = 0;
261 }
262 
263 #endif // !_LIBCXXABI_HAS_NO_THREADS
264 
265 }  // extern "C"
266 
267 }  // __cxxabiv1
268