diff --git a/libcxx/src/thread.cpp b/libcxx/src/thread.cpp --- a/libcxx/src/thread.cpp +++ b/libcxx/src/thread.cpp @@ -115,8 +115,13 @@ __thread_specific_ptr<__thread_struct>& __thread_local_data() { - static __thread_specific_ptr<__thread_struct> __p; - return __p; + // Even though __thread_specific_ptr's destructor doesn't actually destroy + // anything (see comments there), we can't call it at all because threads may + // outlive the static variable and calling its destructor means accessing an + // object outside of its lifetime, which is UB. + alignas(__thread_specific_ptr<__thread_struct>) static char __b[sizeof(__thread_specific_ptr<__thread_struct>)]; + static __thread_specific_ptr<__thread_struct>* __p = new (__b) __thread_specific_ptr<__thread_struct>(); + return *__p; } // __thread_struct_imp diff --git a/libcxx/test/libcxx/thread/thread.threads/create_late.pass.cpp b/libcxx/test/libcxx/thread/thread.threads/create_late.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/libcxx/thread/thread.threads/create_late.pass.cpp @@ -0,0 +1,29 @@ +//===----------------------------------------------------------------------===// +// +// 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: libcpp-has-no-threads +// UNSUPPORTED: c++03 + +#include "make_test_thread.h" + +void func() {} + +struct T { + ~T() { + // __thread_local_data is expected to be destroyed as it was created + // from the main(). Now trigger another access. + support::make_test_thread(func).join(); + } +} t; + +int main(int, char**) { + // Triggers construction of __thread_local_data. + support::make_test_thread(func).join(); + + return 0; +}