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