Merge "linker: use %m in error messages." into main
diff --git a/libc/bionic/exit.cpp b/libc/bionic/exit.cpp
index a5aed78..8eda5b2 100644
--- a/libc/bionic/exit.cpp
+++ b/libc/bionic/exit.cpp
@@ -26,6 +26,7 @@
* SUCH DAMAGE.
*/
+#include <pthread.h>
#include <stdlib.h>
#include <unistd.h>
@@ -34,8 +35,13 @@
extern "C" void __cxa_finalize(void* dso_handle);
extern "C" void __cxa_thread_finalize();
+static pthread_mutex_t g_exit_mutex = PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP;
+
__BIONIC_WEAK_FOR_NATIVE_BRIDGE
void exit(int status) {
+ // https://austingroupbugs.net/view.php?id=1845
+ pthread_mutex_lock(&g_exit_mutex);
+
__cxa_thread_finalize();
__cxa_finalize(nullptr);
_exit(status);
diff --git a/tests/stdlib_test.cpp b/tests/stdlib_test.cpp
index 4793150..f5e57a5 100644
--- a/tests/stdlib_test.cpp
+++ b/tests/stdlib_test.cpp
@@ -29,6 +29,7 @@
#include <limits>
#include <string>
+#include <thread>
#include <android-base/file.h>
#include <android-base/macros.h>
@@ -650,6 +651,58 @@
AssertChildExited(pid, 99);
}
+static void exit_from_atexit_func4() {
+ std::thread([] { exit(4); }).detach();
+ usleep(1000);
+ fprintf(stderr, "4");
+}
+
+static void exit_from_atexit_func3() {
+ std::thread([] { exit(3); }).detach();
+ fprintf(stderr, "3");
+ usleep(1000);
+ // This should cause us to exit with status 99,
+ // but not before printing "4",
+ // and without re-running the previous atexit handlers.
+ exit(99);
+}
+
+static void exit_from_atexit_func2() {
+ std::thread([] { exit(2); }).detach();
+ fprintf(stderr, "2");
+ usleep(1000);
+ // Register another atexit handler from within an atexit handler.
+ atexit(exit_from_atexit_func3);
+}
+
+static void exit_from_atexit_func1() {
+ // These atexit handlers all spawn another thread that tries to exit,
+ // and sleep to try to lose the race.
+ // The lock in exit() should ensure that only the first thread to call
+ // exit() can ever win (but see exit_from_atexit_func3() for a subtelty).
+ std::thread([] { exit(1); }).detach();
+ usleep(1000);
+ fprintf(stderr, "1");
+}
+
+static void exit_torturer() {
+ atexit(exit_from_atexit_func4);
+ // We deliberately don't register exit_from_atexit_func3() here;
+ // see exit_from_atexit_func2().
+ atexit(exit_from_atexit_func2);
+ atexit(exit_from_atexit_func1);
+ exit(0);
+}
+
+TEST(stdlib, exit_torture) {
+ // Test that the atexit() handlers are run in the defined order (reverse
+ // order of registration), even though one of them is registered by another
+ // when it runs, and that we get the exit code from the last call to exit()
+ // on the first thread to call exit() (rather than one of the other threads
+ // or a deadlock from the second call on the same thread).
+ ASSERT_EXIT(exit_torturer(), testing::ExitedWithCode(99), "1234");
+}
+
TEST(unistd, _Exit) {
pid_t pid = fork();
ASSERT_NE(-1, pid) << strerror(errno);