[libc-commits] [libc] [libc] Implement pthread_getattr_np (PR #221231)
Pavel Labath via libc-commits
libc-commits at lists.llvm.org
Mon Sep 7 03:33:06 PDT 2026
https://github.com/labath updated https://github.com/llvm/llvm-project/pull/221231
>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 1/3] [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;
+}
>From 5f45df492225507694d843267fe9cd5fbad4d8c7 Mon Sep 17 00:00:00 2001
From: Pavel Labath <pavel at labath.sk>
Date: Mon, 7 Sep 2026 09:06:24 +0000
Subject: [PATCH 2/3] - check states more explicitly - add pagesize(),
PthreadAttrValues - work around barrier destruction
---
libc/src/pthread/CMakeLists.txt | 1 +
libc/src/pthread/pthread_getattr_np.cpp | 21 +-
.../integration/src/pthread/CMakeLists.txt | 1 -
.../src/pthread/pthread_getattr_np_test.cpp | 293 ++++++------------
4 files changed, 118 insertions(+), 198 deletions(-)
diff --git a/libc/src/pthread/CMakeLists.txt b/libc/src/pthread/CMakeLists.txt
index 5007ba206d391..846a524f436c0 100644
--- a/libc/src/pthread/CMakeLists.txt
+++ b/libc/src/pthread/CMakeLists.txt
@@ -983,6 +983,7 @@ add_entrypoint_object(
libc.src.__support.macros.config
libc.src.__support.macros.null_check
libc.src.__support.threads.thread
+ libc.src.__support.threads.thread_attributes
)
add_entrypoint_object(
diff --git a/libc/src/pthread/pthread_getattr_np.cpp b/libc/src/pthread/pthread_getattr_np.cpp
index e63d468c16aac..d44d10f1608d9 100644
--- a/libc/src/pthread/pthread_getattr_np.cpp
+++ b/libc/src/pthread/pthread_getattr_np.cpp
@@ -20,6 +20,7 @@
#include "src/__support/macros/config.h"
#include "src/__support/macros/null_check.h"
#include "src/__support/threads/thread.h"
+#include "src/__support/threads/thread_attributes.h"
namespace LIBC_NAMESPACE_DECL {
@@ -31,12 +32,20 @@ LLVM_LIBC_FUNCTION(int, pthread_getattr_np,
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;
+ switch (static_cast<DetachState>(
+ thread->attrib->detach_state.load(cpp::MemoryOrder::RELAXED))) {
+ case DetachState::DETACHED:
+ attr->__detachstate = PTHREAD_CREATE_DETACHED;
+ break;
+ case DetachState::JOINABLE:
+ attr->__detachstate = PTHREAD_CREATE_JOINABLE;
+ break;
+ case DetachState::EXITING:
+ // We don't know what was the detach state of the thread before it started
+ // exiting, but even if we did, we could not read it reliably as the memory
+ // backing thread->attrib can go away any moment.
+ __builtin_unreachable();
+ }
attr->__stack = thread->attrib->stack;
attr->__stacksize = thread->attrib->stacksize;
attr->__guardsize = thread->attrib->guardsize;
diff --git a/libc/test/integration/src/pthread/CMakeLists.txt b/libc/test/integration/src/pthread/CMakeLists.txt
index 210f0e4500d84..75c9fed78a713 100644
--- a/libc/test/integration/src/pthread/CMakeLists.txt
+++ b/libc/test/integration/src/pthread/CMakeLists.txt
@@ -309,7 +309,6 @@ add_integration_test(
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
diff --git a/libc/test/integration/src/pthread/pthread_getattr_np_test.cpp b/libc/test/integration/src/pthread/pthread_getattr_np_test.cpp
index b02a784e688d1..6fe9f87c1df7e 100644
--- a/libc/test/integration/src/pthread/pthread_getattr_np_test.cpp
+++ b/libc/test/integration/src/pthread/pthread_getattr_np_test.cpp
@@ -21,7 +21,6 @@
#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"
@@ -34,10 +33,14 @@
#include "src/unistd/sysconf.h"
#include "test/IntegrationTest/test.h"
+static size_t pagesize() {
+ return static_cast<size_t>(LIBC_NAMESPACE::sysconf(_SC_PAGESIZE));
+}
+
static void check_readable(const void *start, size_t size) {
- size_t pagesize = LIBC_NAMESPACE::sysconf(_SC_PAGESIZE);
+ size_t page_size = pagesize();
auto *bytes = static_cast<const volatile char *>(start);
- for (size_t offset = 0; offset < size; offset += pagesize)
+ for (size_t offset = 0; offset < size; offset += page_size)
(void)bytes[offset];
if (size > 0)
(void)bytes[size - 1];
@@ -48,88 +51,75 @@ static void wait_barrier(pthread_barrier_t &barrier) {
ASSERT_TRUE(res == 0 || res == PTHREAD_BARRIER_SERIAL_THREAD);
}
+static pthread_barrier_t ready_barrier;
+static pthread_barrier_t done_barrier;
+
+struct PthreadAttrValues {
+ int detachstate{-1};
+ void *stackaddr{reinterpret_cast<void *>(1)};
+ size_t stacksize{1234};
+ size_t guardsize{1234};
+
+ PthreadAttrValues() = default;
+
+ void populate_from(pthread_t th) {
+ pthread_attr_t attr;
+ ASSERT_EQ(LIBC_NAMESPACE::pthread_getattr_np(th, &attr), 0);
+ ASSERT_EQ(LIBC_NAMESPACE::pthread_attr_getdetachstate(&attr, &detachstate),
+ 0);
+ ASSERT_EQ(
+ LIBC_NAMESPACE::pthread_attr_getstack(&attr, &stackaddr, &stacksize),
+ 0);
+ ASSERT_EQ(LIBC_NAMESPACE::pthread_attr_getguardsize(&attr, &guardsize), 0);
+ ASSERT_EQ(LIBC_NAMESPACE::pthread_attr_destroy(&attr), 0);
+ }
+};
+
// 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));
+ PthreadAttrValues values;
+ values.populate_from(LIBC_NAMESPACE::pthread_self());
- 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),
+ ASSERT_EQ(values.detachstate, static_cast<int>(PTHREAD_CREATE_DETACHED));
+ ASSERT_NE(values.stackaddr, static_cast<void *>(nullptr));
+ ASSERT_EQ(values.stacksize, static_cast<size_t>(PTHREAD_STACK_DYNAMIC_NP));
+ ASSERT_EQ(reinterpret_cast<uintptr_t>(values.stackaddr) % 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));
+ uintptr_t local_var_addr = reinterpret_cast<uintptr_t>(&values);
+ uintptr_t stack_high = reinterpret_cast<uintptr_t>(values.stackaddr);
+ ASSERT_TRUE(local_var_addr < stack_high);
+ check_readable(&values, stack_high - local_var_addr);
- ASSERT_EQ(LIBC_NAMESPACE::pthread_attr_destroy(&attr), 0);
+ ASSERT_EQ(values.guardsize, static_cast<size_t>(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);
+ auto *values = static_cast<PthreadAttrValues *>(arg);
- pthread_attr_t attr;
- ASSERT_EQ(
- LIBC_NAMESPACE::pthread_getattr_np(LIBC_NAMESPACE::pthread_self(), &attr),
- 0);
+ values->populate_from(LIBC_NAMESPACE::pthread_self());
- ASSERT_EQ(
- LIBC_NAMESPACE::pthread_attr_getdetachstate(&attr, &args->detachstate),
- 0);
- ASSERT_EQ(args->detachstate, static_cast<int>(PTHREAD_CREATE_JOINABLE));
+ ASSERT_EQ(values->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);
+ ASSERT_NE(values->stackaddr, static_cast<void *>(nullptr));
+ ASSERT_NE(values->stacksize, static_cast<size_t>(PTHREAD_STACK_DYNAMIC_NP));
+ ASSERT_TRUE(values->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;
+ int local_var = 0;
+ uintptr_t local_var_addr = reinterpret_cast<uintptr_t>(&local_var);
+ uintptr_t stack_low = reinterpret_cast<uintptr_t>(values->stackaddr);
+ uintptr_t stack_high = stack_low + values->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)));
+ check_readable(values->stackaddr, values->stacksize);
- ASSERT_EQ(LIBC_NAMESPACE::pthread_attr_destroy(&attr), 0);
+ ASSERT_EQ(values->guardsize, pagesize());
- wait_barrier(args->ready_barrier);
- wait_barrier(args->done_barrier);
+ wait_barrier(ready_barrier);
+ wait_barrier(done_barrier);
return nullptr;
}
@@ -139,80 +129,33 @@ static void *child_default_func(void *arg) {
// 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);
-
+ PthreadAttrValues child_values;
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),
+ ASSERT_EQ(LIBC_NAMESPACE::pthread_create(&th, nullptr, child_default_func,
+ &child_values),
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);
+ wait_barrier(ready_barrier);
- size_t guardsize = 0;
- ASSERT_EQ(LIBC_NAMESPACE::pthread_attr_getguardsize(&attr, &guardsize), 0);
- ASSERT_EQ(guardsize, args.guardsize);
+ // Query from the parent thread while child is still running.
+ PthreadAttrValues parent_values;
+ parent_values.populate_from(th);
- ASSERT_EQ(LIBC_NAMESPACE::pthread_attr_destroy(&attr), 0);
+ ASSERT_EQ(parent_values.detachstate,
+ static_cast<int>(PTHREAD_CREATE_JOINABLE));
+ ASSERT_EQ(parent_values.stackaddr, child_values.stackaddr);
+ ASSERT_EQ(parent_values.stacksize, child_values.stacksize);
+ ASSERT_EQ(parent_values.guardsize, child_values.guardsize);
- wait_barrier(args.done_barrier);
+ wait_barrier(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);
-
+ auto *values = static_cast<PthreadAttrValues *>(arg);
+ values->populate_from(LIBC_NAMESPACE::pthread_self());
return nullptr;
}
@@ -233,44 +176,32 @@ static void test_child_thread_custom_stack() {
custom_stacksize),
0);
- ChildCustomArgs args;
+ PthreadAttrValues values;
pthread_t th;
ASSERT_EQ(
- LIBC_NAMESPACE::pthread_create(&th, &attr, child_custom_func, &args), 0);
+ LIBC_NAMESPACE::pthread_create(&th, &attr, child_custom_func, &values),
+ 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(values.stackaddr, custom_stack);
+ ASSERT_EQ(values.stacksize, custom_stacksize);
+ ASSERT_EQ(values.guardsize, static_cast<size_t>(0));
+ ASSERT_EQ(values.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);
+static void *child_detached_func(void *) {
+ PthreadAttrValues values;
+ values.populate_from(LIBC_NAMESPACE::pthread_self());
- pthread_attr_t attr;
- ASSERT_EQ(
- LIBC_NAMESPACE::pthread_getattr_np(LIBC_NAMESPACE::pthread_self(), &attr),
- 0);
+ ASSERT_EQ(values.detachstate, static_cast<int>(PTHREAD_CREATE_DETACHED));
+ ASSERT_EQ(values.guardsize, pagesize());
- 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);
+ wait_barrier(done_barrier);
return nullptr;
}
@@ -278,9 +209,6 @@ static void *child_detached_func(void *arg) {
// 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(
@@ -289,23 +217,16 @@ static void test_child_thread_detached() {
pthread_t th;
ASSERT_EQ(
- LIBC_NAMESPACE::pthread_create(&th, &attr, child_detached_func, &barrier),
+ LIBC_NAMESPACE::pthread_create(&th, &attr, child_detached_func, nullptr),
0);
ASSERT_EQ(LIBC_NAMESPACE::pthread_attr_destroy(&attr), 0);
- wait_barrier(barrier);
- ASSERT_EQ(LIBC_NAMESPACE::pthread_barrier_destroy(&barrier), 0);
+ wait_barrier(done_barrier);
}
-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);
+static void *child_dynamic_detach_func(void *) {
+ wait_barrier(ready_barrier);
+ wait_barrier(done_barrier);
return nullptr;
}
@@ -313,42 +234,32 @@ static void *child_dynamic_detach_func(void *arg) {
// 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),
+ child_dynamic_detach_func, nullptr),
0);
- wait_barrier(args.ready_barrier);
+ wait_barrier(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);
+ PthreadAttrValues values;
+ values.populate_from(th);
+ ASSERT_EQ(values.detachstate, static_cast<int>(PTHREAD_CREATE_JOINABLE));
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);
+ values.populate_from(th);
+ ASSERT_EQ(values.detachstate, static_cast<int>(PTHREAD_CREATE_DETACHED));
- ASSERT_EQ(LIBC_NAMESPACE::pthread_barrier_destroy(&args.ready_barrier), 0);
- ASSERT_EQ(LIBC_NAMESPACE::pthread_barrier_destroy(&args.done_barrier), 0);
+ wait_barrier(done_barrier);
}
TEST_MAIN() {
+ // Barriers cannot be destroyed safely due to issue #221680.
+ ASSERT_EQ(
+ LIBC_NAMESPACE::pthread_barrier_init(&ready_barrier, nullptr, 2), 0);
+ ASSERT_EQ(
+ LIBC_NAMESPACE::pthread_barrier_init(&done_barrier, nullptr, 2), 0);
+
test_main_thread();
test_child_thread_default();
test_child_thread_custom_stack();
>From cab14385e6e3b552ae7fb833bb9249f5aeec01b7 Mon Sep 17 00:00:00 2001
From: Pavel Labath <pavel at labath.sk>
Date: Mon, 7 Sep 2026 10:32:18 +0000
Subject: [PATCH 3/3] format
---
.../integration/src/pthread/pthread_getattr_np_test.cpp | 7 +++----
1 file changed, 3 insertions(+), 4 deletions(-)
diff --git a/libc/test/integration/src/pthread/pthread_getattr_np_test.cpp b/libc/test/integration/src/pthread/pthread_getattr_np_test.cpp
index 6fe9f87c1df7e..ccba2102acdac 100644
--- a/libc/test/integration/src/pthread/pthread_getattr_np_test.cpp
+++ b/libc/test/integration/src/pthread/pthread_getattr_np_test.cpp
@@ -255,10 +255,9 @@ static void test_child_thread_dynamic_detach() {
TEST_MAIN() {
// Barriers cannot be destroyed safely due to issue #221680.
- ASSERT_EQ(
- LIBC_NAMESPACE::pthread_barrier_init(&ready_barrier, nullptr, 2), 0);
- ASSERT_EQ(
- LIBC_NAMESPACE::pthread_barrier_init(&done_barrier, nullptr, 2), 0);
+ ASSERT_EQ(LIBC_NAMESPACE::pthread_barrier_init(&ready_barrier, nullptr, 2),
+ 0);
+ ASSERT_EQ(LIBC_NAMESPACE::pthread_barrier_init(&done_barrier, nullptr, 2), 0);
test_main_thread();
test_child_thread_default();
More information about the libc-commits
mailing list