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 
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.
55 inline bool is_initialized(guard_type* guard_object) {
56     return (*guard_object) & 1;
57 }
58 
59 #else
60 
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
82 get_lock(uint64_t x)
83 {
84     return static_cast<lock_type>(x >> 32);
85 }
86 
87 inline
88 void
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
98 get_lock(uint64_t x)
99 {
100     return static_cast<lock_type>(x);
101 }
102 
103 inline
104 void
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
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
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
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
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
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         const lock_type id = std::__libcpp_thread_get_port();
183         lock_type lock = get_lock(*guard_object);
184         if (lock)
185         {
186             // if this thread set lock for this same guard_object, abort
187             if (lock == id)
188                 abort_message("__cxa_guard_acquire detected deadlock");
189             do
190             {
191                 if (std::__libcpp_condvar_wait(&guard_cv, &guard_mut))
192                     abort_message("__cxa_guard_acquire condition variable wait failed");
193                 lock = get_lock(*guard_object);
194             } while (lock);
195             result = !is_initialized(guard_object);
196             if (result)
197                 set_lock(*guard_object, id);
198         }
199         else
200             set_lock(*guard_object, id);
201 #else  // !__APPLE__ || __arm__
202         while (get_lock(*guard_object))
203             if (std::__libcpp_condvar_wait(&guard_cv, &guard_mut))
204                 abort_message("__cxa_guard_acquire condition variable wait failed");
205         result = *initialized == 0;
206         if (result)
207             set_lock(*guard_object, true);
208 #endif  // !__APPLE__ || __arm__
209     }
210     if (std::__libcpp_mutex_unlock(&guard_mut))
211         abort_message("__cxa_guard_acquire failed to release mutex");
212     return result;
213 }
214 
215 _LIBCXXABI_FUNC_VIS void __cxa_guard_release(guard_type *guard_object) {
216     if (std::__libcpp_mutex_lock(&guard_mut))
217         abort_message("__cxa_guard_release failed to acquire mutex");
218     *guard_object = 0;
219     set_initialized(guard_object);
220     if (std::__libcpp_mutex_unlock(&guard_mut))
221         abort_message("__cxa_guard_release failed to release mutex");
222     if (std::__libcpp_condvar_broadcast(&guard_cv))
223         abort_message("__cxa_guard_release failed to broadcast condition variable");
224 }
225 
226 _LIBCXXABI_FUNC_VIS void __cxa_guard_abort(guard_type *guard_object) {
227     if (std::__libcpp_mutex_lock(&guard_mut))
228         abort_message("__cxa_guard_abort failed to acquire mutex");
229     *guard_object = 0;
230     if (std::__libcpp_mutex_unlock(&guard_mut))
231         abort_message("__cxa_guard_abort failed to release mutex");
232     if (std::__libcpp_condvar_broadcast(&guard_cv))
233         abort_message("__cxa_guard_abort failed to broadcast condition variable");
234 }
235 
236 #else // _LIBCXXABI_HAS_NO_THREADS
237 
238 _LIBCXXABI_FUNC_VIS int __cxa_guard_acquire(guard_type *guard_object) {
239     return !is_initialized(guard_object);
240 }
241 
242 _LIBCXXABI_FUNC_VIS void __cxa_guard_release(guard_type *guard_object) {
243     *guard_object = 0;
244     set_initialized(guard_object);
245 }
246 
247 _LIBCXXABI_FUNC_VIS void __cxa_guard_abort(guard_type *guard_object) {
248     *guard_object = 0;
249 }
250 
251 #endif // !_LIBCXXABI_HAS_NO_THREADS
252 
253 }  // extern "C"
254 
255 }  // __cxxabiv1
256