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 
15 #if !LIBCXXABI_HAS_NO_THREADS
16 #  include <pthread.h>
17 #endif
18 #include <stdint.h>
19 
20 /*
21     This implementation must be careful to not call code external to this file
22     which will turn around and try to call __cxa_guard_acquire reentrantly.
23     For this reason, the headers of this file are as restricted as possible.
24     Previous implementations of this code for __APPLE__ have used
25     pthread_mutex_lock and the abort_message utility without problem.  This
26     implementation also uses pthread_cond_wait which has tested to not be a
27     problem.
28 */
29 
30 namespace __cxxabiv1
31 {
32 
33 namespace
34 {
35 
36 #ifdef __arm__
37 
38 // A 32-bit, 4-byte-aligned static data value. The least significant 2 bits must
39 // be statically initialized to 0.
40 typedef uint32_t guard_type;
41 
42 // Test the lowest bit.
43 inline bool is_initialized(guard_type* guard_object) {
44     return (*guard_object) & 1;
45 }
46 
47 inline void set_initialized(guard_type* guard_object) {
48     *guard_object |= 1;
49 }
50 
51 #else
52 
53 typedef uint64_t guard_type;
54 
55 bool is_initialized(guard_type* guard_object) {
56     char* initialized = (char*)guard_object;
57     return *initialized;
58 }
59 
60 void set_initialized(guard_type* guard_object) {
61     char* initialized = (char*)guard_object;
62     *initialized = 1;
63 }
64 
65 #endif
66 
67 #if !LIBCXXABI_HAS_NO_THREADS
68 pthread_mutex_t guard_mut = PTHREAD_MUTEX_INITIALIZER;
69 pthread_cond_t  guard_cv  = PTHREAD_COND_INITIALIZER;
70 #endif
71 
72 #if defined(__APPLE__) && !defined(__arm__)
73 
74 typedef uint32_t lock_type;
75 
76 #if __LITTLE_ENDIAN__
77 
78 inline
79 lock_type
80 get_lock(uint64_t x)
81 {
82     return static_cast<lock_type>(x >> 32);
83 }
84 
85 inline
86 void
87 set_lock(uint64_t& x, lock_type y)
88 {
89     x = static_cast<uint64_t>(y) << 32;
90 }
91 
92 #else  // __LITTLE_ENDIAN__
93 
94 inline
95 lock_type
96 get_lock(uint64_t x)
97 {
98     return static_cast<lock_type>(x);
99 }
100 
101 inline
102 void
103 set_lock(uint64_t& x, lock_type y)
104 {
105     x = y;
106 }
107 
108 #endif  // __LITTLE_ENDIAN__
109 
110 #else  // !__APPLE__ || __arm__
111 
112 typedef bool lock_type;
113 
114 inline
115 lock_type
116 get_lock(uint64_t x)
117 {
118     union
119     {
120         uint64_t guard;
121         uint8_t lock[2];
122     } f = {x};
123     return f.lock[1] != 0;
124 }
125 
126 inline
127 void
128 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 
139 inline
140 lock_type
141 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 
151 inline
152 void
153 set_lock(uint32_t& x, lock_type y)
154 {
155     union
156     {
157         uint32_t guard;
158         uint8_t lock[2];
159     } f = {0};
160     f.lock[1] = y;
161     x = f.guard;
162 }
163 
164 #endif  // __APPLE__
165 
166 }  // unnamed namespace
167 
168 extern "C"
169 {
170 
171 #if LIBCXXABI_HAS_NO_THREADS
172 _LIBCXXABI_FUNC_VIS int __cxa_guard_acquire(guard_type *guard_object) {
173     return !is_initialized(guard_object);
174 }
175 
176 _LIBCXXABI_FUNC_VIS void __cxa_guard_release(guard_type *guard_object) {
177     *guard_object = 0;
178     set_initialized(guard_object);
179 }
180 
181 _LIBCXXABI_FUNC_VIS void __cxa_guard_abort(guard_type *guard_object) {
182     *guard_object = 0;
183 }
184 
185 #else // !LIBCXXABI_HAS_NO_THREADS
186 
187 _LIBCXXABI_FUNC_VIS int __cxa_guard_acquire(guard_type *guard_object) {
188     char* initialized = (char*)guard_object;
189     if (pthread_mutex_lock(&guard_mut))
190         abort_message("__cxa_guard_acquire failed to acquire mutex");
191     int result = *initialized == 0;
192     if (result)
193     {
194 #if defined(__APPLE__) && !defined(__arm__)
195         const lock_type id = pthread_mach_thread_np(pthread_self());
196         lock_type lock = get_lock(*guard_object);
197         if (lock)
198         {
199             // if this thread set lock for this same guard_object, abort
200             if (lock == id)
201                 abort_message("__cxa_guard_acquire detected deadlock");
202             do
203             {
204                 if (pthread_cond_wait(&guard_cv, &guard_mut))
205                     abort_message("__cxa_guard_acquire condition variable wait failed");
206                 lock = get_lock(*guard_object);
207             } while (lock);
208             result = !is_initialized(guard_object);
209             if (result)
210                 set_lock(*guard_object, id);
211         }
212         else
213             set_lock(*guard_object, id);
214 #else  // !__APPLE__ || __arm__
215         while (get_lock(*guard_object))
216             if (pthread_cond_wait(&guard_cv, &guard_mut))
217                 abort_message("__cxa_guard_acquire condition variable wait failed");
218         result = *initialized == 0;
219         if (result)
220             set_lock(*guard_object, true);
221 #endif  // !__APPLE__ || __arm__
222     }
223     if (pthread_mutex_unlock(&guard_mut))
224         abort_message("__cxa_guard_acquire failed to release mutex");
225     return result;
226 }
227 
228 _LIBCXXABI_FUNC_VIS void __cxa_guard_release(guard_type *guard_object) {
229     if (pthread_mutex_lock(&guard_mut))
230         abort_message("__cxa_guard_release failed to acquire mutex");
231     *guard_object = 0;
232     set_initialized(guard_object);
233     if (pthread_mutex_unlock(&guard_mut))
234         abort_message("__cxa_guard_release failed to release mutex");
235     if (pthread_cond_broadcast(&guard_cv))
236         abort_message("__cxa_guard_release failed to broadcast condition variable");
237 }
238 
239 _LIBCXXABI_FUNC_VIS void __cxa_guard_abort(guard_type *guard_object) {
240     if (pthread_mutex_lock(&guard_mut))
241         abort_message("__cxa_guard_abort failed to acquire mutex");
242     *guard_object = 0;
243     if (pthread_mutex_unlock(&guard_mut))
244         abort_message("__cxa_guard_abort failed to release mutex");
245     if (pthread_cond_broadcast(&guard_cv))
246         abort_message("__cxa_guard_abort failed to broadcast condition variable");
247 }
248 
249 #endif // !LIBCXXABI_HAS_NO_THREADS
250 
251 }  // extern "C"
252 
253 }  // __cxxabiv1
254