[libc-commits] [libc] [libc] Implement pthread_getattr_np (PR #221231)
Pavel Labath via libc-commits
libc-commits at lists.llvm.org
Fri Sep 4 07:19:47 PDT 2026
https://github.com/labath created https://github.com/llvm/llvm-project/pull/221231
Implement the GNU extension pthread_getattr_np.
For the stack address and size, the function returns the same value as pthread_getstack_np. Specifically, this means returning PTHREAD_STACK_DYNAMIC_NP for the main thread. Since we're not promising async-signal-safety of this function, we could parse /proc/self/maps (like glibc does), to get the actual stack VMA, but:
- that's a lot of code
- I'm not particularly fond of relying on /proc for libc features
- the consistency with pthread_getattr_np is nice
For these reasons, I'm not implementing this (or other alternatives like RLIMIT_STACK) right now. This incidentally means that the current implementation of this function is async-signal-safe, but that is likely to change once we add support for additional attributes.
For consistency with glibc, I report the guard size of newly created threads as zero, regardless of what was present in the thread creation attributes. I zero the attribute directly during thread creation as it is not used for managed stacks.
Assisted-by: Gemini
>From d8862215f400bdf1070685c2e7f5c2154285cd8e Mon Sep 17 00:00:00 2001
From: Pavel Labath <pavel at labath.sk>
Date: Fri, 4 Sep 2026 13:21:28 +0000
Subject: [PATCH] [libc] Implement pthread_getattr_np
Implement the GNU extension pthread_getattr_np.
For the stack address and size, the function returns the same value as
pthread_getstack_np. Specifically, this means returning
PTHREAD_STACK_DYNAMIC_NP for the main thread. Since we're not promising
async-signal-safety of this function, we could parse /proc/self/maps
(like glibc does), to get the actual stack VMA, but:
- that's a lot of code
- I'm not particularly fond of relying on /proc for libc features
- the consistency with pthread_getattr_np is nice
For these reasons, I'm not implementing this (or other alternatives
like RLIMIT_STACK) right now. This incidentally means that the
current implementation of this function is async-signal-safe, but
that is likely to change once we add support for additional
attributes.
For consistency with glibc, I report the guard size of newly created
threads as zero, regardless of what was present in the thread creation
attributes. I zero the attribute directly during thread creation as it
is not used for managed stacks.
Assisted-by: Gemini
---
libc/config/linux/aarch64/entrypoints.txt | 1 +
libc/config/linux/riscv/entrypoints.txt | 1 +
libc/config/linux/x86_64/entrypoints.txt | 1 +
libc/hdr/types/CMakeLists.txt | 8 +
libc/hdr/types/pthread_attr_t.h | 27 ++
libc/src/__support/threads/linux/thread.cpp | 4 +
libc/src/pthread/CMakeLists.txt | 17 +
libc/src/pthread/pthread_getattr_np.cpp | 46 +++
libc/src/pthread/pthread_getattr_np.h | 14 +-
.../integration/src/pthread/CMakeLists.txt | 30 ++
.../src/pthread/pthread_getattr_np_test.cpp | 358 ++++++++++++++++++
11 files changed, 503 insertions(+), 4 deletions(-)
create mode 100644 libc/hdr/types/pthread_attr_t.h
create mode 100644 libc/src/pthread/pthread_getattr_np.cpp
create mode 100644 libc/test/integration/src/pthread/pthread_getattr_np_test.cpp
diff --git a/libc/config/linux/aarch64/entrypoints.txt b/libc/config/linux/aarch64/entrypoints.txt
index 9340f49ed6bc7..be1d086416e81 100644
--- a/libc/config/linux/aarch64/entrypoints.txt
+++ b/libc/config/linux/aarch64/entrypoints.txt
@@ -1128,6 +1128,7 @@ if(LLVM_LIBC_FULL_BUILD)
libc.src.pthread.pthread_detach
libc.src.pthread.pthread_equal
libc.src.pthread.pthread_exit
+ libc.src.pthread.pthread_getattr_np
libc.src.pthread.pthread_getname_np
libc.src.pthread.pthread_getschedparam
libc.src.pthread.pthread_getspecific
diff --git a/libc/config/linux/riscv/entrypoints.txt b/libc/config/linux/riscv/entrypoints.txt
index 82b125c561766..ccf7bd353fe54 100644
--- a/libc/config/linux/riscv/entrypoints.txt
+++ b/libc/config/linux/riscv/entrypoints.txt
@@ -1316,6 +1316,7 @@ if(LLVM_LIBC_FULL_BUILD)
libc.src.pthread.pthread_detach
libc.src.pthread.pthread_equal
libc.src.pthread.pthread_exit
+ libc.src.pthread.pthread_getattr_np
libc.src.pthread.pthread_getname_np
libc.src.pthread.pthread_getschedparam
libc.src.pthread.pthread_getspecific
diff --git a/libc/config/linux/x86_64/entrypoints.txt b/libc/config/linux/x86_64/entrypoints.txt
index 85b8dffbd828d..2d970a57c7b95 100644
--- a/libc/config/linux/x86_64/entrypoints.txt
+++ b/libc/config/linux/x86_64/entrypoints.txt
@@ -1325,6 +1325,7 @@ if(LLVM_LIBC_FULL_BUILD)
libc.src.pthread.pthread_detach
libc.src.pthread.pthread_equal
libc.src.pthread.pthread_exit
+ libc.src.pthread.pthread_getattr_np
libc.src.pthread.pthread_getname_np
libc.src.pthread.pthread_getschedparam
libc.src.pthread.pthread_getspecific
diff --git a/libc/hdr/types/CMakeLists.txt b/libc/hdr/types/CMakeLists.txt
index 33be74c1420c5..b0e37012b294a 100644
--- a/libc/hdr/types/CMakeLists.txt
+++ b/libc/hdr/types/CMakeLists.txt
@@ -406,6 +406,14 @@ add_proxy_header_library(
libc.include.llvm-libc-types.pthread_barrierattr_t
)
+add_proxy_header_library(
+ pthread_attr_t
+ HDRS
+ pthread_attr_t.h
+ FULL_BUILD_DEPENDS
+ libc.include.llvm-libc-types.pthread_attr_t
+)
+
add_proxy_header_library(
pthread_t
HDRS
diff --git a/libc/hdr/types/pthread_attr_t.h b/libc/hdr/types/pthread_attr_t.h
new file mode 100644
index 0000000000000..314692da3ab56
--- /dev/null
+++ b/libc/hdr/types/pthread_attr_t.h
@@ -0,0 +1,27 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file
+/// Proxy for pthread_attr_t.
+///
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_LIBC_HDR_TYPES_PTHREAD_ATTR_T_H
+#define LLVM_LIBC_HDR_TYPES_PTHREAD_ATTR_T_H
+
+#ifdef LIBC_FULL_BUILD
+
+#include "include/llvm-libc-types/pthread_attr_t.h"
+
+#else // Overlay mode
+
+#include <pthread.h>
+
+#endif // LIBC_FULL_BUILD
+
+#endif // LLVM_LIBC_HDR_TYPES_PTHREAD_ATTR_T_H
diff --git a/libc/src/__support/threads/linux/thread.cpp b/libc/src/__support/threads/linux/thread.cpp
index 231b7cf2903ca..20b3e5b88fa8d 100644
--- a/libc/src/__support/threads/linux/thread.cpp
+++ b/libc/src/__support/threads/linux/thread.cpp
@@ -223,6 +223,10 @@ int Thread::run(ThreadStyle style, ThreadRunner runner, void *arg, void *stack,
else
stack = alloc.value();
owned_stack = true;
+ } else {
+ // The user is responsible for setting up the stack guard (or not) for the
+ // provided stack.
+ guardsize = 0;
}
// Validate that stack/stacksize are validly aligned.
diff --git a/libc/src/pthread/CMakeLists.txt b/libc/src/pthread/CMakeLists.txt
index 94af498779291..5007ba206d391 100644
--- a/libc/src/pthread/CMakeLists.txt
+++ b/libc/src/pthread/CMakeLists.txt
@@ -968,6 +968,23 @@ add_entrypoint_object(
libc.src.__support.threads.thread
)
+add_entrypoint_object(
+ pthread_getattr_np
+ SRCS
+ pthread_getattr_np.cpp
+ HDRS
+ pthread_getattr_np.h
+ DEPENDS
+ libc.hdr.pthread_macros
+ libc.hdr.types.pthread_attr_t
+ libc.hdr.types.pthread_t
+ libc.src.__support.CPP.atomic
+ libc.src.__support.common
+ libc.src.__support.macros.config
+ libc.src.__support.macros.null_check
+ libc.src.__support.threads.thread
+)
+
add_entrypoint_object(
pthread_getstack_np
SRCS
diff --git a/libc/src/pthread/pthread_getattr_np.cpp b/libc/src/pthread/pthread_getattr_np.cpp
new file mode 100644
index 0000000000000..e63d468c16aac
--- /dev/null
+++ b/libc/src/pthread/pthread_getattr_np.cpp
@@ -0,0 +1,46 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file
+/// Implementation of the pthread_getattr_np function (GNU extension).
+///
+//===----------------------------------------------------------------------===//
+
+#include "src/pthread/pthread_getattr_np.h"
+#include "hdr/pthread_macros.h"
+#include "hdr/types/pthread_attr_t.h"
+#include "hdr/types/pthread_t.h"
+#include "src/__support/CPP/atomic.h"
+#include "src/__support/common.h"
+#include "src/__support/macros/config.h"
+#include "src/__support/macros/null_check.h"
+#include "src/__support/threads/thread.h"
+
+namespace LIBC_NAMESPACE_DECL {
+
+static_assert(sizeof(pthread_t) == sizeof(LIBC_NAMESPACE::Thread),
+ "Mismatch between pthread_t and internal Thread.");
+
+LLVM_LIBC_FUNCTION(int, pthread_getattr_np,
+ (pthread_t th, pthread_attr_t *attr)) {
+ LIBC_CRASH_ON_NULLPTR(attr);
+ auto *thread = reinterpret_cast<Thread *>(&th);
+
+ uint32_t detach_state =
+ thread->attrib->detach_state.load(cpp::MemoryOrder::RELAXED);
+ attr->__detachstate =
+ (detach_state == static_cast<uint32_t>(DetachState::DETACHED))
+ ? PTHREAD_CREATE_DETACHED
+ : PTHREAD_CREATE_JOINABLE;
+ attr->__stack = thread->attrib->stack;
+ attr->__stacksize = thread->attrib->stacksize;
+ attr->__guardsize = thread->attrib->guardsize;
+ return 0;
+}
+
+} // namespace LIBC_NAMESPACE_DECL
diff --git a/libc/src/pthread/pthread_getattr_np.h b/libc/src/pthread/pthread_getattr_np.h
index 7dada95681dcd..8daf223ddbef5 100644
--- a/libc/src/pthread/pthread_getattr_np.h
+++ b/libc/src/pthread/pthread_getattr_np.h
@@ -1,20 +1,26 @@
-//===-- Implementation header for pthread_getattr_np function -*- C++ -*-===//
+//===----------------------------------------------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
-//===---------------------------------------------------------------------===//
+//===----------------------------------------------------------------------===//
+///
+/// \file
+/// Implementation header for pthread_getattr_np (GNU extension).
+///
+//===----------------------------------------------------------------------===//
#ifndef LLVM_LIBC_SRC_PTHREAD_PTHREAD_GETATTR_NP_H
#define LLVM_LIBC_SRC_PTHREAD_PTHREAD_GETATTR_NP_H
+#include "hdr/types/pthread_attr_t.h"
+#include "hdr/types/pthread_t.h"
#include "src/__support/macros/config.h"
-#include <pthread.h>
namespace LIBC_NAMESPACE_DECL {
-int pthread_getattr_np(pthread_t, pthread_attr_t *);
+int pthread_getattr_np(pthread_t th, pthread_attr_t *attr);
} // namespace LIBC_NAMESPACE_DECL
diff --git a/libc/test/integration/src/pthread/CMakeLists.txt b/libc/test/integration/src/pthread/CMakeLists.txt
index 19d02bbc3f0c4..210f0e4500d84 100644
--- a/libc/test/integration/src/pthread/CMakeLists.txt
+++ b/libc/test/integration/src/pthread/CMakeLists.txt
@@ -292,6 +292,36 @@ add_integration_test(
libc.src.__support.CPP.new
)
+add_integration_test(
+ pthread_getattr_np_test
+ SUITE
+ libc-pthread-integration-tests
+ SRCS
+ pthread_getattr_np_test.cpp
+ DEPENDS
+ libc.hdr.pthread_macros
+ libc.hdr.stdint_proxy
+ libc.hdr.sys_mman_macros
+ libc.src.pthread.pthread_attr_destroy
+ libc.src.pthread.pthread_attr_getdetachstate
+ libc.src.pthread.pthread_attr_getguardsize
+ libc.src.pthread.pthread_attr_getstack
+ libc.src.pthread.pthread_attr_init
+ libc.src.pthread.pthread_attr_setdetachstate
+ libc.src.pthread.pthread_attr_setstack
+ libc.src.pthread.pthread_barrier_destroy
+ libc.src.pthread.pthread_barrier_init
+ libc.src.pthread.pthread_barrier_wait
+ libc.src.pthread.pthread_create
+ libc.src.pthread.pthread_detach
+ libc.src.pthread.pthread_getattr_np
+ libc.src.pthread.pthread_join
+ libc.src.pthread.pthread_self
+ libc.src.sys.mman.mmap
+ libc.src.sys.mman.munmap
+ libc.src.unistd.sysconf
+)
+
add_integration_test(
pthread_getstack_np_test
SUITE
diff --git a/libc/test/integration/src/pthread/pthread_getattr_np_test.cpp b/libc/test/integration/src/pthread/pthread_getattr_np_test.cpp
new file mode 100644
index 0000000000000..b02a784e688d1
--- /dev/null
+++ b/libc/test/integration/src/pthread/pthread_getattr_np_test.cpp
@@ -0,0 +1,358 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file
+/// Integration tests for pthread_getattr_np.
+///
+//===----------------------------------------------------------------------===//
+
+#include "hdr/pthread_macros.h"
+#include "hdr/stdint_proxy.h"
+#include "hdr/sys_mman_macros.h"
+#include "src/pthread/pthread_attr_destroy.h"
+#include "src/pthread/pthread_attr_getdetachstate.h"
+#include "src/pthread/pthread_attr_getguardsize.h"
+#include "src/pthread/pthread_attr_getstack.h"
+#include "src/pthread/pthread_attr_init.h"
+#include "src/pthread/pthread_attr_setdetachstate.h"
+#include "src/pthread/pthread_attr_setstack.h"
+#include "src/pthread/pthread_barrier_destroy.h"
+#include "src/pthread/pthread_barrier_init.h"
+#include "src/pthread/pthread_barrier_wait.h"
+#include "src/pthread/pthread_create.h"
+#include "src/pthread/pthread_detach.h"
+#include "src/pthread/pthread_getattr_np.h"
+#include "src/pthread/pthread_join.h"
+#include "src/pthread/pthread_self.h"
+#include "src/sys/mman/mmap.h"
+#include "src/sys/mman/munmap.h"
+#include "src/unistd/sysconf.h"
+#include "test/IntegrationTest/test.h"
+
+static void check_readable(const void *start, size_t size) {
+ size_t pagesize = LIBC_NAMESPACE::sysconf(_SC_PAGESIZE);
+ auto *bytes = static_cast<const volatile char *>(start);
+ for (size_t offset = 0; offset < size; offset += pagesize)
+ (void)bytes[offset];
+ if (size > 0)
+ (void)bytes[size - 1];
+}
+
+static void wait_barrier(pthread_barrier_t &barrier) {
+ int res = LIBC_NAMESPACE::pthread_barrier_wait(&barrier);
+ ASSERT_TRUE(res == 0 || res == PTHREAD_BARRIER_SERIAL_THREAD);
+}
+
+// Test 1: Main thread attributes
+// Verifies that pthread_getattr_np on the main thread reports a detached state,
+// a dynamic stack size (PTHREAD_STACK_DYNAMIC_NP), and a zero guard size.
+static void test_main_thread() {
+ pthread_attr_t attr;
+ ASSERT_EQ(
+ LIBC_NAMESPACE::pthread_getattr_np(LIBC_NAMESPACE::pthread_self(), &attr),
+ 0);
+
+ int detachstate = -1;
+ ASSERT_EQ(LIBC_NAMESPACE::pthread_attr_getdetachstate(&attr, &detachstate),
+ 0);
+ ASSERT_EQ(detachstate, static_cast<int>(PTHREAD_CREATE_DETACHED));
+
+ void *stackaddr = nullptr;
+ size_t stacksize = 1234;
+ ASSERT_EQ(
+ LIBC_NAMESPACE::pthread_attr_getstack(&attr, &stackaddr, &stacksize), 0);
+ ASSERT_NE(stackaddr, static_cast<void *>(nullptr));
+ ASSERT_EQ(stacksize, static_cast<size_t>(PTHREAD_STACK_DYNAMIC_NP));
+ ASSERT_EQ(reinterpret_cast<uintptr_t>(stackaddr) %
+ LIBC_NAMESPACE::sysconf(_SC_PAGESIZE),
+ static_cast<uintptr_t>(0));
+
+ uintptr_t local_var_addr = reinterpret_cast<uintptr_t>(&attr);
+ ASSERT_TRUE(local_var_addr < reinterpret_cast<uintptr_t>(stackaddr));
+ check_readable(&attr,
+ reinterpret_cast<uintptr_t>(stackaddr) - local_var_addr);
+
+ size_t guardsize = 1234;
+ ASSERT_EQ(LIBC_NAMESPACE::pthread_attr_getguardsize(&attr, &guardsize), 0);
+ ASSERT_EQ(guardsize, static_cast<size_t>(0));
+
+ ASSERT_EQ(LIBC_NAMESPACE::pthread_attr_destroy(&attr), 0);
+}
+
+struct ChildDefaultArgs {
+ pthread_barrier_t ready_barrier;
+ pthread_barrier_t done_barrier;
+ void *stackaddr{nullptr};
+ size_t stacksize{0};
+ size_t guardsize{0};
+ int detachstate{-1};
+};
+
+static void *child_default_func(void *arg) {
+ auto *args = static_cast<ChildDefaultArgs *>(arg);
+
+ pthread_attr_t attr;
+ ASSERT_EQ(
+ LIBC_NAMESPACE::pthread_getattr_np(LIBC_NAMESPACE::pthread_self(), &attr),
+ 0);
+
+ ASSERT_EQ(
+ LIBC_NAMESPACE::pthread_attr_getdetachstate(&attr, &args->detachstate),
+ 0);
+ ASSERT_EQ(args->detachstate, static_cast<int>(PTHREAD_CREATE_JOINABLE));
+
+ ASSERT_EQ(LIBC_NAMESPACE::pthread_attr_getstack(&attr, &args->stackaddr,
+ &args->stacksize),
+ 0);
+ ASSERT_NE(args->stackaddr, static_cast<void *>(nullptr));
+ ASSERT_NE(args->stacksize, static_cast<size_t>(PTHREAD_STACK_DYNAMIC_NP));
+ ASSERT_TRUE(args->stacksize > 0);
+
+ uintptr_t local_var_addr = reinterpret_cast<uintptr_t>(&attr);
+ uintptr_t stack_low = reinterpret_cast<uintptr_t>(args->stackaddr);
+ uintptr_t stack_high = stack_low + args->stacksize;
+ ASSERT_TRUE(local_var_addr >= stack_low);
+ ASSERT_TRUE(local_var_addr < stack_high);
+
+ check_readable(args->stackaddr, args->stacksize);
+
+ ASSERT_EQ(LIBC_NAMESPACE::pthread_attr_getguardsize(&attr, &args->guardsize),
+ 0);
+ ASSERT_EQ(args->guardsize,
+ static_cast<size_t>(LIBC_NAMESPACE::sysconf(_SC_PAGESIZE)));
+
+ ASSERT_EQ(LIBC_NAMESPACE::pthread_attr_destroy(&attr), 0);
+
+ wait_barrier(args->ready_barrier);
+ wait_barrier(args->done_barrier);
+
+ return nullptr;
+}
+
+// Test 2: Child thread with default attributes
+// Verifies that a joinable thread created with default attributes reports
+// joinable state, an implementation-allocated stack, and a page-sized guard,
+// both when queried by the thread itself and by the parent thread.
+static void test_child_thread_default() {
+ ChildDefaultArgs args;
+ ASSERT_EQ(
+ LIBC_NAMESPACE::pthread_barrier_init(&args.ready_barrier, nullptr, 2), 0);
+ ASSERT_EQ(
+ LIBC_NAMESPACE::pthread_barrier_init(&args.done_barrier, nullptr, 2), 0);
+
+ pthread_t th;
+ ASSERT_EQ(
+ LIBC_NAMESPACE::pthread_create(&th, nullptr, child_default_func, &args),
+ 0);
+
+ wait_barrier(args.ready_barrier);
+
+ // Query from the parent thread while child is still running.
+ pthread_attr_t attr;
+ ASSERT_EQ(LIBC_NAMESPACE::pthread_getattr_np(th, &attr), 0);
+
+ int detachstate = -1;
+ ASSERT_EQ(LIBC_NAMESPACE::pthread_attr_getdetachstate(&attr, &detachstate),
+ 0);
+ ASSERT_EQ(detachstate, static_cast<int>(PTHREAD_CREATE_JOINABLE));
+
+ void *stackaddr = nullptr;
+ size_t stacksize = 0;
+ ASSERT_EQ(
+ LIBC_NAMESPACE::pthread_attr_getstack(&attr, &stackaddr, &stacksize), 0);
+ ASSERT_EQ(stackaddr, args.stackaddr);
+ ASSERT_EQ(stacksize, args.stacksize);
+
+ size_t guardsize = 0;
+ ASSERT_EQ(LIBC_NAMESPACE::pthread_attr_getguardsize(&attr, &guardsize), 0);
+ ASSERT_EQ(guardsize, args.guardsize);
+
+ ASSERT_EQ(LIBC_NAMESPACE::pthread_attr_destroy(&attr), 0);
+
+ wait_barrier(args.done_barrier);
+
+ void *retval = nullptr;
+ ASSERT_EQ(LIBC_NAMESPACE::pthread_join(th, &retval), 0);
+
+ ASSERT_EQ(LIBC_NAMESPACE::pthread_barrier_destroy(&args.ready_barrier), 0);
+ ASSERT_EQ(LIBC_NAMESPACE::pthread_barrier_destroy(&args.done_barrier), 0);
+}
+
+struct ChildCustomArgs {
+ void *stackaddr{nullptr};
+ size_t stacksize{0};
+ size_t guardsize{0};
+ int detachstate{-1};
+};
+
+static void *child_custom_func(void *arg) {
+ auto *args = static_cast<ChildCustomArgs *>(arg);
+
+ pthread_attr_t attr;
+ ASSERT_EQ(
+ LIBC_NAMESPACE::pthread_getattr_np(LIBC_NAMESPACE::pthread_self(), &attr),
+ 0);
+
+ ASSERT_EQ(
+ LIBC_NAMESPACE::pthread_attr_getdetachstate(&attr, &args->detachstate),
+ 0);
+ ASSERT_EQ(args->detachstate, static_cast<int>(PTHREAD_CREATE_JOINABLE));
+
+ ASSERT_EQ(LIBC_NAMESPACE::pthread_attr_getstack(&attr, &args->stackaddr,
+ &args->stacksize),
+ 0);
+
+ ASSERT_EQ(LIBC_NAMESPACE::pthread_attr_getguardsize(&attr, &args->guardsize),
+ 0);
+ ASSERT_EQ(args->guardsize, static_cast<size_t>(0));
+
+ ASSERT_EQ(LIBC_NAMESPACE::pthread_attr_destroy(&attr), 0);
+
+ return nullptr;
+}
+
+// Test 3: Child thread with custom stack
+// Verifies that a thread created with a user-allocated stack reports the exact
+// stack address and size, and reports a guard size of 0.
+static void test_child_thread_custom_stack() {
+ size_t custom_stacksize = PTHREAD_STACK_MIN * 2;
+ void *custom_stack =
+ LIBC_NAMESPACE::mmap(nullptr, custom_stacksize, PROT_READ | PROT_WRITE,
+ MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
+ ASSERT_NE(custom_stack, MAP_FAILED);
+ ASSERT_NE(custom_stack, static_cast<void *>(nullptr));
+
+ pthread_attr_t attr;
+ ASSERT_EQ(LIBC_NAMESPACE::pthread_attr_init(&attr), 0);
+ ASSERT_EQ(LIBC_NAMESPACE::pthread_attr_setstack(&attr, custom_stack,
+ custom_stacksize),
+ 0);
+
+ ChildCustomArgs args;
+ pthread_t th;
+ ASSERT_EQ(
+ LIBC_NAMESPACE::pthread_create(&th, &attr, child_custom_func, &args), 0);
+ ASSERT_EQ(LIBC_NAMESPACE::pthread_attr_destroy(&attr), 0);
+
+ void *retval = nullptr;
+ ASSERT_EQ(LIBC_NAMESPACE::pthread_join(th, &retval), 0);
+
+ ASSERT_EQ(args.stackaddr, custom_stack);
+ ASSERT_EQ(args.stacksize, custom_stacksize);
+ ASSERT_EQ(args.guardsize, static_cast<size_t>(0));
+ ASSERT_EQ(args.detachstate, static_cast<int>(PTHREAD_CREATE_JOINABLE));
+
+ ASSERT_EQ(LIBC_NAMESPACE::munmap(custom_stack, custom_stacksize), 0);
+}
+
+static void *child_detached_func(void *arg) {
+ auto &barrier = *static_cast<pthread_barrier_t *>(arg);
+
+ pthread_attr_t attr;
+ ASSERT_EQ(
+ LIBC_NAMESPACE::pthread_getattr_np(LIBC_NAMESPACE::pthread_self(), &attr),
+ 0);
+
+ int detachstate = -1;
+ ASSERT_EQ(LIBC_NAMESPACE::pthread_attr_getdetachstate(&attr, &detachstate),
+ 0);
+ ASSERT_EQ(detachstate, static_cast<int>(PTHREAD_CREATE_DETACHED));
+
+ size_t guardsize = 0;
+ ASSERT_EQ(LIBC_NAMESPACE::pthread_attr_getguardsize(&attr, &guardsize), 0);
+ ASSERT_EQ(guardsize,
+ static_cast<size_t>(LIBC_NAMESPACE::sysconf(_SC_PAGESIZE)));
+
+ ASSERT_EQ(LIBC_NAMESPACE::pthread_attr_destroy(&attr), 0);
+
+ wait_barrier(barrier);
+ return nullptr;
+}
+
+// Test 4: Child thread created detached
+// Verifies that a thread created with PTHREAD_CREATE_DETACHED reports a
+// detached state.
+static void test_child_thread_detached() {
+ pthread_barrier_t barrier;
+ ASSERT_EQ(LIBC_NAMESPACE::pthread_barrier_init(&barrier, nullptr, 2), 0);
+
+ pthread_attr_t attr;
+ ASSERT_EQ(LIBC_NAMESPACE::pthread_attr_init(&attr), 0);
+ ASSERT_EQ(LIBC_NAMESPACE::pthread_attr_setdetachstate(
+ &attr, PTHREAD_CREATE_DETACHED),
+ 0);
+
+ pthread_t th;
+ ASSERT_EQ(
+ LIBC_NAMESPACE::pthread_create(&th, &attr, child_detached_func, &barrier),
+ 0);
+ ASSERT_EQ(LIBC_NAMESPACE::pthread_attr_destroy(&attr), 0);
+
+ wait_barrier(barrier);
+ ASSERT_EQ(LIBC_NAMESPACE::pthread_barrier_destroy(&barrier), 0);
+}
+
+struct DynamicDetachArgs {
+ pthread_barrier_t ready_barrier;
+ pthread_barrier_t done_barrier;
+};
+
+static void *child_dynamic_detach_func(void *arg) {
+ auto *args = static_cast<DynamicDetachArgs *>(arg);
+ wait_barrier(args->ready_barrier);
+ wait_barrier(args->done_barrier);
+ return nullptr;
+}
+
+// Test 5: Dynamically detached child thread
+// Verifies that detaching a running joinable thread transitions its reported
+// detach state from PTHREAD_CREATE_JOINABLE to PTHREAD_CREATE_DETACHED.
+static void test_child_thread_dynamic_detach() {
+ DynamicDetachArgs args;
+ ASSERT_EQ(
+ LIBC_NAMESPACE::pthread_barrier_init(&args.ready_barrier, nullptr, 2), 0);
+ ASSERT_EQ(
+ LIBC_NAMESPACE::pthread_barrier_init(&args.done_barrier, nullptr, 2), 0);
+
+ pthread_t th;
+ ASSERT_EQ(LIBC_NAMESPACE::pthread_create(&th, nullptr,
+ child_dynamic_detach_func, &args),
+ 0);
+
+ wait_barrier(args.ready_barrier);
+
+ pthread_attr_t attr;
+ ASSERT_EQ(LIBC_NAMESPACE::pthread_getattr_np(th, &attr), 0);
+ int detachstate = -1;
+ ASSERT_EQ(LIBC_NAMESPACE::pthread_attr_getdetachstate(&attr, &detachstate),
+ 0);
+ ASSERT_EQ(detachstate, static_cast<int>(PTHREAD_CREATE_JOINABLE));
+ ASSERT_EQ(LIBC_NAMESPACE::pthread_attr_destroy(&attr), 0);
+
+ ASSERT_EQ(LIBC_NAMESPACE::pthread_detach(th), 0);
+
+ ASSERT_EQ(LIBC_NAMESPACE::pthread_getattr_np(th, &attr), 0);
+ ASSERT_EQ(LIBC_NAMESPACE::pthread_attr_getdetachstate(&attr, &detachstate),
+ 0);
+ ASSERT_EQ(detachstate, static_cast<int>(PTHREAD_CREATE_DETACHED));
+ ASSERT_EQ(LIBC_NAMESPACE::pthread_attr_destroy(&attr), 0);
+
+ wait_barrier(args.done_barrier);
+
+ ASSERT_EQ(LIBC_NAMESPACE::pthread_barrier_destroy(&args.ready_barrier), 0);
+ ASSERT_EQ(LIBC_NAMESPACE::pthread_barrier_destroy(&args.done_barrier), 0);
+}
+
+TEST_MAIN() {
+ test_main_thread();
+ test_child_thread_default();
+ test_child_thread_custom_stack();
+ test_child_thread_detached();
+ test_child_thread_dynamic_detach();
+ return 0;
+}
More information about the libc-commits
mailing list