diff --git a/libc/Android.mk b/libc/Android.mk index 6f430ccfc..ebc59ded7 100644 --- a/libc/Android.mk +++ b/libc/Android.mk @@ -533,6 +533,9 @@ libc_pthread_src_files := \ bionic/pthread_setschedparam.cpp \ bionic/pthread_sigmask.cpp \ +libc_thread_atexit_impl_src_files := \ + bionic/__cxa_thread_atexit_impl.cpp \ + libc_arch_static_src_files := \ bionic/dl_iterate_phdr_static.cpp \ @@ -1002,6 +1005,24 @@ $(eval $(call patch-up-arch-specific-flags,LOCAL_CFLAGS,libc_common_cflags)) $(eval $(call patch-up-arch-specific-flags,LOCAL_SRC_FILES,libc_bionic_src_files)) include $(BUILD_STATIC_LIBRARY) +include $(CLEAR_VARS) +LOCAL_SRC_FILES := $(libc_thread_atexit_impl_src_files) +LOCAL_CFLAGS := $(libc_common_cflags) -fno-data-sections -Wframe-larger-than=2048 + +LOCAL_CONLYFLAGS := $(libc_common_conlyflags) +LOCAL_CPPFLAGS := $(libc_common_cppflags) -Wold-style-cast +LOCAL_C_INCLUDES := $(libc_common_c_includes) +LOCAL_MODULE := libc_thread_atexit_impl +# TODO: Clang tries to use __tls_get_addr which is not supported yet +# remove after it is implemented. +LOCAL_CLANG := false +LOCAL_ADDITIONAL_DEPENDENCIES := $(libc_common_additional_dependencies) +LOCAL_CXX_STL := none +LOCAL_SYSTEM_SHARED_LIBRARIES := +LOCAL_ADDRESS_SANITIZER := false +LOCAL_NATIVE_COVERAGE := $(bionic_coverage) + +include $(BUILD_STATIC_LIBRARY) # ======================================================== # libc_pthread.a - pthreads parts that previously lived in @@ -1206,6 +1227,7 @@ LOCAL_WHOLE_STATIC_LIBRARIES := \ libc_pthread \ libc_stack_protector \ libc_syscalls \ + libc_thread_atexit_impl \ libc_tzcode \ LOCAL_WHOLE_STATIC_LIBRARIES_arm := libc_aeabi diff --git a/libc/bionic/__cxa_thread_atexit_impl.cpp b/libc/bionic/__cxa_thread_atexit_impl.cpp new file mode 100644 index 000000000..9ae6dfda1 --- /dev/null +++ b/libc/bionic/__cxa_thread_atexit_impl.cpp @@ -0,0 +1,48 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include + +struct thread_local_dtor { + void (*func) (void *); + void *arg; + void *dso_handle; // unused... + thread_local_dtor* next; +}; + +__thread thread_local_dtor* thread_local_dtors = nullptr; + +extern "C" int __cxa_thread_atexit_impl(void (*func) (void *), void *arg, void *dso_handle) { + thread_local_dtor* dtor = new thread_local_dtor(); + + dtor->func = func; + dtor->arg = arg; + dtor->dso_handle = dso_handle; + dtor->next = thread_local_dtors; + + thread_local_dtors = dtor; + + return 0; +} + +extern "C" __LIBC_HIDDEN__ void __cxa_thread_finalize() { + while (thread_local_dtors != nullptr) { + thread_local_dtor* current = thread_local_dtors; + thread_local_dtors = current->next; + + current->func(current->arg); + delete current; + } +} diff --git a/libc/bionic/pthread_exit.cpp b/libc/bionic/pthread_exit.cpp index c2232a939..1de85f510 100644 --- a/libc/bionic/pthread_exit.cpp +++ b/libc/bionic/pthread_exit.cpp @@ -37,6 +37,7 @@ extern "C" __noreturn void _exit_with_stack_teardown(void*, size_t); extern "C" __noreturn void __exit(int); extern "C" int __set_tid_address(int*); +extern "C" void __cxa_thread_finalize(); /* CAVEAT: our implementation of pthread_cleanup_push/pop doesn't support C++ exceptions * and thread cancelation @@ -59,10 +60,13 @@ void __pthread_cleanup_pop(__pthread_cleanup_t* c, int execute) { } void pthread_exit(void* return_value) { + // Call dtors for thread_local objects first. + __cxa_thread_finalize(); + pthread_internal_t* thread = __get_thread(); thread->return_value = return_value; - // Call the cleanup handlers first. + // Call the cleanup handlers. while (thread->cleanup_stack) { __pthread_cleanup_t* c = thread->cleanup_stack; thread->cleanup_stack = c->__cleanup_prev; diff --git a/tests/Android.mk b/tests/Android.mk index 0a83e8478..8804b71b8 100644 --- a/tests/Android.mk +++ b/tests/Android.mk @@ -258,10 +258,12 @@ bionic-unit-tests_static_libraries := \ libtinyxml2 \ liblog \ +# TODO: Include __cxa_thread_atexit_test.cpp to glibc tests once it is upgraded (glibc 2.18+) bionic-unit-tests_src_files := \ atexit_test.cpp \ dl_test.cpp \ dlext_test.cpp \ + __cxa_thread_atexit_test.cpp \ dlfcn_test.cpp \ bionic-unit-tests_cflags := $(test_cflags) diff --git a/tests/__cxa_thread_atexit_test.cpp b/tests/__cxa_thread_atexit_test.cpp new file mode 100644 index 000000000..017731498 --- /dev/null +++ b/tests/__cxa_thread_atexit_test.cpp @@ -0,0 +1,74 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +#include + +#include + +extern "C" int __cxa_thread_atexit_impl(void (*fn)(void*), void* arg, void* dso_handle); + +static void thread_atexit_fn1(void* arg) { + std::string* call_sequence = static_cast(arg); + *call_sequence += "one, "; +} + +static void thread_atexit_fn2(void* arg) { + std::string* call_sequence = static_cast(arg); + *call_sequence += "two, "; +} + +static void thread_atexit_from_atexit(void* arg) { + std::string* call_sequence = static_cast(arg); + *call_sequence += "oops, "; +} + +static void thread_atexit_fn3(void* arg) { + __cxa_thread_atexit_impl(thread_atexit_from_atexit, arg, nullptr); + std::string* call_sequence = static_cast(arg); + *call_sequence += "three, "; +} + +static void thread_atexit_fn4(void* arg) { + std::string* call_sequence = static_cast(arg); + *call_sequence += "four, "; +} + +static void thread_atexit_fn5(void* arg) { + std::string* call_sequence = static_cast(arg); + *call_sequence += "five."; +} + +static void* thread_main(void* arg) { + __cxa_thread_atexit_impl(thread_atexit_fn5, arg, nullptr); + __cxa_thread_atexit_impl(thread_atexit_fn4, arg, nullptr); + __cxa_thread_atexit_impl(thread_atexit_fn3, arg, nullptr); + __cxa_thread_atexit_impl(thread_atexit_fn2, arg, nullptr); + __cxa_thread_atexit_impl(thread_atexit_fn1, arg, nullptr); + return nullptr; +} + +TEST(__cxa_thread_atexit_impl, smoke) { + std::string atexit_call_sequence; + + pthread_t t; + ASSERT_EQ(0, pthread_create(&t, nullptr, thread_main, &atexit_call_sequence)); + ASSERT_EQ(0, pthread_join(t, nullptr)); + ASSERT_EQ("one, two, three, oops, four, five.", atexit_call_sequence); +} + +