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