| //===---------------------------- cxa_guard.cpp ---------------------------===// |
| // |
| // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
| // See https://llvm.org/LICENSE.txt for license information. |
| // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
| // |
| //===----------------------------------------------------------------------===// |
| |
| #include "__cxxabi_config.h" |
| |
| #include "abort_message.h" |
| #include <__threading_support> |
| |
| #include <stdint.h> |
| #include <string.h> |
| |
| /* |
| This implementation must be careful to not call code external to this file |
| which will turn around and try to call __cxa_guard_acquire reentrantly. |
| For this reason, the headers of this file are as restricted as possible. |
| Previous implementations of this code for __APPLE__ have used |
| std::__libcpp_mutex_lock and the abort_message utility without problem. This |
| implementation also uses std::__libcpp_condvar_wait which has tested |
| to not be a problem. |
| */ |
| |
| namespace __cxxabiv1 |
| { |
| |
| namespace |
| { |
| |
| enum InitializationResult { |
| INIT_COMPLETE, |
| INIT_NOT_COMPLETE, |
| }; |
| |
| #if defined(_LIBCXXABI_GUARD_ABI_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; |
| #else |
| typedef uint64_t guard_type; |
| #endif |
| |
| #if !defined(_LIBCXXABI_HAS_NO_THREADS) && defined(__APPLE__) && \ |
| !defined(_LIBCXXABI_GUARD_ABI_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 }; |
| |
| struct GlobalMutexGuard { |
| explicit GlobalMutexGuard(const char* calling_func, OnRelease on_release) |
| : calling_func(calling_func), on_release(on_release) { |
| #ifndef _LIBCXXABI_HAS_NO_THREADS |
| if (std::__libcpp_mutex_lock(&guard_mut)) |
| abort_message("%s failed to acquire mutex", calling_func); |
| #endif |
| } |
| |
| ~GlobalMutexGuard() { |
| #ifndef _LIBCXXABI_HAS_NO_THREADS |
| if (std::__libcpp_mutex_unlock(&guard_mut)) |
| abort_message("%s failed to release mutex", calling_func); |
| if (on_release == OnRelease::UNLOCK_AND_BROADCAST) { |
| if (std::__libcpp_condvar_broadcast(&guard_cv)) |
| abort_message("%s failed to broadcast condition variable", |
| calling_func); |
| } |
| #endif |
| } |
| |
| void wait_for_signal() { |
| #ifndef _LIBCXXABI_HAS_NO_THREADS |
| if (std::__libcpp_condvar_wait(&guard_cv, &guard_mut)) |
| abort_message("%s condition variable wait failed", calling_func); |
| #endif |
| } |
| |
| private: |
| GlobalMutexGuard(GlobalMutexGuard const&) = delete; |
| GlobalMutexGuard& operator=(GlobalMutexGuard const&) = delete; |
| |
| const char* const calling_func; |
| OnRelease on_release; |
| |
| #ifndef _LIBCXXABI_HAS_NO_THREADS |
| static std::__libcpp_mutex_t guard_mut; |
| static std::__libcpp_condvar_t guard_cv; |
| #endif |
| }; |
| |
| #ifndef _LIBCXXABI_HAS_NO_THREADS |
| std::__libcpp_mutex_t GlobalMutexGuard::guard_mut = _LIBCPP_MUTEX_INITIALIZER; |
| std::__libcpp_condvar_t GlobalMutexGuard::guard_cv = |
| _LIBCPP_CONDVAR_INITIALIZER; |
| #endif |
| |
| struct GuardObject; |
| |
| /// 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; |
| |
| 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(); |
| |
| /// Returns true if the guard value represents that the initialization is |
| /// complete. |
| bool is_initialization_complete() const; |
| |
| /// Returns true if the guard value represents that the initialization is |
| /// currently pending. |
| bool is_initialization_pending() const; |
| |
| /// Returns the lock value for the current guard value. |
| lock_type get_lock_value() const; |
| |
| private: |
| // Returns a guard object corresponding to the specified lock value. |
| static guard_type guard_value_from_lock(lock_type l); |
| |
| // Returns the lock value represented by the specified guard object. |
| static lock_type lock_value_from_guard(guard_type g); |
| |
| private: |
| guard_type value; |
| }; |
| |
| /// GuardObject - Manages correctly reading and writing to the guard object. |
| struct GuardObject { |
| explicit GuardObject(guard_type *g) : guard(g) {} |
| |
| // Read the current value of the guard object. |
| // TODO: Make this read atomic. |
| GuardValue read() const; |
| |
| // Write the specified value to the guard object. |
| // TODO: Make this atomic |
| void write(GuardValue new_val); |
| |
| private: |
| GuardObject(const GuardObject&) = delete; |
| GuardObject& operator=(const GuardObject&) = delete; |
| |
| guard_type *guard; |
| }; |
| |
| } // unnamed namespace |
| |
| extern "C" |
| { |
| |
| _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; |
| } |
| |
| _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()); |
| } |
| |
| _LIBCXXABI_FUNC_VIS void __cxa_guard_abort(guard_type *raw_guard_object) { |
| GlobalMutexGuard gmutex("__cxa_guard_abort", OnRelease::UNLOCK_AND_BROADCAST); |
| GuardObject guard(raw_guard_object); |
| guard.write(GuardValue::ZERO()); |
| } |
| } // extern "C" |
| |
| //===----------------------------------------------------------------------===// |
| // GuardObject Definitions |
| //===----------------------------------------------------------------------===// |
| |
| GuardValue GuardObject::read() const { |
| // FIXME: Make this atomic |
| guard_type val = *guard; |
| return GuardValue(val); |
| } |
| |
| void GuardObject::write(GuardValue new_val) { |
| // FIXME: make this atomic |
| *guard = new_val.value; |
| } |
| |
| //===----------------------------------------------------------------------===// |
| // GuardValue Definitions |
| //===----------------------------------------------------------------------===// |
| |
| GuardValue GuardValue::ZERO() { return GuardValue(0); } |
| |
| GuardValue GuardValue::INIT_COMPLETE() { |
| guard_type value = {0}; |
| #if defined(_LIBCXXABI_GUARD_ABI_ARM) |
| value |= 1; |
| #else |
| char* init_bit = (char*)&value; |
| *init_bit = 1; |
| #endif |
| return GuardValue(value); |
| } |
| |
| GuardValue GuardValue::INIT_PENDING() { |
| return GuardValue(guard_value_from_lock(LOCK_ID_FOR_THREAD())); |
| } |
| |
| bool GuardValue::is_initialization_complete() const { |
| #if defined(_LIBCXXABI_GUARD_ABI_ARM) |
| return value & 1; |
| #else |
| const char* init_bit = (const char*)&value; |
| return *init_bit; |
| #endif |
| } |
| |
| bool GuardValue::is_initialization_pending() const { |
| return lock_value_from_guard(value) != 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(_LIBCXXABI_GUARD_ABI_ARM) |
| #if __LITTLE_ENDIAN__ |
| return static_cast<guard_type>(l) << 32; |
| #else |
| return static_cast<guard_type>(l); |
| #endif |
| #else // defined(__APPLE__) && !defined(_LIBCXXABI_GUARD_ABI_ARM) |
| guard_type f = {0}; |
| memcpy(static_cast<char*>(static_cast<void*>(&f)) + 1, &l, sizeof(lock_type)); |
| return f; |
| #endif // defined(__APPLE__) && !defined(_LIBCXXABI_GUARD_ABI_ARM) |
| } |
| |
| lock_type GuardValue::lock_value_from_guard(guard_type g) { |
| #if defined(__APPLE__) && !defined(_LIBCXXABI_GUARD_ABI_ARM) |
| #if __LITTLE_ENDIAN__ |
| return static_cast<lock_type>(g >> 32); |
| #else |
| return static_cast<lock_type>(g); |
| #endif |
| #else // defined(__APPLE__) && !defined(_LIBCXXABI_GUARD_ABI_ARM) |
| uint8_t guard_bytes[sizeof(guard_type)]; |
| memcpy(&guard_bytes, &g, sizeof(guard_type)); |
| return guard_bytes[1] != 0; |
| #endif // defined(__APPLE__) && !defined(_LIBCXXABI_GUARD_ABI_ARM) |
| } |
| |
| } // __cxxabiv1 |