summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorChristopher Ferris <cferris@google.com>2014-08-06 14:15:01 -0700
committerChristopher Ferris <cferris@google.com>2014-08-06 17:12:30 -0700
commit18d93f2793fad393b6aa6eae6afe1054958339d5 (patch)
treeee2d1717eeb7155ec0a9854e750e11cdcf7c796d
parent1b1966d9448e979d1503a3d8843708bfa8880dc6 (diff)
downloadbionic-18d93f2793fad393b6aa6eae6afe1054958339d5.zip
bionic-18d93f2793fad393b6aa6eae6afe1054958339d5.tar.gz
bionic-18d93f2793fad393b6aa6eae6afe1054958339d5.tar.bz2
Do a second key cleanup in pthread_exit.
During pthread_exit, the keys are cleaned. Unfortunately, a call to free occurs after the cleanup and the memory for some of the keys is recreated when using jemalloc. The solution is to do the key cleanup twice. Also, modify the pthread_detach__leak test to be less flaky when run on a jemalloc system. Bug: 16513133 Change-Id: Ic17e8344bdc1ba053c4f5b6d827a4c19c57860c1
-rw-r--r--libc/bionic/pthread_exit.cpp6
-rw-r--r--tests/pthread_test.cpp45
2 files changed, 33 insertions, 18 deletions
diff --git a/libc/bionic/pthread_exit.cpp b/libc/bionic/pthread_exit.cpp
index a6bb363..6cd5311 100644
--- a/libc/bionic/pthread_exit.cpp
+++ b/libc/bionic/pthread_exit.cpp
@@ -112,6 +112,12 @@ void pthread_exit(void* return_value) {
}
pthread_mutex_unlock(&g_thread_list_lock);
+ // Perform a second key cleanup. When using jemalloc, a call to free from
+ // _pthread_internal_remove_locked causes the memory associated with a key
+ // to be reallocated.
+ // TODO: When b/16847284 is fixed this call can be removed.
+ pthread_key_clean_all();
+
if (user_allocated_stack) {
// Cleaning up this thread's stack is the creator's responsibility, not ours.
__exit(0);
diff --git a/tests/pthread_test.cpp b/tests/pthread_test.cpp
index 4da003f..5328e48 100644
--- a/tests/pthread_test.cpp
+++ b/tests/pthread_test.cpp
@@ -400,27 +400,36 @@ TEST(pthread, pthread_detach__no_such_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 initial_bytes = 0;
+ // Run this loop more than once since the first loop causes some memory
+ // to be allocated permenantly. Run an extra loop to help catch any subtle
+ // memory leaks.
+ for (size_t loop = 0; loop < 3; loop++) {
+ // Set the initial bytes on the second loop since the memory in use
+ // should have stabilized.
+ if (loop == 1) {
+ 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.