diff options
-rw-r--r-- | libc/bionic/pthread_detach.cpp | 6 | ||||
-rw-r--r-- | tests/pthread_test.cpp | 30 |
2 files changed, 36 insertions, 0 deletions
diff --git a/libc/bionic/pthread_detach.cpp b/libc/bionic/pthread_detach.cpp index 95f11ac..a8608e3 100644 --- a/libc/bionic/pthread_detach.cpp +++ b/libc/bionic/pthread_detach.cpp @@ -44,6 +44,12 @@ int pthread_detach(pthread_t t) { return 0; // Already being joined; silently do nothing, like glibc. } + if (thread->tid == 0) { + // Already exited; clean up. + _pthread_internal_remove_locked(thread.get()); + return 0; + } + thread->attr.flags |= PTHREAD_ATTR_FLAG_DETACHED; return 0; } diff --git a/tests/pthread_test.cpp b/tests/pthread_test.cpp index d4d38f5..5551c48 100644 --- a/tests/pthread_test.cpp +++ b/tests/pthread_test.cpp @@ -18,6 +18,7 @@ #include <errno.h> #include <limits.h> +#include <malloc.h> #include <pthread.h> #include <unistd.h> @@ -278,6 +279,35 @@ TEST(pthread, pthread_detach__no_such_thread) { ASSERT_EQ(ESRCH, pthread_detach(dead_thread)); } +TEST(pthread, pthread_detach__leak) { + size_t initial_bytes = mallinfo().uordblks; + + pthread_attr_t attr; + ASSERT_EQ(0, pthread_attr_init(&attr)); + ASSERT_EQ(0, pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE)); + + std::vector<pthread_t> threads; + for (size_t i = 0; i < 32; ++i) { + pthread_t t; + ASSERT_EQ(0, pthread_create(&t, &attr, IdFn, NULL)); + threads.push_back(t); + } + + sleep(1); + + for (size_t i = 0; i < 32; ++i) { + ASSERT_EQ(0, pthread_detach(threads[i])) << i; + } + + size_t final_bytes = mallinfo().uordblks; + + int leaked_bytes = (final_bytes - initial_bytes); + + // User code (like this test) doesn't know how large pthread_internal_t is. + // We can be pretty sure it's more than 128 bytes. + ASSERT_LT(leaked_bytes, 32 /*threads*/ * 128 /*bytes*/); +} + TEST(pthread, pthread_getcpuclockid__clock_gettime) { pthread_t t; ASSERT_EQ(0, pthread_create(&t, NULL, SleepFn, reinterpret_cast<void*>(5))); |