blob: 70a2eb610bdff80cb3c6e37e1f655e545ce6343d [file] [log] [blame]
// Copyright 2022 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#define _ALL_SOURCE // For thrd_create_with_name.
#include <pthread.h>
#include <threads.h>
#include <zircon/assert.h>
#include <algorithm>
#include <mutex>
#include <zxtest/zxtest.h>
struct Thread {
thrd_t thrd;
std::mutex mutex;
static int thread(void *mutex_ptr) __TA_ACQUIRE(mutex) __TA_RELEASE(mutex) {
std::mutex &mutex = *reinterpret_cast<std::mutex *>(mutex_ptr);
std::unique_lock lock{mutex};
return 0;
}
Thread() {
mutex.lock();
thrd_create_with_name(&thrd, thread, &mutex, "thread-name");
}
~Thread() {
mutex.unlock();
thrd_join(thrd, nullptr);
}
operator thrd_t() const { return thrd; }
};
template <class Function>
void testBoth(Function &f) {
f(pthread_self());
Thread thrd;
f(thrd.thrd);
}
TEST(PthreadGetSetNameTest, GetNameBasic) {
Thread thrd;
char name[ZX_MAX_NAME_LEN];
pthread_getname_np(thrd, name, sizeof(name));
EXPECT_STREQ(name, "thread-name");
}
TEST(PthreadGetSetNameTest, GetNameTruncate) {
Thread thrd;
char name[ZX_MAX_NAME_LEN]{'a', 'b'};
// Size 0 shouldn't touch name.
pthread_getname_np(thrd, name, 0);
EXPECT_STREQ(name, "ab");
pthread_getname_np(thrd, name, 1);
EXPECT_STREQ(name, "");
pthread_getname_np(thrd, name, 2);
EXPECT_STREQ(name, "t");
pthread_getname_np(thrd, name, 7);
EXPECT_STREQ(name, "thread");
pthread_getname_np(thrd, name, sizeof(name));
EXPECT_STREQ(name, "thread-name");
}
#if !__has_feature(undefined_behavior_sanitizer)
TEST(PthreadGetSetNameTest, GetNameErrors) {
auto test = [](auto &&thrd) {
ASSERT_DEATH([&thrd] { pthread_getname_np(thrd, nullptr, ZX_MAX_NAME_LEN); });
};
testBoth(test);
}
#endif
TEST(PthreadGetSetNameTest, SetName) {
auto test = [](auto &&thrd) {
char newname[] = "new-thread-name";
pthread_setname_np(thrd, newname);
char name[ZX_MAX_NAME_LEN];
pthread_getname_np(thrd, name, sizeof(name));
EXPECT_STREQ(name, newname);
};
testBoth(test);
}
template <size_t I>
static void test() {
if (!I)
return;
auto t = [](auto &&thrd) {
struct A {
char c = 'a';
};
A newname[I];
newname[I - 1].c = 0;
pthread_setname_np(thrd, reinterpret_cast<const char *>(newname));
char name[I];
pthread_getname_np(thrd, name, sizeof(name));
constexpr size_t last = std::min(ZX_MAX_NAME_LEN, I) - 1;
EXPECT_EQ(0, name[last]);
for (size_t i = 0; i < last; i++)
EXPECT_EQ('a', name[i]);
};
testBoth(t);
}
template <size_t... Ints>
static void runTests(std::index_sequence<Ints...>) {
(test<Ints>(), ...);
}
template <size_t next, size_t... Ints>
constexpr auto append(std::index_sequence<Ints...> seq) {
return std::index_sequence<Ints..., next>{};
}
TEST(PthreadGetSetNameTest, SetNameManySizes) {
runTests(append<10000>(std::make_index_sequence<ZX_MAX_NAME_LEN + 5>{}));
}
extern "C" zx_handle_t thrd_get_zx_handle(thrd_t);
TEST(PthreadGetSetNameTest, InvalidHandle) {
thrd_t thrd;
int (*fn)(void *) = +[](void *) {
zx_thread_exit();
return 0;
};
EXPECT_EQ(thrd_create(&thrd, fn, nullptr), thrd_success);
zx_object_wait_one(thrd_get_zx_handle(thrd), ZX_THREAD_SUSPENDED | ZX_THREAD_TERMINATED,
ZX_TIME_INFINITE, nullptr);
EXPECT_EQ(pthread_setname_np(thrd, ""), ESRCH);
// We can't join the thread because it called zx_thread_exit, so the threads data will leak here.
}