diff --git a/libcxxabi/src/cxa_guard_impl.h b/libcxxabi/src/cxa_guard_impl.h --- a/libcxxabi/src/cxa_guard_impl.h +++ b/libcxxabi/src/cxa_guard_impl.h @@ -136,6 +136,12 @@ IntType* b_; }; +#ifdef _LIBCXXABI_HAS_NO_THREADS +static inline bool IsThreaded() { return false; } +#else +static inline bool IsThreaded() { return std::__libcpp_has_spawned_other_threads(); } +#endif + //===----------------------------------------------------------------------===// // PlatformGetThreadID //===----------------------------------------------------------------------===// @@ -504,6 +510,57 @@ static_assert(Wait != nullptr && Wake != nullptr, ""); }; +/// InitByteHybrid - At runtime determines whether we're in a single-threaded +/// or multi-threaded environment. Then manages reads and writes to the init +/// byte by delegating to either InitByteNoThreads or InitByteGlobalMutex. +template +struct InitByteHybrid { + using InitByteGlobalMutexT = InitByteGlobalMutex; + +private: + InitByteNoThreads init_byte_no_threads; + InitByteGlobalMutexT init_byte_global_mutex; + +public: + InitByteHybrid() = delete; + InitByteHybrid(InitByteHybrid const&) = delete; + InitByteHybrid& operator=(InitByteHybrid const&) = delete; + + explicit InitByteHybrid(uint8_t* _init_byte_address, uint32_t* _thread_id_address) + : init_byte_no_threads(_init_byte_address, _thread_id_address), + init_byte_global_mutex(_init_byte_address, _thread_id_address) {} + + /// The init_byte portion of cxa_guard_acquire. Returns true if + /// initialization has already been completed. + /// Note: On completion, we haven't 'acquired' ownership of anything mutex + /// related. The name is simply referring to __cxa_guard_acquire. + bool acquire() { + if (ImpSelector()) + return init_byte_global_mutex.acquire(); + else + return init_byte_no_threads.acquire(); + } + + /// The init_byte portion of cxa_guard_release. + /// Note: On completion, we haven't 'released' ownership of anything mutex + /// related. The name is simply referring to __cxa_guard_release. + void release() { + if (ImpSelector()) + return init_byte_global_mutex.release(); + else + return init_byte_no_threads.release(); + } + + /// The init_byte portion of cxa_guard_abort. + void abort() { + if (ImpSelector()) + return init_byte_global_mutex.abort(); + else + return init_byte_no_threads.abort(); + } +}; + //===----------------------------------------------------------------------===// // Guards //===----------------------------------------------------------------------===// @@ -589,6 +646,15 @@ using BaseT::BaseT; }; +/// HybridGuard - Manages initialization using a hybrid of InitByteNoThreads +/// and InitByteGlobalMutex +template +struct HybridGuard : GuardBase> { + using BaseT = typename HybridGuard::GuardBase; + using BaseT::BaseT; +}; + //===----------------------------------------------------------------------===// // //===----------------------------------------------------------------------===// @@ -600,7 +666,7 @@ template _LIBCPP_SAFE_STATIC T GlobalStatic::instance = {}; -enum class Implementation { NoThreads, GlobalMutex, Futex }; +enum class Implementation { NoThreads, GlobalMutex, Futex, Hybrid }; template struct SelectImplementation; @@ -621,11 +687,19 @@ using type = FutexGuard; }; +template <> +struct SelectImplementation { + using type = HybridGuard::instance, + GlobalStatic::instance, PlatformThreadID>; +}; + // TODO(EricWF): We should prefer the futex implementation when available. But // it should be done in a separate step from adding the implementation. constexpr Implementation CurrentImplementation = #if defined(_LIBCXXABI_HAS_NO_THREADS) Implementation::NoThreads; +#elif defined(__MVS__) + Implementation::Hybrid; #elif defined(_LIBCXXABI_USE_FUTEX) Implementation::Futex; #else diff --git a/libcxxabi/test/guard_test_basic.pass.cpp b/libcxxabi/test/guard_test_basic.pass.cpp --- a/libcxxabi/test/guard_test_basic.pass.cpp +++ b/libcxxabi/test/guard_test_basic.pass.cpp @@ -115,11 +115,20 @@ void NopFutexWake(int*) { assert(false); } uint32_t MockGetThreadID() { return 0; } +bool ImpSelectorFalse() { return false; } +bool ImpSelectorTrue() { return true; } + int main(int, char**) { { #if defined(_LIBCXXABI_HAS_NO_THREADS) static_assert(CurrentImplementation == Implementation::NoThreads, ""); static_assert(std::is_same::value, ""); +#elif defined(__MVS__) + static_assert(CurrentImplementation == Implementation::Hybrid, ""); + static_assert(std::is_same::instance, + GlobalStatic::instance>>::value, + ""); #else static_assert(CurrentImplementation == Implementation::GlobalMutex, ""); static_assert(std::is_same::test(); Tests::test(); } + { + using HybridImplNothreads = + HybridGuard; + using HybridImplThreaded = + HybridGuard; + Tests::test(); + Tests::test(); + Tests::test(); + Tests::test(); + } return 0; } diff --git a/libcxxabi/test/guard_threaded_test.pass.cpp b/libcxxabi/test/guard_threaded_test.pass.cpp --- a/libcxxabi/test/guard_threaded_test.pass.cpp +++ b/libcxxabi/test/guard_threaded_test.pass.cpp @@ -327,6 +327,7 @@ void test_all_impls() { using MutexImpl = SelectImplementation::type; + using HybridImpl = SelectImplementation::type; // Attempt to test the Futex based implementation if it's supported on the // target platform. @@ -338,6 +339,7 @@ >::type; test_impl(); + test_impl(); if (PlatformSupportsFutex()) test_impl(); }