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