summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorEric Fiselier <eric@efcs.ca>2021-06-09 20:59:07 +0000
committerAutomerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>2021-06-09 20:59:07 +0000
commit64684d7e484f6b6640ed001f47477b7595c98301 (patch)
tree6297739b34203028b23da80bc15092e5a30dc2a6
parent42308500ae5e3439e898a03559768603e11d0059 (diff)
parent98ae54353515d1f76161d9a2ff327a5ba075d0a8 (diff)
downloadlibcxxabi-64684d7e484f6b6640ed001f47477b7595c98301.tar.gz
Further refactor cxa_guard.cpp am: 9cbec86496 am: 14ef6a7e0e am: 98ae543535
Original change: https://android-review.googlesource.com/c/platform/external/libcxxabi/+/1727432 Change-Id: I9c6078474d9112be7fb73c3d627486d730a78965
-rw-r--r--src/cxa_guard.cpp320
1 files changed, 176 insertions, 144 deletions
diff --git a/src/cxa_guard.cpp b/src/cxa_guard.cpp
index c00f702..1f5a88a 100644
--- a/src/cxa_guard.cpp
+++ b/src/cxa_guard.cpp
@@ -13,6 +13,7 @@
#include <__threading_support>
#include <stdint.h>
+#include <string.h>
/*
This implementation must be careful to not call code external to this file
@@ -30,32 +31,38 @@ namespace __cxxabiv1
namespace
{
+enum InitializationResult {
+ INIT_COMPLETE,
+ INIT_NOT_COMPLETE,
+};
+
#ifdef __arm__
// A 32-bit, 4-byte-aligned static data value. The least significant 2 bits must
// be statically initialized to 0.
typedef uint32_t guard_type;
-
-inline void set_initialized(guard_type* guard_object) {
- *guard_object |= 1;
-}
-
-// Test the lowest bit.
-inline bool is_initialized(guard_type* guard_object) {
- return (*guard_object) & 1;
-}
-
#else
typedef uint64_t guard_type;
+#endif
-void set_initialized(guard_type* guard_object) {
- char* initialized = (char*)guard_object;
- *initialized = 1;
-}
-
-bool is_initialized(guard_type* guard_object) {
- char* initialized = (char*)guard_object;
- return *initialized;
-}
+#if !defined(_LIBCXXABI_HAS_NO_THREADS) && defined(__APPLE__) && \
+ !defined(__arm__)
+// This is a special-case pthread dependency for Mac. We can't pull this
+// out into libcxx's threading API (__threading_support) because not all
+// supported Mac environments provide this function (in pthread.h). To
+// make it possible to build/use libcxx in those environments, we have to
+// keep this pthread dependency local to libcxxabi. If there is some
+// convenient way to detect precisely when pthread_mach_thread_np is
+// available in a given Mac environment, it might still be possible to
+// bury this dependency in __threading_support.
+#ifndef _LIBCPP_HAS_THREAD_API_PTHREAD
+#error "How do I pthread_mach_thread_np()?"
+#endif
+#define LIBCXXABI_HAS_DEADLOCK_DETECTION
+#define LOCK_ID_FOR_THREAD() pthread_mach_thread_np(std::__libcpp_thread_get_current_id())
+typedef uint32_t lock_type;
+#else
+#define LOCK_ID_FOR_THREAD() true
+typedef bool lock_type;
#endif
enum class OnRelease : char { UNLOCK, UNLOCK_AND_BROADCAST };
@@ -107,164 +114,189 @@ std::__libcpp_condvar_t GlobalMutexGuard::guard_cv =
_LIBCPP_CONDVAR_INITIALIZER;
#endif
-#if defined(__APPLE__) && !defined(__arm__)
+struct GuardObject;
-typedef uint32_t lock_type;
+/// GuardValue - An abstraction for accessing the various fields and bits of
+/// the guard object.
+struct GuardValue {
+private:
+ explicit GuardValue(guard_type v) : value(v) {}
+ friend struct GuardObject;
-#if __LITTLE_ENDIAN__
+public:
+ /// Functions returning the values used to represent the uninitialized,
+ /// initialized, and initialization pending states.
+ static GuardValue ZERO();
+ static GuardValue INIT_COMPLETE();
+ static GuardValue INIT_PENDING();
-inline
-lock_type
-get_lock(uint64_t x)
-{
- return static_cast<lock_type>(x >> 32);
-}
+ /// Returns true if the guard value represents that the initialization is
+ /// complete.
+ bool is_initialization_complete() const;
-inline
-void
-set_lock(uint64_t& x, lock_type y)
-{
- x = static_cast<uint64_t>(y) << 32;
-}
+ /// Returns true if the guard value represents that the initialization is
+ /// currently pending.
+ bool is_initialization_pending() const;
-#else // __LITTLE_ENDIAN__
+ /// Returns the lock value for the current guard value.
+ lock_type get_lock_value() const;
-inline
-lock_type
-get_lock(uint64_t x)
-{
- return static_cast<lock_type>(x);
-}
+private:
+ // Returns a guard object corresponding to the specified lock value.
+ static guard_type guard_value_from_lock(lock_type l);
-inline
-void
-set_lock(uint64_t& x, lock_type y)
-{
- x = y;
-}
+ // Returns the lock value represented by the specified guard object.
+ static lock_type lock_value_from_guard(guard_type g);
-#endif // __LITTLE_ENDIAN__
+private:
+ guard_type value;
+};
-#else // !__APPLE__ || __arm__
+/// GuardObject - Manages correctly reading and writing to the guard object.
+struct GuardObject {
+ explicit GuardObject(guard_type *g) : guard(g) {}
-typedef bool lock_type;
+ // Read the current value of the guard object.
+ // TODO: Make this read atomic.
+ GuardValue read() const;
-#if !defined(__arm__)
-static_assert(std::is_same<guard_type, uint64_t>::value, "");
+ // Write the specified value to the guard object.
+ // TODO: Make this atomic
+ void write(GuardValue new_val);
-inline lock_type get_lock(uint64_t x)
+private:
+ GuardObject(const GuardObject&) = delete;
+ GuardObject& operator=(const GuardObject&) = delete;
+
+ guard_type *guard;
+};
+
+} // unnamed namespace
+
+extern "C"
{
- union
- {
- uint64_t guard;
- uint8_t lock[2];
- } f = {x};
- return f.lock[1] != 0;
+
+_LIBCXXABI_FUNC_VIS int __cxa_guard_acquire(guard_type* raw_guard_object) {
+ GlobalMutexGuard gmutex("__cxa_guard_acquire", OnRelease::UNLOCK);
+ GuardObject guard(raw_guard_object);
+ GuardValue current_value = guard.read();
+
+ if (current_value.is_initialization_complete())
+ return INIT_COMPLETE;
+
+ const GuardValue LOCK_ID = GuardValue::INIT_PENDING();
+#ifdef LIBCXXABI_HAS_DEADLOCK_DETECTION
+ if (current_value.is_initialization_pending() &&
+ current_value.get_lock_value() == LOCK_ID.get_lock_value()) {
+ abort_message("__cxa_guard_acquire detected deadlock");
+ }
+#endif
+ while (current_value.is_initialization_pending()) {
+ gmutex.wait_for_signal();
+ current_value = guard.read();
+ }
+ if (current_value.is_initialization_complete())
+ return INIT_COMPLETE;
+
+ guard.write(LOCK_ID);
+ return INIT_NOT_COMPLETE;
}
-inline void set_lock(uint64_t& x, lock_type y)
-{
- union
- {
- uint64_t guard;
- uint8_t lock[2];
- } f = {0};
- f.lock[1] = y;
- x = f.guard;
+_LIBCXXABI_FUNC_VIS void __cxa_guard_release(guard_type *raw_guard_object) {
+ GlobalMutexGuard gmutex("__cxa_guard_release",
+ OnRelease::UNLOCK_AND_BROADCAST);
+ GuardObject guard(raw_guard_object);
+ guard.write(GuardValue::ZERO());
+ guard.write(GuardValue::INIT_COMPLETE());
}
-#else // defined(__arm__)
-static_assert(std::is_same<guard_type, uint32_t>::value, "");
-inline lock_type get_lock(uint32_t x)
-{
- union
- {
- uint32_t guard;
- uint8_t lock[2];
- } f = {x};
- return f.lock[1] != 0;
+_LIBCXXABI_FUNC_VIS void __cxa_guard_abort(guard_type *raw_guard_object) {
+ GlobalMutexGuard gmutex("__cxa_guard_abort", OnRelease::UNLOCK);
+ GuardObject guard(raw_guard_object);
+ guard.write(GuardValue::ZERO());
}
+} // extern "C"
-inline void set_lock(uint32_t& x, lock_type y)
-{
- union
- {
- uint32_t guard;
- uint8_t lock[2];
- } f = {0};
- f.lock[1] = y;
- x = f.guard;
+//===----------------------------------------------------------------------===//
+// GuardObject Definitions
+//===----------------------------------------------------------------------===//
+
+GuardValue GuardObject::read() const {
+ // FIXME: Make this atomic
+ guard_type val = *guard;
+ return GuardValue(val);
}
-#endif // !defined(__arm__)
+void GuardObject::write(GuardValue new_val) {
+ // FIXME: make this atomic
+ *guard = new_val.value;
+}
-#endif // __APPLE__ && !__arm__
+//===----------------------------------------------------------------------===//
+// GuardValue Definitions
+//===----------------------------------------------------------------------===//
-} // unnamed namespace
+GuardValue GuardValue::ZERO() { return GuardValue(0); }
-extern "C"
-{
+GuardValue GuardValue::INIT_COMPLETE() {
+ guard_type value = {0};
+#ifdef __arm__
+ value |= 1;
+#else
+ char* init_bit = (char*)&value;
+ *init_bit = 1;
+#endif
+ return GuardValue(value);
+}
-_LIBCXXABI_FUNC_VIS int __cxa_guard_acquire(guard_type *guard_object) {
- GlobalMutexGuard gmutex("__cxa_guard_acquire", OnRelease::UNLOCK);
- int result = !is_initialized(guard_object);
- if (result) {
-#if defined(_LIBCXXABI_HAS_NO_THREADS)
- // nothing to do
-#elif defined(__APPLE__) && !defined(__arm__)
-// This is a special-case pthread dependency for Mac. We can't pull this
-// out into libcxx's threading API (__threading_support) because not all
-// supported Mac environments provide this function (in pthread.h). To
-// make it possible to build/use libcxx in those environments, we have to
-// keep this pthread dependency local to libcxxabi. If there is some
-// convenient way to detect precisely when pthread_mach_thread_np is
-// available in a given Mac environment, it might still be possible to
-// bury this dependency in __threading_support.
-#ifdef _LIBCPP_HAS_THREAD_API_PTHREAD
- const lock_type id =
- pthread_mach_thread_np(std::__libcpp_thread_get_current_id());
+GuardValue GuardValue::INIT_PENDING() {
+ return GuardValue(guard_value_from_lock(LOCK_ID_FOR_THREAD()));
+}
+
+bool GuardValue::is_initialization_complete() const {
+#ifdef __arm__
+ return value & 1;
#else
-#error "How do I pthread_mach_thread_np()?"
+ const char* init_bit = (const char*)&value;
+ return *init_bit;
#endif
- lock_type lock = get_lock(*guard_object);
- if (lock) {
- // if this thread set lock for this same guard_object, abort
- if (lock == id)
- abort_message("__cxa_guard_acquire detected deadlock");
- do {
- gmutex.wait_for_signal();
- lock = get_lock(*guard_object);
- } while (lock);
- result = !is_initialized(guard_object);
- if (result)
- set_lock(*guard_object, id);
- } else
- set_lock(*guard_object, id);
-#else // !__APPLE__ || __arm__
- while (get_lock(*guard_object)) {
- gmutex.wait_for_signal();
- }
- result = !is_initialized(guard_object);
- if (result)
- set_lock(*guard_object, true);
-#endif // !__APPLE__ || __arm__
- }
- return result;
}
-_LIBCXXABI_FUNC_VIS void __cxa_guard_release(guard_type *guard_object) {
- GlobalMutexGuard gmutex("__cxa_guard_release",
- OnRelease::UNLOCK_AND_BROADCAST);
- *guard_object = 0;
- set_initialized(guard_object);
+bool GuardValue::is_initialization_pending() const {
+ return lock_value_from_guard(value) != 0;
}
-_LIBCXXABI_FUNC_VIS void __cxa_guard_abort(guard_type *guard_object) {
- GlobalMutexGuard gmutex("__cxa_guard_abort", OnRelease::UNLOCK);
- *guard_object = 0;
+lock_type GuardValue::get_lock_value() const {
+ return lock_value_from_guard(value);
}
+// Create a guard object with the lock set to the specified value.
+guard_type GuardValue::guard_value_from_lock(lock_type l) {
+#if defined(__APPLE__) && !defined(__arm__)
+#if __LITTLE_ENDIAN__
+ return static_cast<guard_type>(l) << 32;
+#else
+ return static_cast<guard_type>(l);
+#endif
+#else // defined(__APPLE__) && !defined(__arm__)
+ guard_type f = {0};
+ memcpy(static_cast<char*>(static_cast<void*>(&f)) + 1, &l, sizeof(lock_type));
+ return f;
+#endif // defined(__APPLE__) && !defined(__arm__)
+}
-} // extern "C"
+lock_type GuardValue::lock_value_from_guard(guard_type g) {
+#if defined(__APPLE__) && !defined(__arm__)
+#if __LITTLE_ENDIAN__
+ return static_cast<lock_type>(g >> 32);
+#else
+ return static_cast<lock_type>(g);
+#endif
+#else // defined(__APPLE__) && !defined(__arm__)
+ uint8_t guard_bytes[sizeof(guard_type)];
+ memcpy(&guard_bytes, &g, sizeof(guard_type));
+ return guard_bytes[1] != 0;
+#endif // defined(__APPLE__) && !defined(__arm__)
+}
} // __cxxabiv1