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,19 @@ // //===----------------------------------------------------------------------===// -#include -#include -#include -#include <__threading_support> -#include +// UNSUPPORTED: c++03 +#include "assert_macros.h" +#include "concat_macros.h" #include "../src/cxa_exception.h" -#include "test_macros.h" +int main(int, char**) { + void* globals = __cxxabiv1::__cxa_get_globals(); + TEST_REQUIRE(globals != nullptr, TEST_WRITE_CONCATENATED("Got null result from __cxa_get_globals")); -typedef __cxxabiv1::__cxa_eh_globals globals_t; + void* fast_globals = __cxxabiv1::__cxa_get_globals_fast(); + TEST_REQUIRE(globals == fast_globals, TEST_WRITE_CONCATENATED("__cxa_get_globals returned ", globals, + " but __cxa_get_globals_fast returned ", fast_globals)); -void* thread_code(void* parm) { - size_t* result = (size_t*)parm; - globals_t *glob1, *glob2; - - glob1 = __cxxabiv1::__cxa_get_globals(); - if (NULL == glob1) - std::printf("Got null result from __cxa_get_globals\n"); - - glob2 = __cxxabiv1::__cxa_get_globals_fast(); - if (glob1 != glob2) - std::printf("Got different globals!\n"); - - *result = (size_t)glob1; -#ifndef TEST_HAS_NO_THREADS - sleep(1); -#endif - return parm; -} - -#ifndef TEST_HAS_NO_THREADS -# define NUMTHREADS 10 -size_t thread_globals[NUMTHREADS] = {0}; -std::__libcpp_thread_t threads[NUMTHREADS]; -#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 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::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; - } - } - 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; -#endif // !TEST_HAS_NO_THREADS + return 0; } diff --git a/libcxxabi/test/test_exception_storage.threads.pass.cpp b/libcxxabi/test/test_exception_storage.threads.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxxabi/test/test_exception_storage.threads.pass.cpp @@ -0,0 +1,77 @@ +//===----------------------------------------------------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++03, no-threads + +#include "test_macros.h" + +#include +#include +#include +#include +#include +#include +#include + +#include "assert_macros.h" +#include "concat_macros.h" +#include "../src/cxa_exception.h" + +static int threads_remaining; +static std::mutex threads_remaining_lock; +static std::condition_variable threads_remaining_cv; + +static void thread_code(void*& globals) { + std::thread::id thread_id = std::this_thread::get_id(); + (void)thread_id; + + globals = __cxxabiv1::__cxa_get_globals(); + TEST_REQUIRE(globals != nullptr, + TEST_WRITE_CONCATENATED("Got null result from __cxa_get_globals on thread ", thread_id)); + + void* fast_globals = __cxxabiv1::__cxa_get_globals_fast(); + TEST_REQUIRE(globals == fast_globals, + TEST_WRITE_CONCATENATED("__cxa_get_globals returned ", globals, " but __cxa_get_globals_fast returned ", + fast_globals, " on thread ", thread_id)); + + // 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; }); + } +} + +int main(int, char**) { + int num_threads = std::thread::hardware_concurrency(); + if (num_threads == 0) + num_threads = 4; // arbitrary fallback value + + 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(); + + std::sort(thread_globals.begin(), thread_globals.end()); + for (int i = 1; i < num_threads; ++i) { + TEST_REQUIRE(thread_globals[i - 1] != thread_globals[i], + TEST_WRITE_CONCATENATED("Duplicate thread globals ", thread_globals[i])); + } + + return 0; +}