• 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 <__threading_support>
14 
15 #include <stdint.h>
16 
17 /*
18     This implementation must be careful to not call code external to this file
19     which will turn around and try to call __cxa_guard_acquire reentrantly.
20     For this reason, the headers of this file are as restricted as possible.
21     Previous implementations of this code for __APPLE__ have used
22     std::__libcpp_mutex_lock and the abort_message utility without problem. This
23     implementation also uses std::__libcpp_condvar_wait which has tested
24     to not be a problem.
25 */
26 
27 namespace __cxxabiv1
28 {
29 
30 namespace
31 {
32 
33 #ifdef __arm__
34 // A 32-bit, 4-byte-aligned static data value. The least significant 2 bits must
35 // be statically initialized to 0.
36 typedef uint32_t guard_type;
37 
set_initialized(guard_type * guard_object)38 inline void set_initialized(guard_type* guard_object) {
39     *guard_object |= 1;
40 }
41 #else
42 typedef uint64_t guard_type;
43 
44 void set_initialized(guard_type* guard_object) {
45     char* initialized = (char*)guard_object;
46     *initialized = 1;
47 }
48 #endif
49 
50 #if defined(_LIBCXXABI_HAS_NO_THREADS) || (defined(__APPLE__) && !defined(__arm__))
51 #ifdef __arm__
52 
53 // Test the lowest bit.
is_initialized(guard_type * guard_object)54 inline bool is_initialized(guard_type* guard_object) {
55     return (*guard_object) & 1;
56 }
57 
58 #else
59 
is_initialized(guard_type * guard_object)60 bool is_initialized(guard_type* guard_object) {
61     char* initialized = (char*)guard_object;
62     return *initialized;
63 }
64 
65 #endif
66 #endif
67 
68 #ifndef _LIBCXXABI_HAS_NO_THREADS
69 std::__libcpp_mutex_t guard_mut = _LIBCPP_MUTEX_INITIALIZER;
70 std::__libcpp_condvar_t guard_cv = _LIBCPP_CONDVAR_INITIALIZER;
71 #endif
72 
73 #if defined(__APPLE__) && !defined(__arm__)
74 
75 typedef uint32_t lock_type;
76 
77 #if __LITTLE_ENDIAN__
78 
79 inline
80 lock_type
get_lock(uint64_t x)81 get_lock(uint64_t x)
82 {
83     return static_cast<lock_type>(x >> 32);
84 }
85 
86 inline
87 void
set_lock(uint64_t & x,lock_type y)88 set_lock(uint64_t& x, lock_type y)
89 {
90     x = static_cast<uint64_t>(y) << 32;
91 }
92 
93 #else  // __LITTLE_ENDIAN__
94 
95 inline
96 lock_type
get_lock(uint64_t x)97 get_lock(uint64_t x)
98 {
99     return static_cast<lock_type>(x);
100 }
101 
102 inline
103 void
set_lock(uint64_t & x,lock_type y)104 set_lock(uint64_t& x, lock_type y)
105 {
106     x = y;
107 }
108 
109 #endif  // __LITTLE_ENDIAN__
110 
111 #else  // !__APPLE__ || __arm__
112 
113 typedef bool lock_type;
114 
115 #if !defined(__arm__)
116 static_assert(std::is_same<guard_type, uint64_t>::value, "");
117 
get_lock(uint64_t x)118 inline lock_type 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 
set_lock(uint64_t & x,lock_type y)128 inline void set_lock(uint64_t& x, lock_type y)
129 {
130     union
131     {
132         uint64_t guard;
133         uint8_t lock[2];
134     } f = {0};
135     f.lock[1] = y;
136     x = f.guard;
137 }
138 #else // defined(__arm__)
139 static_assert(std::is_same<guard_type, uint32_t>::value, "");
140 
get_lock(uint32_t x)141 inline lock_type get_lock(uint32_t x)
142 {
143     union
144     {
145         uint32_t guard;
146         uint8_t lock[2];
147     } f = {x};
148     return f.lock[1] != 0;
149 }
150 
set_lock(uint32_t & x,lock_type y)151 inline void 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 // !defined(__arm__)
163 
164 #endif  // __APPLE__ && !__arm__
165 
166 }  // unnamed namespace
167 
168 extern "C"
169 {
170 
171 #ifndef _LIBCXXABI_HAS_NO_THREADS
__cxa_guard_acquire(guard_type * guard_object)172 _LIBCXXABI_FUNC_VIS int __cxa_guard_acquire(guard_type *guard_object) {
173     char* initialized = (char*)guard_object;
174     if (std::__libcpp_mutex_lock(&guard_mut))
175         abort_message("__cxa_guard_acquire failed to acquire mutex");
176     int result = *initialized == 0;
177     if (result)
178     {
179 #if defined(__APPLE__) && !defined(__arm__)
180         // This is a special-case pthread dependency for Mac. We can't pull this
181         // out into libcxx's threading API (__threading_support) because not all
182         // supported Mac environments provide this function (in pthread.h). To
183         // make it possible to build/use libcxx in those environments, we have to
184         // keep this pthread dependency local to libcxxabi. If there is some
185         // convenient way to detect precisely when pthread_mach_thread_np is
186         // available in a given Mac environment, it might still be possible to
187         // bury this dependency in __threading_support.
188         #ifdef _LIBCPP_HAS_THREAD_API_PTHREAD
189            const lock_type id = pthread_mach_thread_np(std::__libcpp_thread_get_current_id());
190         #else
191            #error "How do I pthread_mach_thread_np()?"
192         #endif
193         lock_type lock = get_lock(*guard_object);
194         if (lock)
195         {
196             // if this thread set lock for this same guard_object, abort
197             if (lock == id)
198                 abort_message("__cxa_guard_acquire detected deadlock");
199             do
200             {
201                 if (std::__libcpp_condvar_wait(&guard_cv, &guard_mut))
202                     abort_message("__cxa_guard_acquire condition variable wait failed");
203                 lock = get_lock(*guard_object);
204             } while (lock);
205             result = !is_initialized(guard_object);
206             if (result)
207                 set_lock(*guard_object, id);
208         }
209         else
210             set_lock(*guard_object, id);
211 #else  // !__APPLE__ || __arm__
212         while (get_lock(*guard_object))
213             if (std::__libcpp_condvar_wait(&guard_cv, &guard_mut))
214                 abort_message("__cxa_guard_acquire condition variable wait failed");
215         result = *initialized == 0;
216         if (result)
217             set_lock(*guard_object, true);
218 #endif  // !__APPLE__ || __arm__
219     }
220     if (std::__libcpp_mutex_unlock(&guard_mut))
221         abort_message("__cxa_guard_acquire failed to release mutex");
222     return result;
223 }
224 
__cxa_guard_release(guard_type * guard_object)225 _LIBCXXABI_FUNC_VIS void __cxa_guard_release(guard_type *guard_object) {
226     if (std::__libcpp_mutex_lock(&guard_mut))
227         abort_message("__cxa_guard_release failed to acquire mutex");
228     *guard_object = 0;
229     set_initialized(guard_object);
230     if (std::__libcpp_mutex_unlock(&guard_mut))
231         abort_message("__cxa_guard_release failed to release mutex");
232     if (std::__libcpp_condvar_broadcast(&guard_cv))
233         abort_message("__cxa_guard_release failed to broadcast condition variable");
234 }
235 
__cxa_guard_abort(guard_type * guard_object)236 _LIBCXXABI_FUNC_VIS void __cxa_guard_abort(guard_type *guard_object) {
237     if (std::__libcpp_mutex_lock(&guard_mut))
238         abort_message("__cxa_guard_abort failed to acquire mutex");
239     *guard_object = 0;
240     if (std::__libcpp_mutex_unlock(&guard_mut))
241         abort_message("__cxa_guard_abort failed to release mutex");
242     if (std::__libcpp_condvar_broadcast(&guard_cv))
243         abort_message("__cxa_guard_abort failed to broadcast condition variable");
244 }
245 
246 #else // _LIBCXXABI_HAS_NO_THREADS
247 
248 _LIBCXXABI_FUNC_VIS int __cxa_guard_acquire(guard_type *guard_object) {
249     return !is_initialized(guard_object);
250 }
251 
252 _LIBCXXABI_FUNC_VIS void __cxa_guard_release(guard_type *guard_object) {
253     *guard_object = 0;
254     set_initialized(guard_object);
255 }
256 
257 _LIBCXXABI_FUNC_VIS void __cxa_guard_abort(guard_type *guard_object) {
258     *guard_object = 0;
259 }
260 
261 #endif // !_LIBCXXABI_HAS_NO_THREADS
262 
263 }  // extern "C"
264 
265 }  // __cxxabiv1
266