877ec6d904
Let the kernel keep pthread_internal_t::tid updated, including across forks and for the main thread. This then lets us fix pthread_join to only return after the thread has really exited. Also fix the thread attributes of the main thread so we don't unmap the main thread's stack (which is really owned by the dynamic linker and contains things like environment variables), which fixes crashes when joining with an exited main thread and also fixes problems reported publicly with accessing environment variables after the main thread exits (for which I've added a new unit test). In passing I also fixed a bug where if the clone(2) inside pthread_create(3) fails, we'd unmap the child's stack and TLS (which contains the mutex) and then try to unlock the mutex. Boom! It wasn't until after I'd uploaded the fix for this that I came across a new public bug reporting this exact failure. Bug: 8206355 Bug: 11693195 Bug: https://code.google.com/p/android/issues/detail?id=57421 Bug: https://code.google.com/p/android/issues/detail?id=62392 Change-Id: I2af9cf6e8ae510a67256ad93cad891794ed0580b
159 lines
4.5 KiB
C++
159 lines
4.5 KiB
C++
/*
|
|
* Copyright (C) 2012 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 <gtest/gtest.h>
|
|
|
|
#include <errno.h>
|
|
#include <libgen.h>
|
|
#include <limits.h>
|
|
#include <pthread.h>
|
|
#include <stdint.h>
|
|
#include <stdlib.h>
|
|
|
|
TEST(stdlib, drand48) {
|
|
srand48(0x01020304);
|
|
EXPECT_DOUBLE_EQ(0.65619299195623526, drand48());
|
|
EXPECT_DOUBLE_EQ(0.18522597229772941, drand48());
|
|
EXPECT_DOUBLE_EQ(0.42015087072844537, drand48());
|
|
EXPECT_DOUBLE_EQ(0.061637783047395089, drand48());
|
|
}
|
|
|
|
TEST(stdlib, lrand48_random_rand) {
|
|
srand48(0x01020304);
|
|
EXPECT_EQ(1409163720, lrand48());
|
|
EXPECT_EQ(397769746, lrand48());
|
|
EXPECT_EQ(902267124, lrand48());
|
|
EXPECT_EQ(132366131, lrand48());
|
|
|
|
#if __BIONIC__
|
|
// On bionic, random(3) is equivalent to lrand48...
|
|
srandom(0x01020304);
|
|
EXPECT_EQ(1409163720, random());
|
|
EXPECT_EQ(397769746, random());
|
|
EXPECT_EQ(902267124, random());
|
|
EXPECT_EQ(132366131, random());
|
|
|
|
// ...and rand(3) is the bottom 32 bits.
|
|
srand(0x01020304);
|
|
EXPECT_EQ(static_cast<int>(1409163720), rand());
|
|
EXPECT_EQ(static_cast<int>(397769746), rand());
|
|
EXPECT_EQ(static_cast<int>(902267124), rand());
|
|
EXPECT_EQ(static_cast<int>(132366131), rand());
|
|
#endif
|
|
}
|
|
|
|
TEST(stdlib, mrand48) {
|
|
srand48(0x01020304);
|
|
EXPECT_EQ(-1476639856, mrand48());
|
|
EXPECT_EQ(795539493, mrand48());
|
|
EXPECT_EQ(1804534249, mrand48());
|
|
EXPECT_EQ(264732262, mrand48());
|
|
}
|
|
|
|
TEST(stdlib, posix_memalign) {
|
|
void* p;
|
|
|
|
ASSERT_EQ(0, posix_memalign(&p, 512, 128));
|
|
ASSERT_EQ(0U, reinterpret_cast<uintptr_t>(p) % 512);
|
|
free(p);
|
|
|
|
// Can't align to a non-power of 2.
|
|
ASSERT_EQ(EINVAL, posix_memalign(&p, 81, 128));
|
|
}
|
|
|
|
TEST(stdlib, realpath__NULL_filename) {
|
|
errno = 0;
|
|
char* p = realpath(NULL, NULL);
|
|
ASSERT_TRUE(p == NULL);
|
|
ASSERT_EQ(EINVAL, errno);
|
|
}
|
|
|
|
TEST(stdlib, realpath__empty_filename) {
|
|
errno = 0;
|
|
char* p = realpath("", NULL);
|
|
ASSERT_TRUE(p == NULL);
|
|
ASSERT_EQ(ENOENT, errno);
|
|
}
|
|
|
|
TEST(stdlib, realpath__ENOENT) {
|
|
errno = 0;
|
|
char* p = realpath("/this/directory/path/almost/certainly/does/not/exist", NULL);
|
|
ASSERT_TRUE(p == NULL);
|
|
ASSERT_EQ(ENOENT, errno);
|
|
}
|
|
|
|
TEST(stdlib, realpath) {
|
|
// Get the name of this executable.
|
|
char executable_path[PATH_MAX];
|
|
int rc = readlink("/proc/self/exe", executable_path, sizeof(executable_path));
|
|
ASSERT_NE(rc, -1);
|
|
executable_path[rc] = '\0';
|
|
|
|
char buf[PATH_MAX + 1];
|
|
char* p = realpath("/proc/self/exe", buf);
|
|
ASSERT_STREQ(executable_path, p);
|
|
|
|
p = realpath("/proc/self/exe", NULL);
|
|
ASSERT_STREQ(executable_path, p);
|
|
free(p);
|
|
}
|
|
|
|
TEST(stdlib, qsort) {
|
|
struct s {
|
|
char name[16];
|
|
static int comparator(const void* lhs, const void* rhs) {
|
|
return strcmp(reinterpret_cast<const s*>(lhs)->name, reinterpret_cast<const s*>(rhs)->name);
|
|
}
|
|
};
|
|
s entries[3];
|
|
strcpy(entries[0].name, "charlie");
|
|
strcpy(entries[1].name, "bravo");
|
|
strcpy(entries[2].name, "alpha");
|
|
|
|
qsort(entries, 3, sizeof(s), s::comparator);
|
|
ASSERT_STREQ("alpha", entries[0].name);
|
|
ASSERT_STREQ("bravo", entries[1].name);
|
|
ASSERT_STREQ("charlie", entries[2].name);
|
|
|
|
qsort(entries, 3, sizeof(s), s::comparator);
|
|
ASSERT_STREQ("alpha", entries[0].name);
|
|
ASSERT_STREQ("bravo", entries[1].name);
|
|
ASSERT_STREQ("charlie", entries[2].name);
|
|
}
|
|
|
|
static void* TestBug57421_child(void* arg) {
|
|
pthread_t main_thread = reinterpret_cast<pthread_t>(arg);
|
|
pthread_join(main_thread, NULL);
|
|
char* value = getenv("ENVIRONMENT_VARIABLE");
|
|
if (value == NULL) {
|
|
setenv("ENVIRONMENT_VARIABLE", "value", 1);
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
static void TestBug57421_main() {
|
|
pthread_t t;
|
|
ASSERT_EQ(0, pthread_create(&t, NULL, TestBug57421_child, reinterpret_cast<void*>(pthread_self())));
|
|
pthread_exit(NULL);
|
|
}
|
|
|
|
// Even though this isn't really a death test, we have to say "DeathTest" here so gtest knows to
|
|
// run this test (which exits normally) in its own process.
|
|
TEST(stdlib_DeathTest, getenv_after_main_thread_exits) {
|
|
// https://code.google.com/p/android/issues/detail?id=57421
|
|
::testing::FLAGS_gtest_death_test_style = "threadsafe";
|
|
ASSERT_EXIT(TestBug57421_main(), ::testing::ExitedWithCode(0), "");
|
|
}
|