Skip to content

Commit 70ebeab

Browse files
committed
Rewrite cxa guard implementation.
This patch does three main things: (1) It re-writes the cxa guard implementation to make it testable. (2) Adds support for recursive init detection on non-apple platforms. (3) It adds a futex based implementation. The futex based implementation locks and notifies on a per-object basis, unlike the current implementation which uses a global lock for all objects. Once this patch settles I'll turn it on by default when supported. llvm-svn: 359060
1 parent 0b09875 commit 70ebeab

File tree

5 files changed

+1127
-263
lines changed

5 files changed

+1127
-263
lines changed

libcxxabi/src/cxa_guard.cpp

Lines changed: 14 additions & 262 deletions
Original file line numberDiff line numberDiff line change
@@ -7,12 +7,12 @@
77
//===----------------------------------------------------------------------===//
88

99
#include "__cxxabi_config.h"
10+
#include "cxxabi.h"
1011

11-
#include "abort_message.h"
12-
#include <__threading_support>
13-
14-
#include <stdint.h>
15-
#include <string.h>
12+
// Tell the implementation that we're building the actual implementation
13+
// (and not testing it)
14+
#define BUILDING_CXA_GUARD
15+
#include "cxa_guard_impl.h"
1616

1717
/*
1818
This implementation must be careful to not call code external to this file
@@ -24,278 +24,30 @@
2424
to not be a problem.
2525
*/
2626

27-
namespace __cxxabiv1
28-
{
29-
30-
namespace
31-
{
32-
33-
enum InitializationResult {
34-
INIT_COMPLETE,
35-
INIT_NOT_COMPLETE,
36-
};
27+
namespace __cxxabiv1 {
3728

3829
#if defined(_LIBCXXABI_GUARD_ABI_ARM)
39-
// A 32-bit, 4-byte-aligned static data value. The least significant 2 bits must
40-
// be statically initialized to 0.
41-
typedef uint32_t guard_type;
30+
using guard_type = uint32_t;
4231
#else
43-
typedef uint64_t guard_type;
44-
#endif
45-
46-
#if !defined(_LIBCXXABI_HAS_NO_THREADS) && defined(__APPLE__) && \
47-
!defined(_LIBCXXABI_GUARD_ABI_ARM)
48-
// This is a special-case pthread dependency for Mac. We can't pull this
49-
// out into libcxx's threading API (__threading_support) because not all
50-
// supported Mac environments provide this function (in pthread.h). To
51-
// make it possible to build/use libcxx in those environments, we have to
52-
// keep this pthread dependency local to libcxxabi. If there is some
53-
// convenient way to detect precisely when pthread_mach_thread_np is
54-
// available in a given Mac environment, it might still be possible to
55-
// bury this dependency in __threading_support.
56-
#ifndef _LIBCPP_HAS_THREAD_API_PTHREAD
57-
#error "How do I pthread_mach_thread_np()?"
58-
#endif
59-
#define LIBCXXABI_HAS_DEADLOCK_DETECTION
60-
#define LOCK_ID_FOR_THREAD() pthread_mach_thread_np(std::__libcpp_thread_get_current_id())
61-
typedef uint32_t lock_type;
62-
#else
63-
#define LOCK_ID_FOR_THREAD() true
64-
typedef bool lock_type;
65-
#endif
66-
67-
enum class OnRelease : char { UNLOCK, UNLOCK_AND_BROADCAST };
68-
69-
struct GlobalMutexGuard {
70-
explicit GlobalMutexGuard(const char* calling_func, OnRelease on_release)
71-
: calling_func(calling_func), on_release(on_release) {
72-
#ifndef _LIBCXXABI_HAS_NO_THREADS
73-
if (std::__libcpp_mutex_lock(&guard_mut))
74-
abort_message("%s failed to acquire mutex", calling_func);
75-
#endif
76-
}
77-
78-
~GlobalMutexGuard() {
79-
#ifndef _LIBCXXABI_HAS_NO_THREADS
80-
if (std::__libcpp_mutex_unlock(&guard_mut))
81-
abort_message("%s failed to release mutex", calling_func);
82-
if (on_release == OnRelease::UNLOCK_AND_BROADCAST) {
83-
if (std::__libcpp_condvar_broadcast(&guard_cv))
84-
abort_message("%s failed to broadcast condition variable",
85-
calling_func);
86-
}
87-
#endif
88-
}
89-
90-
void wait_for_signal() {
91-
#ifndef _LIBCXXABI_HAS_NO_THREADS
92-
if (std::__libcpp_condvar_wait(&guard_cv, &guard_mut))
93-
abort_message("%s condition variable wait failed", calling_func);
94-
#endif
95-
}
96-
97-
private:
98-
GlobalMutexGuard(GlobalMutexGuard const&) = delete;
99-
GlobalMutexGuard& operator=(GlobalMutexGuard const&) = delete;
100-
101-
const char* const calling_func;
102-
OnRelease on_release;
103-
104-
#ifndef _LIBCXXABI_HAS_NO_THREADS
105-
static std::__libcpp_mutex_t guard_mut;
106-
static std::__libcpp_condvar_t guard_cv;
32+
using guard_type = uint64_t;
10733
#endif
108-
};
109-
110-
#ifndef _LIBCXXABI_HAS_NO_THREADS
111-
std::__libcpp_mutex_t GlobalMutexGuard::guard_mut = _LIBCPP_MUTEX_INITIALIZER;
112-
std::__libcpp_condvar_t GlobalMutexGuard::guard_cv =
113-
_LIBCPP_CONDVAR_INITIALIZER;
114-
#endif
115-
116-
struct GuardObject;
117-
118-
/// GuardValue - An abstraction for accessing the various fields and bits of
119-
/// the guard object.
120-
struct GuardValue {
121-
private:
122-
explicit GuardValue(guard_type v) : value(v) {}
123-
friend struct GuardObject;
124-
125-
public:
126-
/// Functions returning the values used to represent the uninitialized,
127-
/// initialized, and initialization pending states.
128-
static GuardValue ZERO();
129-
static GuardValue INIT_COMPLETE();
130-
static GuardValue INIT_PENDING();
131-
132-
/// Returns true if the guard value represents that the initialization is
133-
/// complete.
134-
bool is_initialization_complete() const;
135-
136-
/// Returns true if the guard value represents that the initialization is
137-
/// currently pending.
138-
bool is_initialization_pending() const;
139-
140-
/// Returns the lock value for the current guard value.
141-
lock_type get_lock_value() const;
142-
143-
private:
144-
// Returns a guard object corresponding to the specified lock value.
145-
static guard_type guard_value_from_lock(lock_type l);
146-
147-
// Returns the lock value represented by the specified guard object.
148-
static lock_type lock_value_from_guard(guard_type g);
149-
150-
private:
151-
guard_type value;
152-
};
153-
154-
/// GuardObject - Manages correctly reading and writing to the guard object.
155-
struct GuardObject {
156-
explicit GuardObject(guard_type *g) : guard(g) {}
157-
158-
// Read the current value of the guard object.
159-
// TODO: Make this read atomic.
160-
GuardValue read() const;
161-
162-
// Write the specified value to the guard object.
163-
// TODO: Make this atomic
164-
void write(GuardValue new_val);
165-
166-
private:
167-
GuardObject(const GuardObject&) = delete;
168-
GuardObject& operator=(const GuardObject&) = delete;
169-
170-
guard_type *guard;
171-
};
172-
173-
} // unnamed namespace
17434

17535
extern "C"
17636
{
177-
17837
_LIBCXXABI_FUNC_VIS int __cxa_guard_acquire(guard_type* raw_guard_object) {
179-
GlobalMutexGuard gmutex("__cxa_guard_acquire", OnRelease::UNLOCK);
180-
GuardObject guard(raw_guard_object);
181-
GuardValue current_value = guard.read();
182-
183-
if (current_value.is_initialization_complete())
184-
return INIT_COMPLETE;
185-
186-
const GuardValue LOCK_ID = GuardValue::INIT_PENDING();
187-
#ifdef LIBCXXABI_HAS_DEADLOCK_DETECTION
188-
if (current_value.is_initialization_pending() &&
189-
current_value.get_lock_value() == LOCK_ID.get_lock_value()) {
190-
abort_message("__cxa_guard_acquire detected deadlock");
191-
}
192-
#endif
193-
while (current_value.is_initialization_pending()) {
194-
gmutex.wait_for_signal();
195-
current_value = guard.read();
196-
}
197-
if (current_value.is_initialization_complete())
198-
return INIT_COMPLETE;
199-
200-
guard.write(LOCK_ID);
201-
return INIT_NOT_COMPLETE;
38+
SelectedImplementation imp(raw_guard_object);
39+
return static_cast<int>(imp.cxa_guard_acquire());
20240
}
20341

20442
_LIBCXXABI_FUNC_VIS void __cxa_guard_release(guard_type *raw_guard_object) {
205-
GlobalMutexGuard gmutex("__cxa_guard_release",
206-
OnRelease::UNLOCK_AND_BROADCAST);
207-
GuardObject guard(raw_guard_object);
208-
guard.write(GuardValue::ZERO());
209-
guard.write(GuardValue::INIT_COMPLETE());
43+
SelectedImplementation imp(raw_guard_object);
44+
imp.cxa_guard_release();
21045
}
21146

21247
_LIBCXXABI_FUNC_VIS void __cxa_guard_abort(guard_type *raw_guard_object) {
213-
GlobalMutexGuard gmutex("__cxa_guard_abort", OnRelease::UNLOCK_AND_BROADCAST);
214-
GuardObject guard(raw_guard_object);
215-
guard.write(GuardValue::ZERO());
48+
SelectedImplementation imp(raw_guard_object);
49+
imp.cxa_guard_abort();
21650
}
21751
} // extern "C"
21852

219-
//===----------------------------------------------------------------------===//
220-
// GuardObject Definitions
221-
//===----------------------------------------------------------------------===//
222-
223-
GuardValue GuardObject::read() const {
224-
// FIXME: Make this atomic
225-
guard_type val = *guard;
226-
return GuardValue(val);
227-
}
228-
229-
void GuardObject::write(GuardValue new_val) {
230-
// FIXME: make this atomic
231-
*guard = new_val.value;
232-
}
233-
234-
//===----------------------------------------------------------------------===//
235-
// GuardValue Definitions
236-
//===----------------------------------------------------------------------===//
237-
238-
GuardValue GuardValue::ZERO() { return GuardValue(0); }
239-
240-
GuardValue GuardValue::INIT_COMPLETE() {
241-
guard_type value = {0};
242-
#if defined(_LIBCXXABI_GUARD_ABI_ARM)
243-
value |= 1;
244-
#else
245-
char* init_bit = (char*)&value;
246-
*init_bit = 1;
247-
#endif
248-
return GuardValue(value);
249-
}
250-
251-
GuardValue GuardValue::INIT_PENDING() {
252-
return GuardValue(guard_value_from_lock(LOCK_ID_FOR_THREAD()));
253-
}
254-
255-
bool GuardValue::is_initialization_complete() const {
256-
#if defined(_LIBCXXABI_GUARD_ABI_ARM)
257-
return value & 1;
258-
#else
259-
const char* init_bit = (const char*)&value;
260-
return *init_bit;
261-
#endif
262-
}
263-
264-
bool GuardValue::is_initialization_pending() const {
265-
return lock_value_from_guard(value) != 0;
266-
}
267-
268-
lock_type GuardValue::get_lock_value() const {
269-
return lock_value_from_guard(value);
270-
}
271-
272-
// Create a guard object with the lock set to the specified value.
273-
guard_type GuardValue::guard_value_from_lock(lock_type l) {
274-
#if defined(__APPLE__) && !defined(_LIBCXXABI_GUARD_ABI_ARM)
275-
#if __LITTLE_ENDIAN__
276-
return static_cast<guard_type>(l) << 32;
277-
#else
278-
return static_cast<guard_type>(l);
279-
#endif
280-
#else // defined(__APPLE__) && !defined(_LIBCXXABI_GUARD_ABI_ARM)
281-
guard_type f = {0};
282-
memcpy(static_cast<char*>(static_cast<void*>(&f)) + 1, &l, sizeof(lock_type));
283-
return f;
284-
#endif // defined(__APPLE__) && !defined(_LIBCXXABI_GUARD_ABI_ARM)
285-
}
286-
287-
lock_type GuardValue::lock_value_from_guard(guard_type g) {
288-
#if defined(__APPLE__) && !defined(_LIBCXXABI_GUARD_ABI_ARM)
289-
#if __LITTLE_ENDIAN__
290-
return static_cast<lock_type>(g >> 32);
291-
#else
292-
return static_cast<lock_type>(g);
293-
#endif
294-
#else // defined(__APPLE__) && !defined(_LIBCXXABI_GUARD_ABI_ARM)
295-
uint8_t guard_bytes[sizeof(guard_type)];
296-
memcpy(&guard_bytes, &g, sizeof(guard_type));
297-
return guard_bytes[1] != 0;
298-
#endif // defined(__APPLE__) && !defined(_LIBCXXABI_GUARD_ABI_ARM)
299-
}
300-
30153
} // __cxxabiv1

0 commit comments

Comments
 (0)