diff --git a/libcxxabi/test/test_exception_storage.pass.cpp b/libcxxabi/test/test_exception_storage.pass.cpp --- a/libcxxabi/test/test_exception_storage.pass.cpp +++ b/libcxxabi/test/test_exception_storage.pass.cpp @@ -6,71 +6,97 @@ // //===----------------------------------------------------------------------===// -#include -#include -#include -#include <__threading_support> -#include - -#include "../src/cxa_exception.h" +// UNSUPPORTED: c++03, no-localization #include "test_macros.h" -typedef __cxxabiv1::__cxa_eh_globals globals_t ; - -void *thread_code (void *parm) { - size_t *result = (size_t *) parm; - globals_t *glob1, *glob2; +#include +#include +#include +#ifndef TEST_HAS_NO_THREADS +# include +# include +# include +#endif +#include - glob1 = __cxxabiv1::__cxa_get_globals (); - if ( NULL == glob1 ) - std::printf("Got null result from __cxa_get_globals\n"); +#include "../src/cxa_exception.h" - glob2 = __cxxabiv1::__cxa_get_globals_fast (); - if ( glob1 != glob2 ) - std::printf("Got different globals!\n"); +#ifndef TEST_HAS_NO_THREADS +static int threads_remaining; +static std::mutex threads_remaining_lock; +static std::condition_variable threads_remaining_cv; +#endif - *result = (size_t) glob1; +static void thread_code(void*& globals) { #ifndef TEST_HAS_NO_THREADS - sleep ( 1 ); + std::thread::id thread_id = std::this_thread::get_id(); +#else + int thread_id = 0; #endif - return parm; -} + + globals = __cxxabiv1::__cxa_get_globals(); + if (!globals) + std::cerr << "Got null result from __cxa_get_globals on thread " << thread_id << "\n"; + + void* fast_globals = __cxxabiv1::__cxa_get_globals_fast(); + if (globals != fast_globals) { + std::cerr << "__cxa_get_globals returned " << globals << " but __cxa_get_globals_fast returned " << fast_globals + << " on thread " << thread_id << "\n"; + globals = nullptr; + } #ifndef TEST_HAS_NO_THREADS -#define NUMTHREADS 10 -size_t thread_globals [ NUMTHREADS ] = { 0 }; -std::__libcpp_thread_t threads [ NUMTHREADS ]; + // Ensure that all threads are running at the same time, since we check for + // duplicate globals below. We do this manually instead of using std::barrier + // or std::latch to avoid requiring C++20. + std::unique_lock lock(threads_remaining_lock); + --threads_remaining; + if (threads_remaining == 0) { + lock.unlock(); + threads_remaining_cv.notify_all(); + } else { + threads_remaining_cv.wait(lock, []() { return threads_remaining == 0; }); + } #endif +} int main() { #ifndef TEST_HAS_NO_THREADS -// Make the threads, let them run, and wait for them to finish - for ( int i = 0; i < NUMTHREADS; ++i ) - std::__libcpp_thread_create ( threads + i, thread_code, (void *) (thread_globals + i)); - for ( int i = 0; i < NUMTHREADS; ++i ) - std::__libcpp_thread_join ( &threads [ i ] ); + int num_threads = std::thread::hardware_concurrency(); + if (num_threads == 0) + num_threads = 4; // arbitrary fallback value - int retVal = 0; - for ( int i = 0; i < NUMTHREADS; ++i ) { - if ( 0 == thread_globals [ i ] ) { - std::printf("Thread #%d had a zero global\n", i); - retVal = 1; - } - } + std::vector thread_globals(num_threads); + std::vector threads; + threads_remaining = num_threads; + + // Make the threads, let them run, and wait for them to finish + for (int i = 0; i < num_threads; ++i) + threads.emplace_back(thread_code, std::ref(thread_globals[i])); + for (std::thread& thread : threads) + thread.join(); + + for (int i = 0; i < num_threads; ++i) { + // Either __cxa_get_globals was nullptr or __cxa_get_globals_fast returned a + // different value. We already diagnosed both in the thread. + if (!thread_globals[i]) + return 1; + } - std::sort ( thread_globals, thread_globals + NUMTHREADS ); - for ( int i = 1; i < NUMTHREADS; ++i ) { - if ( thread_globals [ i - 1 ] == thread_globals [ i ] ) { - std::printf("Duplicate thread globals (%d and %d)\n", i-1, i); - retVal = 2; - } + int retval = 0; + std::sort(thread_globals.begin(), thread_globals.end()); + for (int i = 1; i < num_threads; ++i) { + if (thread_globals[i - 1] == thread_globals[i]) { + std::cerr << "Duplicate thread globals " << thread_globals[i] << "\n"; + retval = 2; } - return retVal; -#else // TEST_HAS_NO_THREADS - size_t thread_globals; - thread_code(&thread_globals); - // Check that __cxa_get_globals() is not NULL. - return (thread_globals == 0) ? 1 : 0; + } + return retval; +#else // TEST_HAS_NO_THREADS + void* thread_globals; + thread_code(thread_globals); + // Check that __cxa_get_globals() is not NULL. + return !thread_globals; #endif // !TEST_HAS_NO_THREADS }