diff --git a/libcxxabi/src/cxa_guard.cpp b/libcxxabi/src/cxa_guard.cpp index 9243ff4af7a2..cb4e5661e7bb 100644 --- a/libcxxabi/src/cxa_guard.cpp +++ b/libcxxabi/src/cxa_guard.cpp @@ -57,9 +57,53 @@ bool is_initialized(guard_type* guard_object) { } #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 -std::__libcpp_mutex_t guard_mut = _LIBCPP_MUTEX_INITIALIZER; -std::__libcpp_condvar_t guard_cv = _LIBCPP_CONDVAR_INITIALIZER; + 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 #if defined(__APPLE__) && !defined(__arm__) @@ -160,96 +204,65 @@ inline void set_lock(uint32_t& x, lock_type y) extern "C" { -#ifndef _LIBCXXABI_HAS_NO_THREADS _LIBCXXABI_FUNC_VIS int __cxa_guard_acquire(guard_type *guard_object) { - if (std::__libcpp_mutex_lock(&guard_mut)) - abort_message("__cxa_guard_acquire failed to acquire mutex"); - int result = !is_initialized(guard_object); - if (result) - { -#if 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()); - #else - #error "How do I pthread_mach_thread_np()?" - #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 - { - if (std::__libcpp_condvar_wait(&guard_cv, &guard_mut)) - abort_message("__cxa_guard_acquire condition variable wait failed"); - 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); + 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()); +#else +#error "How do I pthread_mach_thread_np()?" +#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)) - if (std::__libcpp_condvar_wait(&guard_cv, &guard_mut)) - abort_message("__cxa_guard_acquire condition variable wait failed"); - result = !is_initialized(guard_object); - if (result) - set_lock(*guard_object, true); -#endif // !__APPLE__ || __arm__ + while (get_lock(*guard_object)) { + gmutex.wait_for_signal(); } - if (std::__libcpp_mutex_unlock(&guard_mut)) - abort_message("__cxa_guard_acquire failed to release mutex"); - return result; + 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) { - if (std::__libcpp_mutex_lock(&guard_mut)) - abort_message("__cxa_guard_release failed to acquire mutex"); - *guard_object = 0; - set_initialized(guard_object); - if (std::__libcpp_mutex_unlock(&guard_mut)) - abort_message("__cxa_guard_release failed to release mutex"); - if (std::__libcpp_condvar_broadcast(&guard_cv)) - abort_message("__cxa_guard_release failed to broadcast condition variable"); + GlobalMutexGuard gmutex("__cxa_guard_release", + OnRelease::UNLOCK_AND_BROADCAST); + *guard_object = 0; + set_initialized(guard_object); } _LIBCXXABI_FUNC_VIS void __cxa_guard_abort(guard_type *guard_object) { - if (std::__libcpp_mutex_lock(&guard_mut)) - abort_message("__cxa_guard_abort failed to acquire mutex"); - *guard_object = 0; - if (std::__libcpp_mutex_unlock(&guard_mut)) - abort_message("__cxa_guard_abort failed to release mutex"); - if (std::__libcpp_condvar_broadcast(&guard_cv)) - abort_message("__cxa_guard_abort failed to broadcast condition variable"); + GlobalMutexGuard gmutex("__cxa_guard_abort", OnRelease::UNLOCK); + *guard_object = 0; } -#else // _LIBCXXABI_HAS_NO_THREADS - -_LIBCXXABI_FUNC_VIS int __cxa_guard_acquire(guard_type *guard_object) { - return !is_initialized(guard_object); -} - -_LIBCXXABI_FUNC_VIS void __cxa_guard_release(guard_type *guard_object) { - *guard_object = 0; - set_initialized(guard_object); -} - -_LIBCXXABI_FUNC_VIS void __cxa_guard_abort(guard_type *guard_object) { - *guard_object = 0; -} - -#endif // !_LIBCXXABI_HAS_NO_THREADS } // extern "C"