Create RAII lock guard for global initialization lock.

This patch is a part of a series of cleanups to cxa_guard.cpp.
It should introduce no functionality change.

This patch refactors the use of the global mutex and condvar into
a RAII lock guard class. This improves correctness (since unlocks can't
be forgotten). It also allows the unification of the non-threading and
threading implementations.

llvm-svn: 357669
This commit is contained in:
Eric Fiselier 2019-04-04 02:54:42 +00:00
parent 690c70de76
commit f5de7ad211
1 changed files with 94 additions and 81 deletions

View File

@ -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"