diff options
author | Elliott Hughes <enh@google.com> | 2014-03-07 17:59:05 -0800 |
---|---|---|
committer | Steve Kondik <shade@chemlab.org> | 2014-06-02 02:33:22 -0700 |
commit | 452fa4ec246f502ac1e773151898ee85a8410cd8 (patch) | |
tree | 72b6476599f2fc58c890d779b933c0b89993383a | |
parent | 1c9d43c2c1af3471c1ef064c3598edf131932b34 (diff) | |
download | bionic-452fa4ec246f502ac1e773151898ee85a8410cd8.tar.gz bionic-452fa4ec246f502ac1e773151898ee85a8410cd8.tar.bz2 bionic-452fa4ec246f502ac1e773151898ee85a8410cd8.zip |
Fix pthread_detach for already-exited threads.
This fix handles the case when pthread_detach is
called for threads which have already exited.
This is required to avoid any memory leak
Change-Id: I2bf7f41234d93b226132a4c51705f4186f4961c3
Reported-by: Paresh Nakhe <pnakhe@codeaurora.org>
-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 95f11ac97..a8608e3a7 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 d4d38f521..5551c4833 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))); |